Ders 10 / 14
Dizgi İşleme
sprintf ve printf ile biçimlendirme; strlen ile mb_strlen arasındaki bayt ve karakter ayrımı; bozuk JSON'un json_decode'dan null olarak dönmesi; json_last_error ve JSON_THROW_ON_ERROR ile ayrımın yapılması; implode ve strlen'in dizi argümanındaki davranışı.
Yayın Güncelleme
Bu derste
- sprintf ile sayı ve dizgi biçimlendirir ve genişlik belirtecinin baytla çalıştığını ölçer
- Bozuk JSON'un json_decode'dan null döndürdüğünü ölçer ve dönüş sınıfına yazar
- json_last_error ve JSON_THROW_ON_ERROR ile geçerli null'u bozuk girdiden ayırır
İçindekiler
Dizi Fonksiyonları dersi, dönüş kanalının ayrım gücünün çağıranın ayrım gücüyle sınırlı olduğunu gösterdi: 0 ile false ayırt edilmeyince bulunan ilk eleman “yok” sayıldı. Dizgi işlemede aynı sınır daha sert bir biçimde çıkıyor. Bir dizgi çözüldüğünde dönen null, hem geçerli bir null değerinden hem bozuk girdiden gelebiliyor ve değerin kendisi hangisinin olduğunu söylemiyor.
Dizgi (string) PHP’de bayt dizisidir; bir karakterin kaç bayt tuttuğu dizgiyi okuyan fonksiyona bağlıdır. Ders iki konu işliyor: dizgiyi biçimlendiren ve ölçen fonksiyonlar, ve dizgiyi JSON olarak kodlayıp çözen fonksiyonlar. Sınıf adları önceki derslerdeki anlamıyla kullanılır: sessiz, dönüş, uyarı, durdurur.
Biçimlendirme ve Bayt Ölçümü
sprintf bir biçim dizgisine göre değerleri yerleştirir; printf aynı biçimlendirmeyi doğrudan yazdırır. Genişlik, hizalama ve ondalık hane sayısı belirteçle verilir.
<?php // bicim.php — sprintf ile bicimlendirme echo sprintf('%05d', 42), "\n"; echo sprintf('%.2f', 3.14159), "\n"; echo sprintf('%-8s|', 'php'), "\n"; echo sprintf('%8s|', 'php'), "\n"; echo sprintf('%s kitap, %d sayfa', 'Ders', 120), "\n"; printf("%x %o %b\n", 255, 8, 5); echo sprintf('%1$s-%2$s-%1$s', 'a', 'b'), "\n";
00042
3.14
php |
php|
Ders kitap, 120 sayfa
ff 10 101
a-b-a
Genişlik belirteci karakter değil bayt sayar. Çok baytlı bir harf içeren dizgide bu fark görünür; aşağıdaki dosya yalnız uzunlukları basıyor, hizalı bir sütun kurmuyor.
<?php // bayt.php — strlen bayt, mb_strlen karakter sayar $ad = 'señor'; echo 'strlen: ', strlen($ad), "\n"; echo 'mb_strlen: ', mb_strlen($ad), "\n"; $dolgulu = sprintf('%-8s', $ad); echo 'dolgulu bayt: ', strlen($dolgulu), "\n"; echo 'dolgulu karakter: ', mb_strlen($dolgulu), "\n"; echo 'ascii strlen: ', strlen('senor'), "\n"; echo 'ascii mb_strlen: ', mb_strlen('senor'), "\n";
strlen: 6 mb_strlen: 5 dolgulu bayt: 8 dolgulu karakter: 7 ascii strlen: 5 ascii mb_strlen: 5
strlen('señor') altı, mb_strlen('señor') beş verdi: ñ iki bayttır ve tek karakterdir. sprintf('%-8s') ile sekiz bayta doldurulan dizgi yalnız yedi karakter genişliğinde görünür; hizalı bir sütunda bu kayma bir sonraki sütunu sola çeker. Tamamen ASCII olan senor için iki fonksiyon aynı sayıyı verir, bu yüzden fark yalnız çok baytlı veri geldiğinde ortaya çıkar. Sorun hiçbir kanaldan haber vermez: sessiz. Çok baytlı harf barındıran veri hizalı bir sütuna konmadan önce genişlik bayt değil karakter üzerinden hesaplanmalıdır.
DZ13. Bu ders mb_ ailesinin yalnız mb_strlen’ini kullanır ve girdinin tek bir kodlamada (UTF-8) olduğunu varsayar. Kodlamanın belirtilmediği ya da karışık olduğu girdi bu dersin dışındadır ve ölçülmez.
Bozuk JSON
JSON, dizileri ve ilişkisel dizileri metne çeviren bir değiş tokuş biçimidir. json_encode PHP değerini, json_decode metni PHP değerine çevirir. İkisi de başarısız olduğunda ne yaptığını dinleyici sınıflıyor; kanal.php aşağıda tam metniyle veriliyor ve çıktı bloğu olmadığı için yalnız diske yazılır.
<?php // kanal.php — bir sorunun hangi kanaldan duyuldugunu siniflandiran yardimcilar // Yakalanan uyarilarin seviye adlari burada birikir; ileti metni tutulmaz. $GLOBALS['duyulan_uyarilar'] = []; set_error_handler(function (int $seviye): bool { $adlar = [E_WARNING => 'E_WARNING', E_NOTICE => 'E_NOTICE', E_DEPRECATED => 'E_DEPRECATED']; $GLOBALS['duyulan_uyarilar'][] = $adlar[$seviye] ?? 'E_' . $seviye; return true; }); // Degeri tipiyle birlikte kisa bir metne cevirir. function yaz(mixed $deger): string { return match (true) { $deger === null => 'null', is_bool($deger) => $deger ? 'true' : 'false', is_array($deger) => 'dizi(' . count($deger) . ')', default => get_debug_type($deger) . ' ' . $deger, }; } // Isi calistirir ve sorunun hangi kanaldan duyuldugunu sirayla sorar: // once firlatma, sonra uyari, sonra false/null donus; hicbiri yoksa sessiz. function dinle(callable $is): array { $GLOBALS['duyulan_uyarilar'] = []; try { $sonuc = $is(); } catch (Throwable $hata) { return ['durdurur', get_class($hata)]; } if ($GLOBALS['duyulan_uyarilar']) { $seviyeler = implode(',', array_unique($GLOBALS['duyulan_uyarilar'])); return ['uyari', $seviyeler . ', sonuc ' . yaz($sonuc)]; } if ($sonuc === false || $sonuc === null) { return ['donus', 'sonuc ' . yaz($sonuc)]; } return ['sessiz', 'sonuc ' . yaz($sonuc)]; } // Kodu ayri bir PHP yurutmesinde calistirir; cikis kodu 255 ise yurutme durmustur. function ayri_yurut(string $kod): array { exec('php -d display_errors=0 -r ' . escapeshellarg($kod) . ' 2>&1', $satirlar, $cikis); $sinif = $cikis === 255 ? 'durdurur' : 'sessiz'; return [$sinif, 'cikti ' . count($satirlar) . ' satir, cikis ' . $cikis]; }
<?php // json.php — json ve dizgi islemlerinde sorun kanallari require 'kanal.php'; $gozlemler = [ 'bozuk json' => fn() => json_decode('{bozuk'), 'gecerli json' => fn() => json_decode('{"a":1}', true), 'bozuk kodlama' => fn() => json_encode("\xB1"), 'istisnali kodlama' => fn() => json_encode("\xB1", JSON_THROW_ON_ERROR), 'ic ice implode' => fn() => implode(',', [1, [2, 3]]), 'dizi ile strlen' => fn() => strlen([1, 2]), ]; foreach ($gozlemler as $ad => $gozlem) { [$sinif, $kanit] = dinle($gozlem); printf("%-19s%-9s%s\n", $ad, $sinif, $kanit); }
bozuk json donus sonuc null gecerli json sessiz sonuc dizi(1) bozuk kodlama donus sonuc false istisnali kodlama durdurur JsonException ic ice implode uyari E_WARNING, sonuc string 1,Array dizi ile strlen durdurur TypeError
İlk satır kursun bozuk json gözlemidir: json_decode('{bozuk') null döndürdü, hiçbir seviye bildirilmedi. Sınıf dönüş. Üçüncü satır aynı kanalın kodlama yönü: geçersiz bir bayt dizisini kodlamak false döndürür. İkisi de bir hata olduğunu yalnız dönüş değeriyle söyler.
Dördüncü satır kanalın değiştiğini gösteriyor. JSON_THROW_ON_ERROR bayrağı verildiğinde aynı sorun JsonException olarak fırlar; sınıf durdurur. Bayrak, dönüş kanalındaki bir sorunu istisna kanalına taşır ve çağırana kontrolü bırakır: yakalamazsa yürütme durur. Beşinci satır dizgi işlemenin üçüncü kanalı: iç içe dizi implode edildiğinde iç dizi 'Array' dizgisine çevrilir ve E_WARNING bildirilir, yani uyarı. Sonuncusu tip kanalıdır: strlen bir dizi argümanıyla çağrıldığında TypeError fırlatır ve durdurur.
Metin Arama ve Kesme
Dizgi içinde arama yapan strpos, aranan başlangıçta bulunduğunda 0, hiç bulunamadığında false döndürür. Dizi Fonksiyonları dersindeki array_search ile aynı kanal ve aynı tuzak: gevşek karşılaştırma ikisini ayırt etmez. PHP bu iş için dönüş değerine bakmayı gerektirmeyen str_contains fonksiyonunu sunar.
<?php // arama.php — dizgi arama ve kesme islevleri $metin = 'php dersi'; echo 'strpos basta: ', var_export(strpos($metin, 'php'), true), "\n"; echo 'strpos yok: ', var_export(strpos($metin, 'js'), true), "\n"; echo 'gevsek basta: ', (strpos($metin, 'php') == false ? 'bulunamadi' : 'bulundu'), "\n"; echo 'str_contains basta: ', var_export(str_contains($metin, 'php'), true), "\n"; echo 'substr: ', var_export(substr($metin, 4), true), "\n"; echo 'substr uzak: ', var_export(substr($metin, 20), true), "\n"; echo 'explode: ', json_encode(explode(' ', $metin)), "\n"; echo 'bos ayirici: ', json_encode(explode(',', '')), "\n"; echo 'trim: ', var_export(trim(" a b \n"), true), "\n"; echo 'str_replace: ', str_replace('php', 'go', $metin), "\n";
strpos basta: 0 strpos yok: false gevsek basta: bulunamadi str_contains basta: true substr: 'dersi' substr uzak: '' explode: ["php","dersi"] bos ayirici: [""] trim: 'a b' str_replace: go dersi
strpos başta bulunan aranan için 0 döndürdü ve gevşek karşılaştırma bunu bulunamadi saydı; str_contains aynı soruyu true ile doğru yanıtladı. substr başlangıç dizgi sınırının ötesindeyse boş dizgi döndürür, hata vermez. explode boş bir dizgiyi ayırdığında boş liste değil, tek boş dizgili bir liste verir: tek elemanlı [""]. Bu sonuç bir sınır koşuludur ve “veri yok” ile “boş veri var” ayrımını sessizce siler.
DZ14. str_contains, str_starts_with ve benzerleri sorunu kanal olarak çözmez; yalnız soruyu doğru tipe çevirir. Kullanım rehberi yazmak bu dersin işi değildir ve yukarıdaki tabloda yer almayan davranışlar için bir iddia kurulmaz.
Çok baytlı bir harfi bayt düzeyinde kesen fonksiyon, geçersiz bir dizgi üretir ve bu dizgi bir sonraki kodlama çağrısında sorun çıkarır. Büyük harfe çevirme de aynı bayt/karakter ayrımına bağlıdır.
<?php // kes.php — bayt duzeyinde kesmenin ve buyutmenin sonucu $ad = 'señor'; $bayt_kesik = substr($ad, 0, 3); $karakter_kesik = mb_substr($ad, 0, 3); echo 'bayt kesik uzunluk: ', strlen($bayt_kesik), "\n"; echo 'bayt kesik gecerli mi: ', var_export(mb_check_encoding($bayt_kesik, 'UTF-8'), true), "\n"; echo 'bayt kesik json: ', var_export(json_encode($bayt_kesik), true), "\n"; echo 'karakter kesik uzunluk: ', mb_strlen($karakter_kesik), "\n"; echo 'karakter kesik gecerli mi: ', var_export(mb_check_encoding($karakter_kesik, 'UTF-8'), true), "\n"; echo 'buyutme ayni mi: ', var_export(strtoupper($ad) === mb_strtoupper($ad), true), "\n";
bayt kesik uzunluk: 3 bayt kesik gecerli mi: false bayt kesik json: false karakter kesik uzunluk: 3 karakter kesik gecerli mi: true buyutme ayni mi: false
substr($ad, 0, 3) üç bayt aldı ve ñ harfinin ilk baytını kesip bıraktı; sonuç geçerli bir UTF-8 dizgisi değil ve json_encode onu false döndürerek reddetti. mb_substr aynı kesimi karakter sınırında yaptı ve sonuç geçerli kaldı. strtoupper yalnız ASCII harflerini büyütür; mb_strtoupper çok baytlı harfi de dönüştürür, bu yüzden iki sonuç eşit çıkmadı. Kesme ve büyütme fonksiyonunun bayt ile karakter arasındaki seçimi sorunu hiçbir kanaldan duyurmaz: kesik dizgi ancak sonradan, kodlama aşamasında dönüş değeriyle fark edilir.
Biçim Belirteçlerinin Sınırı
sprintf biçim dizgisindeki belirteçlerle argümanları eşler; belirteç sayısı, tipi ve sözdizimi çağrının kanalını belirler.
<?php // belirtec.php — sprintf belirteclerinin sinirlari require 'kanal.php'; $gozlemler = [ 'genislik ve duyarlik' => fn() => sprintf('%05.1f|%-4s|', 3.14159, 'ab'), 'dolgu karakteri' => fn() => sprintf("%'*8s", 'php'), 'isaretli sayi' => fn() => sprintf('%+d', 5), 'dizgi sayi olarak' => fn() => sprintf('%d', 'abc'), 'eksik arguman' => fn() => sprintf('%s %s', 'a'), 'bilinmeyen belirtec' => fn() => sprintf('%y', 1), ]; foreach ($gozlemler as $ad => $gozlem) { [$sinif, $kanit] = dinle($gozlem); printf("%-21s%-9s%s\n", $ad, $sinif, $kanit); }
genislik ve duyarlik sessiz sonuc string 003.1|ab | dolgu karakteri sessiz sonuc string *****php isaretli sayi sessiz sonuc string +5 dizgi sayi olarak sessiz sonuc string 0 eksik arguman durdurur ArgumentCountError bilinmeyen belirtec durdurur ValueError
İlk üç satır belirteçlerin normal çalışmasını gösteriyor ve sessizdir. Dördüncü satır asıl noktadır: %d belirteci sayısal olmayan bir dizgiyi 0’a çevirir ve hiçbir seviye bildirmez; yanlış tipte veri biçimlendirmede sessizce kaybolur. Argüman sayısı belirteç sayısından az olduğunda ArgumentCountError, tanımsız bir belirteç yazıldığında ValueError fırlar; ikisi de durdurur. Aynı fonksiyon, bir hata sınıfında sessiz, bir başkasında durdurucu davranıyor: kanal çağrının hatasının türüne bağlıdır.
Doldurma, Sayı Biçimi ve Karşılaştırma
Sayıların ve dizgilerin görüntülenmesi için kullanılan birkaç yardımcı fonksiyon, hizalama ve ayırıcı gibi ayrıntıları argümanlara taşır.
<?php // yardimci.php — doldurma, sayi bicimi ve dizgi karsilastirma require 'kanal.php'; echo number_format(1234567.891, 2, ',', '.'), "\n"; echo number_format(0.5), ' ', number_format(1.5), ' ', number_format(2.5), "\n"; echo str_pad('7', 3, '0', STR_PAD_LEFT), "|", str_pad('ab', 6, '-', STR_PAD_BOTH), "|\n"; echo ucwords('php dizgi isleme'), "\n"; echo strrev('abc'), ' ', str_word_count('bir iki uc'), "\n"; echo 'strcmp: ', strcmp('a', 'b'), ' ', strcmp('b', 'a'), ' ', strcmp('a', 'a'), "\n"; echo 'spaceship: ', 'a' <=> 'b', ' ', 'b' <=> 'a', ' ', 'a' <=> 'a', "\n"; $gozlemler = [ 'sayisal dizgi esit' => fn() => '10' == '1e1', 'sayisal dizgi kati' => fn() => '10' === '1e1', 'baslangic sayisal' => fn() => '12abc' + 1, 'tam sayisal degil' => fn() => 'abc' + 1, 'sayiya cevirme' => fn() => (int) '12abc', ]; foreach ($gozlemler as $ad => $gozlem) { [$sinif, $kanit] = dinle($gozlem); printf("%-20s%-9s%s\n", $ad, $sinif, $kanit); }
1.234.567,89 1 2 3 007|--ab--| Php Dizgi Isleme cba 3 strcmp: -1 1 0 spaceship: -1 1 0 sayisal dizgi esit sessiz sonuc true sayisal dizgi kati donus sonuc false baslangic sayisal uyari E_WARNING, sonuc int 13 tam sayisal degil durdurur TypeError sayiya cevirme sessiz sonuc int 12
number_format ondalıkta yuvarlar (0.5, 1.5, 2.5 için 1 2 3) ve binlik ile ondalık ayırıcısı argümanlarla belirlenir. str_pad sol, sağ ya da iki yandan doldurur. strcmp ve <=> sıralama için aynı üç değeri verir.
Karşılaştırmada asıl dikkat edilecek nokta sayısal dizgilerdir: '10' == '1e1' doğrudur, çünkü iki taraf da sayıya çevrilir; === ise yanlış verir. '12abc' + 1 13 üretir ama E_WARNING verir, 'abc' + 1 ise TypeError ile durur. (int) '12abc' sessizce 12 verir; aynı girdi için dönüştürme sessiz, aritmetik uyarılı, tam sayısal olmayan dizgide aritmetik durdurucudur.
Sınırlayıcı Ölçüm
Dönüş kanalı ayırt edemez. Geçerli null değerinin çözümü ile bozuk girdinin çözümü aynı değeri verir.
<?php // ayirt.php — null geri donusu iki ayri durumdan gelebilir $bozuk = json_decode('{bozuk'); echo 'bozuk: ', var_export($bozuk, true), ' hata ', json_last_error(), "\n"; $gecerli = json_decode('null'); echo 'gecerli null: ', var_export($gecerli, true), ' hata ', json_last_error(), "\n"; echo 'esit mi: ', var_export($bozuk === $gecerli, true), "\n"; echo 'hata adi: ', json_last_error() === JSON_ERROR_NONE ? 'yok' : 'var', "\n"; try { json_decode('{bozuk', false, 512, JSON_THROW_ON_ERROR); echo "bozuk: firlatmadi\n"; } catch (JsonException $e) { echo 'bozuk: ', get_class($e), ' kod ', $e->getCode(), "\n"; } $sonuc = json_decode('null', false, 512, JSON_THROW_ON_ERROR); echo 'gecerli null, bayrakli: ', var_export($sonuc, true), "\n";
bozuk: NULL hata 4 gecerli null: NULL hata 0 esit mi: true hata adi: yok bozuk: JsonException kod 4 gecerli null, bayrakli: NULL
İki çağrı da null döndürdü ve === ile eşit çıktılar. Ayrımı json_last_error() yapıyor: bozuk girdide dört (JSON_ERROR_SYNTAX), geçerli null’da sıfır. Bu fonksiyon son çağrının hata durumunu taşır; bir sonraki json_decode çağrısı onu sıfırlar, bu yüzden hemen ardından okunması gerekir. Daha sağlam yol JSON_THROW_ON_ERROR bayrağıdır: bozuk girdi JsonException fırlatır, geçerli null ise null olarak döner ve iki durum artık ayrı kanallardan gelir.
DZ15. json_last_error() genel bir hata durumunu tutar ve ders onu yalnız json_decode çağrısının hemen ardından okur. Başka bir fonksiyonun araya girdiği durumlar ölçülmez. Ders hiçbir ini değerine güvenmez; JSON_THROW_ON_ERROR bayrağı çağrıya açıkça verilir.
Özet
sprintfveprintfbiçim dizgisine göre değer yerleştirir; genişlik belirteci karakter değil bayt sayar.strlen('señor')altı,mb_strlen('señor')beş verir; çok baytlı harf barındıran veri hizalı sütuna konmadan önce genişlik karakterle hesaplanmalıdır.json_decode('{bozuk')nulldöndürür; bu dersin ölçü sınıfı dönüştür (bozuk json). Geçersiz bayt dizisi içinjson_encodefalsedöndürür.JSON_THROW_ON_ERRORaynı sorunuJsonExceptionolarak fırlatır; sınıf durdurur.- Geçerli
nullile bozuk JSON ikisi denulldöndürür; ayrımıjson_last_error()(sıfır ya da dört) ya daJSON_THROW_ON_ERRORyapar. - İç içe dizinin
implode’u'Array'veE_WARNINGverir; dizi argümanıylastrlenTypeErrorfırlatır.
Sonraki Adım
Dizilerin ve dizgilerin sorunları dört kanaldan duyulabiliyor: sessiz, dönüş, uyarı ve durdurur. Sıradaki konu, verinin kodun içinden değil, dışarıdan geldiği yerdir. Süper Küresel Değişkenler dersi, istek verisi eksik olduğunda hangi kanaldan ne duyulduğunu ölçüyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.