PHP "Malformed UTF-8 characters, possibly incorrectly encoded" hatası, json_encode() içine verilen dizi, nesne veya iç içe yapının en az bir metin değerinde geçerli UTF-8 byte dizisi bulunmadığında görülür. Sorunu çözmek için bozuk alanı kaynağında bulup kodlamasını doğrulamak, kaynak kodlama biliniyorsa veriyi UTF-8'e dönüştürmek ve dönüşümün veri kaybı oluşturup oluşturmadığını kontrol etmek gerekir.
Hata çoğu zaman JSON yapısından değil, form, dosya, veritabanı veya API'den gelen metnin byte içeriğinden kaynaklanır. JSON_UNESCAPED_UNICODE Türkçe karakterleri JSON içinde okunabilir tutabilir, ancak geçersiz byte dizisini onarmaz.
Hata ne anlama gelir: json_encode() neden geçersiz UTF-8 veriyi kabul etmez?
json_encode(), bir dizi veya nesneyi JSON'a dönüştürürken içindeki metin değerlerini de işler. PHP'nin resmî json_encode() belgelerinde tüm string verilerinin UTF-8 kodlu olması gerektiği belirtilir. Bu nedenle tek bir alan bozuksa, dizinin anahtarları ve yapısı doğru görünse bile JSON üretimi başarısız olabilir.
Burada iki farklı katmanı ayırmak önemlidir: JSON, verinin nasıl temsil edildiğini belirleyen çıktı formatıdır; UTF-8 ise metnin byte düzeyindeki kodlamasıdır. Form alanı, dosya içeriği, veritabanı sonucu veya API yanıtı geçersiz byte içeriyorsa kök neden çoğunlukla JSON yapısında değil, verinin kaynağında ya da taşınma biçimindedir.
JSON_UNESCAPED_UNICODE yalnızca geçerli çok baytlı Unicode karakterlerinin JSON içinde doğrudan gösterilmesini sağlar. Böylece Türkçe karakterler uXXXX biçimi yerine okunabilir şekilde kalabilir. Ancak xC3x28 gibi bozuk bir byte dizisini düzeltmez, eksik byte'ları tamamlamaz ve kaynağın kodlamasını değiştirmez. Bu seçeneği bir onarım yöntemi değil, geçerli verinin çıktı biçimini belirleyen bir tercih olarak düşünmelisin.
json_encode() ve hata fonksiyonlarıyla sorunu küçük bir örnekte gör

Önce geçerli Türkçe metni, ardından aynı dizinin bozuk byte içeren hâlini kodlayabilirsin. Aşağıdaki örnekte xC3x28 bilerek geçersiz bir UTF-8 dizisi olarak kullanılır. Çalışma tamamlandı ise geçerli veriyle karşılaştırma yapmak için eklenir.
Varsayılan akışta dönüş değerini ve son hata durumunu birlikte kontrol et. PHP'nin json_last_error() belgeleri, hata kodunu ve hata açıklamasını incelemek için json_last_error() ile json_last_error_msg() işlevlerini kullanabileceğini belirtir.
<?php
$veriler = [
'mesaj' => 'Çalışma tamamlandı',
'bozuk' => "xC3x28",
];
$gecerliJson = json_encode(
$veriler['mesaj'],
JSON_UNESCAPED_UNICODE
);
var_dump($gecerliJson);
var_dump(json_last_error(), json_last_error_msg());
$hataliJson = json_encode(
$veriler,
JSON_UNESCAPED_UNICODE
);
var_dump($hataliJson);
var_dump(json_last_error(), json_last_error_msg());
try {
$json = json_encode(
$veriler,
JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR
);
echo $json, PHP_EOL;
} catch (JsonException $e) {
echo 'Yakalanan hata: ', $e->getMessage(), PHP_EOL;
}
Beklenen sonuçlar:
- Geçerli değer:
"Çalışma tamamlandı"şeklinde JSON üretilir ve son hata durumuJSON_ERROR_NONEile eşleşir. - Bozuk değer:
json_encode()dönüşüfalseolur.json_last_error()sonucuJSON_ERROR_UTF8,json_last_error_msg()sonucu iseMalformed UTF-8 characters, possibly incorrectly encodedolur. - Exception akışı:
JSON_THROW_ON_ERRORkullanıldığındaJsonExceptionyakalanır vecatchbloğundaki hata mesajı yazdırılır.
Örnekte JSON_UNESCAPED_UNICODE geçerli Türkçe metnin karakterlerini korur, fakat xC3x28 değerini onarmaz. Hata devam ediyorsa çözüm, farklı bir JSON seçeneği eklemekten önce bozuk metnin hangi alandan geldiğini bulmaktır.
Kaynağa göre kontrol listesi: form, dosya, veritabanı ve API

JSON üretmeden önce verinin hangi kaynaktan geldiğini ayırmak teşhisi kolaylaştırır. Her kaynağın ham değerini incele, UTF-8 geçerliliğini denetle ve yalnızca kaynak kodlama gerçekten biliniyorsa dönüşüm uygula.
| Kaynak | İlk kontrol | mb_check_encoding() denetimi | Kaynak kodlama biliniyorsa dönüşüm | Veri kaybı riski |
|---|---|---|---|---|
| Form | $_POST veya $_GET içindeki alınan alanı ve alanın türünü kontrol et. |
Alanı tek başına mb_check_encoding($value, 'UTF-8') ile denetle. |
Kaynak biliniyorsa mb_convert_encoding($value, 'UTF-8', $sourceEncoding) uygula ve yeniden denetle. |
Yanlış kaynak seçimi karakterleri değiştirebilir; daha önce kaybolan veri geri gelmez. |
| Dosya | file_get_contents() ile ham byte içeriğini, dosya adı, uzantı ve dosya bilgisini birlikte incele. |
Okunan içeriği mb_check_encoding($value, 'UTF-8') ile kontrol et. |
Dosyanın üretildiği kodlama biliniyorsa uygun mb_convert_encoding() dönüşümünü yap, ardından tekrar kontrol et. |
Yanlış varsayım, özel karakterlerin değişmesine veya içeriğin eksilmesine yol açabilir. |
| Veritabanı | Saklanan değeri ve verinin eklenip okunurken izlediği akışı incele. | Okunan alan için mb_check_encoding($value, 'UTF-8') sonucunu kontrol et. |
Kaynak kodlama kesin olarak biliniyorsa değeri UTF-8'e dönüştür ve yeniden denetle. | Veritabanına yazılmadan önce bozulmuş karakterler dönüşümle geri getirilemez. |
| API | Ham yanıt gövdesini ve Content-Type içindeki mevcut kodlama bilgisini kontrol et. |
Yanıt gövdesini mb_check_encoding($value, 'UTF-8') ile denetle. |
Yanıtın kaynak kodlaması biliniyorsa mb_convert_encoding() kullan, sonra tekrar kontrol et. |
Kaynak bilinmiyorsa rastgele kodlama seçmek veri değişimine neden olabilir. |
mb_check_encoding(), verilen değerin belirtilen kodlamaya uygunluğunu true veya false olarak bildirir. mb_convert_encoding() ise bilinen bir kaynak kodlamadan hedef kodlamaya dönüşüm yapar. Dönüşüm, daha önce bozulmuş veya kaybolmuş byte'ları onaramaz. Bu nedenle dönüşüm sonrasında hem UTF-8 geçerliliğini hem de metnin anlamını kontrol et.
Hata mesajından kök nedene giden teşhis ağacı
Teşhisi şu sırayla ilerlet: veriyi kontrol et, hatalı alanı izole et, kaynağı belirle, UTF-8 geçerliliğini denetle, gerekiyorsa dönüştür ve JSON üretimini yeniden dene.
- Veriyi kontrol et:
json_encode()içine gönderilen dizi veya nesnedeki tüm string alanlarını belirle. - Başarısız alanı izole et: Birden fazla alan varsa değerleri tek tek tara. İç içe dizilerde alt alanları da ayrı incele. Tek bir alan bozukken tüm yapıyı rastgele dönüştürme.
- Kaynağı belirle:
- Form: Hatalı alanın ham gönderim değerine bak.
- Dosya: Dosya içeriğini ve dosya bilgisini birlikte incele.
- Veritabanı: Saklanan değeri ve değerin sisteme giriş yolunu takip et.
- API: Ham yanıt gövdesini ve içerik bilgisini kontrol et.
- UTF-8 geçerliliğini denetle: Şüpheli her string için
mb_check_encoding($value, 'UTF-8')kullan. - Yalnızca kaynak kodlama biliniyorsa dönüştür: Uygun
$sourceEncodingdeğeriylemb_convert_encoding()çağır. - Dönüşümden sonra yeniden denetle: Sonucun geçerli UTF-8 olduğunu ve karakterlerin beklenen anlamı koruduğunu kontrol et.
- Yeniden JSON üret: Sorunlu alan düzeltildikten sonra
json_encode()çağrısını tekrar çalıştır.
mb_check_encoding() başarılı olduğu hâlde JSON üretimi başarısızsa başka bir iç içe alanı veya ham byte içeren değeri inceleme adımına dön. Hata yakalanan tek alanı düzeltmek, bütün veri yapısını körlemesine dönüştürmekten daha güvenli bir yaklaşımdır.
Düzeltmeden sonra veriyi kaybetmeden JSON üret
Güvenli üretim akışı, hatayı susturmakla değil verinin gerçekten geçerli UTF-8 olduğundan emin olmakla başlar. Önce özgün değeri koru, kaynağın kodlamasını belirle, gerekiyorsa tek bir dönüşüm uygula ve sonucu yeniden doğrula. json_encode() için tüm dizgelerin UTF-8 olması gerekir; JSON_THROW_ON_ERROR kullanıldığında kodlama hatası bir JsonException olarak yakalanabilir.
- Özgün değeri dönüşümden önce koru.
mb_check_encoding($deger, 'UTF-8')ile mevcut değeri denetle.- Kaynak kodlama biliniyorsa yalnızca bir kez UTF-8'e dönüştür.
- Dönüşümden sonra
mb_check_encoding()ile yeniden doğrula. JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERRORile JSON üretmeyi dene ve hatayı yakala.
Kaynak kodlama bilinmiyorsa rastgele bir kodlama adı seçerek dönüşüm yapmak, Türkçe karakterleri değiştirebilir veya veri kaybına yol açabilir. Örneğin kaynak gerçekten Windows-1252 ise mb_convert_encoding($deger, 'UTF-8', 'Windows-1252') kullanılabilir; ancak bu kodlama yalnızca verinin gerçek kaynağı buysa doğrudur. mb_convert_encoding() kaynak ve hedef kodlamayı açıkça belirtmeye izin verir.
<?php
$gecerli = "İzmir'de ş, ğ, ü, ö, ç.";
$bozuk = "İzmir xC3x28";
echo json_encode($gecerli, JSON_UNESCAPED_UNICODE) . PHP_EOL;
$sonuc = json_encode($bozuk, JSON_UNESCAPED_UNICODE);
echo ($sonuc === false ? "false" : $sonuc) . PHP_EOL;
echo (json_last_error() === JSON_ERROR_UTF8 ? "JSON_ERROR_UTF8" : "başka hata");
echo " / " . json_last_error_msg() . PHP_EOL;
try {
json_encode($bozuk, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo "Yakalandı: " . $e->getMessage() . PHP_EOL;
}
Burada ilk değer geçerli Türkçe karakterler içerdiği için JSON'a dönüştürülür. xC3x28 ise geçersiz bir UTF-8 byte dizisini simüle eder. Hatasız çağrıda json_last_error() ve json_last_error_msg() ile durum okunabilir; JSON_THROW_ON_ERROR kullanılan çağrıda ise istisna yakalanır.
Beklenen çıktı:
"İzmir'de ş, ğ, ü, ö, ç."
false
JSON_ERROR_UTF8 / Malformed UTF-8 characters, possibly incorrectly encoded
Yakalandı: Malformed UTF-8 characters, possibly incorrectly encoded
JSON_UNESCAPED_UNICODE yalnızca geçerli Unicode karakterlerinin JSON içinde doğrudan görünmesini sağlar. Bozuk byte'ı onarmaz. JSON üretiminin başarılı olması da dönüşümün anlamı koruduğunu tek başına kanıtlamaz. Özellikle ad, açıklama, adres ve kullanıcı mesajı gibi kritik alanları dönüşüm öncesi ve sonrası ayrıca incele. Üretim reçetesi şudur: doğrula, kaynağı belirle, gerekiyorsa dönüştür, yeniden doğrula, JSON üret.
Sık Sorulan Sorular
JSON_UNESCAPED_UNICODE bu PHP hatasını neden düzeltmez?
Bu seçenek yalnızca geçerli çok baytlı karakterlerin JSON içinde uXXXX biçimi yerine karakter olarak yazılmasını sağlar. Geçersiz UTF-8 byte'larını değiştirmez veya onarmaz.
mb_check_encoding() false döndürürse hangi kodlama dönüşümü uygulanmalı?
Tek bir evrensel dönüşüm yoktur. Verinin gerçek kaynak kodlamasını belirleyip o kodlamadan UTF-8'e dönüşüm yapmalısın. Kaynak bilinmiyorsa rastgele dönüşüm uygulamak yerine verinin geldiği katmanı incelemek daha güvenlidir.
Veritabanından gelen bir değerin geçersiz UTF-8 içerdiği nasıl anlaşılır?
PHP değişkenine alınan değeri mb_check_encoding($deger, 'UTF-8') ile denetleyebilirsin. Sonuç false ise değer, PHP'nin beklediği UTF-8 dizisi olarak doğrulanmamıştır.
json_last_error() ile JSON_THROW_ON_ERROR arasında ne fark vardır?
json_last_error() ve json_last_error_msg(), hata oluşturan çağrıdan sonra hata durumunu okumaya yarar. JSON_THROW_ON_ERROR ise hata meydana geldiğinde JsonException fırlatır ve bu istisna try-catch ile ele alınır.
Geçerli JSON üretmek için amaç yalnızca hata mesajını kaldırmak değil, dönüşümün veriyi koruduğunu denetleyerek güvenilir bir çıktı oluşturmaktır.