PHP'de JSON İşlemleri: encode, decode ve Veri Değişimi

PHP'de JSON İşlemleri: encode, decode ve Veri Değişimi

5 dakika okuma

JSON, uygulamalar arası veri değişiminin ortak biçimidir. PHP'nin sunduğu iki fonksiyon işi büyük ölçüde halleder, ancak varsayılan davranışları üretim için uygun değildir: hata durumunda istisna fırlatılmaz, Türkçe karakterler kaçış dizilerine çevrilir ve derinlik sınırı sessizce uygulanır.

Bu bölümde kodlama ve çözümleme bayrakları, hata yönetimi, nesnelerin JSON karşılığının tanımlanması ve gelen istek gövdesinin güvenli işlenmesi ele alınıyor.

Kodlama ve bayraklar

Varsayılan çıktıda Türkçe karakterler ve eğik çizgiler kaçırılır. Sonuç geçerli JSON'dur, ancak okunması güçtür ve gereksiz yer kaplar. Bayraklar bu davranışı düzeltir.

PHP7 satır
$veri = ['baslik' => 'Kaz Dağları', 'adres' => '/yazi/kaz-daglari'];

echo json_encode($veri);
// {"baslik":"Kaz Da\u011flar\u0131","adres":"\/yazi\/kaz-daglari"}

echo json_encode($veri, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// {"baslik":"Kaz Dağları","adres":"/yazi/kaz-daglari"}

Hata durumunda fonksiyon false döndürür ve bu değer sessizce boş çıktıya dönüşebilir. İstisna bayrağı kullanılarak sorun görünür hale getirilir.

PHP6 satır
try {
    $cikti = json_encode($veri, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
} catch (JsonException $e) {
    // en sık neden: geçersiz UTF-8 baytları ya da kaynak tipinde değer
    throw new RuntimeException('Yanıt üretilemedi', previous: $e);
}

Okunabilir çıktı yalnızca hata ayıklama ve yapılandırma dosyalarında kullanılır. API yanıtlarında girinti eklemek, yanıt boyutunu bir kazanç sağlamadan büyütür.

Çözümleme

İkinci parametre çıktının biçimini belirler. Birleşimli dizi, alanlara erişimi kolaylaştırır; nesne dönüşü ise alan adları geçerli birer değişken adı olmadığında sorun çıkarır.

PHP7 satır
$veri = json_decode($ham, associative: true, flags: JSON_THROW_ON_ERROR);

// derinlik sınırı, iç içe yapılarda beklenmedik girdiye karşı korur
$veri = json_decode($ham, true, depth: 32, flags: JSON_THROW_ON_ERROR);

// büyük tam sayılar ondalığa dönüşerek duyarlılık kaybetmesin
$veri = json_decode($ham, true, flags: JSON_BIGINT_AS_STRING | JSON_THROW_ON_ERROR);

Çözümleme sonucunda dönen değer her zaman dizi olmaz. Gövde tek bir sayı ya da metin içeriyorsa sonuç da o tipte olur; bu yüzden beklenen yapı açıkça denetlenmelidir. Geçerli JSON olan null metni de null döndürür ve hatalı çözümlemeden ayırt edilmesi gerekir.

Yalnızca geçerliliğin sorgulandığı durumlarda, tüm yapıyı belleğe almadan denetim yapan fonksiyon kullanılır. Bu, büyük gövdelerde belirgin bellek kazancı sağlar.

PHP4 satır
if (!json_validate($ham)) {          // PHP 8.3 ile geldi
    http_response_code(400);
    exit;
}

Nesneleri JSON'a çevirmek

Bir nesne doğrudan kodlandığında yalnızca dışarı açık özellikleri çıktıya girer. Çıktının biçimini denetlemek için ilgili arayüz uygulanır; böylece iç yapı ile dış sözleşme birbirinden ayrılır.

PHP21 satır
final class Yazi implements JsonSerializable
{
    public function __construct(
        private readonly int $id,
        private readonly string $baslik,
        private readonly DateTimeImmutable $olusturma,
        private readonly string $icPano,      // dışarı verilmeyecek alan
    ) {}

    public function jsonSerialize(): array
    {
        return [
            'id' => $this->id,
            'baslik' => $this->baslik,
            'olusturma' => $this->olusturma->format(DATE_ATOM),
        ];
    }
}

echo json_encode(new Yazi(1, 'Örnek', new DateTimeImmutable(), 'gizli'),
                 JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);

Bu ayrım önemlidir: iç yapıdaki bir alan adı değiştiğinde, dönüşüm metodu güncellenerek dış sözleşme korunur. Nesnenin doğrudan kodlanması durumunda ise her yeniden adlandırma, API tüketicilerini kıran bir değişikliğe dönüşür.

Değer taşıyan enumlar kendiliğinden kendi değerlerine dönüşür. Değer taşımayan enumlar kodlanamaz; API yanıtında kullanılacak enumların değerli tanımlanması bu yüzden pratik bir kuraldır.

Gelen isteği okumak

JSON gövdesiyle gelen istekler süper globallerde bulunmaz; gövde ham akıştan okunur. İçerik türü denetlenmeli ve çözümleme başarısız olduğunda uygun durum kodu döndürülmelidir.

PHP21 satır
$tur = $_SERVER['CONTENT_TYPE'] ?? '';

if (!str_starts_with($tur, 'application/json')) {
    http_response_code(415);
    exit;
}

$ham = file_get_contents('php://input');

try {
    $veri = json_decode($ham, true, 32, JSON_THROW_ON_ERROR);
} catch (JsonException) {
    http_response_code(400);
    echo json_encode(['hata' => 'Gövde geçerli JSON değil']);
    exit;
}

if (!is_array($veri)) {
    http_response_code(400);
    exit;
}

Çözümlenen veri doğrulanmadan kullanılmaz. Alanların varlığı, tipi ve değer aralığı denetlenmelidir; gövdenin JSON olması içeriğinin beklenen yapıda olduğunu göstermez.

PHP7 satır
$baslik = $veri['baslik'] ?? null;

if (!is_string($baslik) || mb_strlen(trim($baslik)) < 3) {
    http_response_code(422);
    echo json_encode(['hata' => 'baslik en az üç karakter olmalı'], JSON_UNESCAPED_UNICODE);
    exit;
}

Sayılar ve duyarlılık

JSON tarafında tam sayı ile ondalık sayı ayrımı biçimden anlaşılır. PHP'nin ondalık sayıları ikilik tabanda saklaması nedeniyle, para tutarlarının JSON üzerinden ondalık olarak taşınması yuvarlama farklarına yol açabilir. Tutarlar en küçük birimde tam sayı olarak ya da metin biçiminde taşınmalıdır.

PHP2 satır
echo json_encode(['tutar' => 0.1 + 0.2]);        // {"tutar":0.30000000000000004}
echo json_encode(['tutar_kurus' => 30]);        // {"tutar_kurus":30}

Çok büyük tam sayılar da dikkat gerektirir. JavaScript tarafında güvenli tam sayı aralığının üzerindeki kimlik değerleri duyarlılık kaybeder; bu tür değerler metin olarak gönderilmelidir.

Boş dizilerin kodlanması da sık karşılaşılan bir karışıklık kaynağıdır. PHP tarafında dizi hem liste hem eşleme olabildiği için, boş bir dizi köşeli parantezle kodlanır ve nesne bekleyen tüketici tarafında hata oluşabilir. Nesne beklendiği kesin olan yerlerde ilgili bayrak kullanılır.

PHP5 satır
echo json_encode(['ayarlar' => []]);                        // {"ayarlar":[]}
echo json_encode(['ayarlar' => []], JSON_FORCE_OBJECT);     // {"ayarlar":{}}

// anahtarları sıralı olmayan diziler de nesne olarak kodlanır
echo json_encode([1 => 'a', 3 => 'b']);                     // {"1":"a","3":"b"}

Yanıt üretmek

JSON yanıtı üretirken içerik türü başlığı, karakter kümesi ve durum kodu birlikte ayarlanır. Bu üçlü tek bir yardımcı fonksiyonda toplandığında, her uçta aynı biçimin kullanılması sağlanır.

PHP14 satır
function jsonYanit(array $govde, int $kod = 200): never
{
    http_response_code($kod);
    header('Content-Type: application/json; charset=utf-8');

    echo json_encode(
        $govde,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR,
    );

    exit;
}

jsonYanit(['ok' => true, 'id' => $yeniId], 201);

Hata yanıtlarının da aynı yapıda olması, tüketen tarafın tek bir çözümleme kodu yazmasını sağlar. Durum kodu ile gövdedeki bilgi çelişmemelidir; başarısız bir işlemin 200 koduyla dönmesi, istemci tarafında hata denetiminin atlanmasına yol açar.

Büyük veri kümeleri

Tek bir JSON dizisinde binlerce kayıt taşımak, hem üreten hem tüketen tarafta tüm verinin belleğe alınmasını gerektirir. Bu tür aktarımlarda satır başına bir JSON nesnesi yazan biçim tercih edilir; okuyucu dosyayı satır satır işleyebilir.

PHP8 satır
$cikti = fopen('php://output', 'w');

foreach ($kayitlar as $kayit) {
    fwrite($cikti, json_encode($kayit, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . "
");
}

fclose($cikti);

Bu biçimde her satır kendi başına geçerli bir JSON belgesidir. Aktarım yarıda kesilse bile okunabilen satırlar işlenebilir, bellek kullanımı kayıt sayısından bağımsız kalır.

Kontrol listesi

  • Kodlama ve çözümlemede JSON_THROW_ON_ERROR kullanılıyor.
  • Türkçe içerikte JSON_UNESCAPED_UNICODE etkin.
  • Çözümlemede derinlik sınırı açıkça veriliyor.
  • Nesneler JsonSerializable ile dış sözleşmeyi kendisi tanımlıyor.
  • Gelen isteklerde içerik türü denetleniyor, hatalarda doğru durum kodu dönüyor.
  • Çözümlenen veri alan alan doğrulanıyor.
  • Para ve büyük kimlik değerleri ondalık sayı olarak taşınmıyor.

İlgili yazılar

PHP eğitim serisi

Bu yazı, temel sözdiziminden üretim ortamına kadar ilerleyen 25 bölümlük serinin bir parçası. Bölümler sırayla okunacak biçimde hazırlandı, ancak her biri tek başına da kullanılabilir.

Temeller

  1. Kurulum ve temel sözdizimi
  2. Döngüler ve akış kontrolü
  3. Diziler
  4. Fonksiyonlar
  5. String fonksiyonları
  6. Süper globaller
  7. Form işleme
  8. Dosya işlemleri

Dilin ayrıntıları

  1. Tip sistemi ve strict_types
  2. Hata ve istisna yönetimi
  3. Düzenli ifadeler
  4. Tarih ve saat işlemleri

Nesne yönelimli PHP

  1. Sınıf ve nesne
  2. Kalıtım, arayüz ve trait
  3. Statik üyeler, sabitler ve enum
  4. Namespace, Composer ve autoload

Veri ve güvenlik

  1. PDO ile veritabanı işlemleri
  2. Oturum yönetimi ve kimlik doğrulama
  3. Güvenli çıktı ve XSS
  4. JSON ile veri değişimi (bu yazı)

Dış dünya ve üretim

  1. cURL ile dış API çağırma
  2. Kendi REST API'ni yazmak
  3. PHPUnit ile birim test
  4. Hata ayıklama ve performans ölçümü
  5. Üretime alma ve güvenlik

Yorumlar

Bu form reCAPTCHA ile korunur; Google Gizlilik Politikası ve Hizmet Şartları geçerlidir.

İlk yorumu sen yap.

İlgili Yazılar