Ders 01 / 14
PHP'nin Yürütme Modeli
PHP'de bir sorunun sessiz, dönüş, uyarı ve durdurur kanallarından hangisiyle duyulduğunun ölçülmesi; yürütmenin istek başına başlayıp bitmesi ve yakalanmayan hatanın yalnız o yürütmeyi kesmesi.
Yayın Güncelleme
Bu derste
- Bir sorunun sessiz, dönüş, uyarı ya da durdurur kanalından duyulduğunu ölçerek sınıflandırır
- Çekirdek ölçüm dosyalarını okuyup on dört gözlemin dağılımını yorumlar
- Yürütmenin istek başına başlayıp bittiğini yerleşik sunucuyla gösterir
- Yakalanmayan bir hatanın yalnız o isteği kestiğini durum koduyla doğrular
İçindekiler
C ve C++ müfredatı (M11) bir ayrımla kapandı: orada sorunların büyük bölümü derlemede yakalanıyordu, geri kalanı da tanımsız davranışa düşüyordu ve dil hiçbir şey söylemiyordu. PHP’de derleme aşaması bu kursta ölçülmez. Bir dosya yürütülürken okunur, yorumlanır ve çalıştırılır; sorunların neredeyse tamamı yürütmenin içinde duyulur. Ama aynı sorun her yerde aynı biçimde duyulmaz.
Bu kurs tek bir soru sorar: bu sorun hangi kanaldan duyuluyor? Eksik bir anahtar, uymayan bir tip ya da bulunamayan bir dosya dört ayrı yoldan haber verebilir. Bu ders önce o dört yolu adlandırıyor ve bir ölçüm çekirdeğiyle on dört gözlemi sayıyor; sonra yürütmenin kendisinin nerede başlayıp nerede bittiğini yerleşik sunucuyla ölçüyor. Önkoşul olan Programlama Temelleri kursu (M01/K02) değişken, tip, döngü ve fonksiyon kavramlarını kurmuştu; burada onlar tekrar anlatılmıyor.
Dört Kanal ve Ölçüm Çekirdeği
Kurs dört sınıf kullanır ve adları kurs boyunca değişmez. Sessiz: bir sonuç üretilir ve hiçbir sinyal yoktur, sorun ancak sonuca bakılarak görülür. Dönüş: fonksiyon false ya da null döndürür, duymak çağıranın denetimine kalır. Uyarı: bir hata seviyesi (E_WARNING, E_DEPRECATED) bildirilir ve yürütme sürer. Durdurur: bir Throwable fırlatılır ya da yürütme biter; yakalanmazsa o yürütme orada kesilir.
Bu ayrım M08/K03’te ölçülen “garantiyi kim veriyor” sorusuyla karışmaz: orada bir davranışın kimin sözü olduğu soruldu, burada söz değil sinyal ölçülüyor. M10/K05’te ölçülen “güvence nerede bitiyor” sorusuyla da karışmaz: orada derleyicinin denetiminin sınırı aranıyordu, burada derleme ölçülmüyor ve kurs sorunların yürütmedeki biçimini ayırıyor.
Sınıfı bir etiket değil bir kural belirler. Aşağıdaki kanal.php dosyası kuralı koda döker: dinle() önce fırlatma var mı diye sorar, sonra uyarı bildirildi mi, sonra sonuç false ya da null mu; hiçbiri yoksa sorun sessizdir. Uyarılar bir set_error_handler ile yakalanır ve yalnız seviye adı tutulur, çünkü ileti metni sürümle değişebilir ve gerçek bir dosya yolu taşı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]; }
Ayrı yürütmede çalışan iki gözlemde ölçü çıkış kodudur: 255 yürütmenin durduğunu gösterir. İkinci dosya, duyulan.php, on dört gözlemi tek tabloda toplar. Gözlemlerin her biri sonraki derslerden birine aittir; burada yalnız hangi kanaldan duyulduklarına bakılıyor.
<?php // duyulan.php — on dort gozlem: bu sorun hangi kanaldan duyuluyor require 'kanal.php'; function iki_topla(int $a, int $b): int { return $a + $b; } $gozlemler = [ 'durdurulan yurutme' => fn() => ayri_yurut('echo "once\n"; throw new RuntimeException("dur");'), 'sozdizim hatasi' => fn() => ayri_yurut('echo "once\n"; $x = ;'), 'tam sayi tasmasi' => fn() => dinle(fn() => PHP_INT_MAX + 1), 'sayisal onekli dizgi' => fn() => dinle(fn() => '5 elma' + 1), 'karsiliksiz match' => fn() => dinle(fn() => match (3) { 1 => 'bir', 2 => 'iki' }), 'fazla arguman' => fn() => dinle(fn() => iki_topla(1, 2, 3)), 'anahtar normallesmesi' => fn() => dinle(fn() => ['1' => 'a', 1 => 'b', true => 'c']), 'eksik ic anahtar' => fn() => dinle(fn() => [['ad' => 'Ada']][0]['soyad']), 'bulunamayan eleman' => fn() => dinle(fn() => array_search('elma', ['armut', 'ayva'])), 'bozuk json' => fn() => dinle(fn() => json_decode('{bozuk')), 'eksik sorgu degiskeni' => fn() => dinle(fn() => $_GET['sayfa']), 'gecersiz form sayisi' => fn() => dinle(fn() => filter_var('12a', FILTER_VALIDATE_INT)), 'yuklenmemis dosya' => fn() => dinle(fn() => move_uploaded_file('kanal.php', 'hedef.php')), 'eksik dahil dosyasi' => fn() => dinle(fn() => include 'yok.php'), ]; $sayilar = ['sessiz' => 0, 'donus' => 0, 'uyari' => 0, 'durdurur' => 0]; printf("%-24s%-10s%s\n", 'gozlem', 'kanal', 'kanit'); foreach ($gozlemler as $ad => $gozlem) { [$sinif, $kanit] = $gozlem(); $sayilar[$sinif]++; printf("%-24s%-10s%s\n", $ad, $sinif, $kanit); } echo "\n"; foreach ($sayilar as $sinif => $adet) { printf("%-10s%d\n", $sinif, $adet); } echo 'toplam gozlem: ', array_sum($sayilar), "\n";
gozlem kanal kanit durdurulan yurutme durdurur cikti 1 satir, cikis 255 sozdizim hatasi durdurur cikti 0 satir, cikis 255 tam sayi tasmasi sessiz sonuc float 9.2233720368548E+18 sayisal onekli dizgi uyari E_WARNING, sonuc int 6 karsiliksiz match durdurur UnhandledMatchError fazla arguman sessiz sonuc int 3 anahtar normallesmesi sessiz sonuc dizi(1) eksik ic anahtar uyari E_WARNING, sonuc null bulunamayan eleman donus sonuc false bozuk json donus sonuc null eksik sorgu degiskeni uyari E_WARNING, sonuc null gecersiz form sayisi donus sonuc false yuklenmemis dosya donus sonuc false eksik dahil dosyasi uyari E_WARNING, sonuc false sessiz 3 donus 4 uyari 4 durdurur 3 toplam gozlem: 14
On dört gözlemin 3’ü sessiz, 4’ü dönüşle, 4’ü uyarıyla duyuldu, 3’ü yürütmeyi durdurdu. KN1. Bu tablo, sandbox’ta php.ini yüklü olmadan koşan bir yürütmenin sonucudur; ölçüm hiçbir ini değerine dayanmaz ve ayar gerektiğinde -d bayrağıyla verilir. Aynı tablonun satırları başka bir kurulumda aynı kalır, çünkü yazdırılan şey ileti metni değil seviye sabitinin adı, istisnanın sınıf adı ve çıkış kodudur.
Tabloda üç şey dikkat çeker. Sessiz sınıfta sonuç yanlış değil, başka türlüdür: tam sayı sınırı aşılınca tip sinyalsiz float oldu. Dönüş sınıfı sinyali çağırana bırakır: array_search bulamayınca false verdi ve sorulmazsa kimse duymaz. Uyarı sınıfı duyurur ama durdurmaz: eksik dahil dosyası false verdi ve yürütme sürdü. Sıra da önemlidir: require ile çekilen eksik dosya önce bir uyarı bildirir, ardından Error fırlatır; yürütmeyi kesen kanal en güçlü kanal olduğu için bu düzenek durdurur sınıfındadır.
Yürütmenin İstek Başına Başlayıp Bitmesi
Bu dersin ölçtüğü sınıf durdurur ve bir sorudan ibarettir: yürütme nerede bitiyor? Yerleşik sunucu (php -S) her istek için dosyayı baştan çalıştırır. Önceki isteğin değişkenlerinden, static değerlerinden ya da bir önceki isteğin yarım kalmış işinden hiçbir şey taşınmaz. Değerlerin ömrünün nerede bittiği M10/K02’de ayrı bir eksen olarak ölçüldü; burada o soru sorulmuyor, yalnız bir yürütmenin sınırları çiziliyor.
İlk dosya bir küresel değişkeni ve bir fonksiyon içi static değişkeni artırır. İkincisi bir tam sayıya bölme yapar ve bölen sıfırsa yakalanmayan bir hata fırlatır. İki dosya da çıktı bloğu olmadan, yalnız dosya olarak verilir; ardından gelen betik onları sunucuda ölçer.
<?php // sayac.php — kuresel degisken ve static her istekte yeniden baslar $ziyaret = 0; function say(): int { static $sayac = 0; return ++$sayac; } $ziyaret++; echo 'ziyaret=', $ziyaret, ' s=', say(), "\n";
<?php // bolme.php — bolen sifirsa yakalanmayan hata, istek 500 ile biter $n = (int) ($_GET['n'] ?? 0); $kalan = 100 % $n; echo 'kalan=', $kalan, "\n";
Sunucu -d display_errors=0 ile başlar; böylece hata metni yanıt gövdesine karışmaz ve yalnız durum kodu ölçülür. Betik aynı dosyayı üç kez, bölme dosyasını da önce n=0, sonra n=2, sonra yeniden n=0 ile ister. Son satır, aynı bölme dosyasını komut satırında çalıştırır ve çıkış kodunu basar.
# istek.sh — istek basina yurutme: sayac, bolme ve komut satirinda ayni dosya php -S 127.0.0.1:8081 -d display_errors=0 >/dev/null 2>&1 & pid=$! for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8081/sayac.php && break; sleep 0.1; done for i in 1 2 3; do curl -s http://127.0.0.1:8081/sayac.php; done curl -s -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/bolme.php?n=0' curl -s -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/bolme.php?n=2' curl -s -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/bolme.php?n=0' kill $pid php -d display_errors=0 bolme.php; echo "cikis=$?"
ziyaret=1 s=1 ziyaret=1 s=1 ziyaret=1 s=1 durum=500 kalan=0 durum=200 durum=500 cikis=255
KN2. Üç ayrı istekte küresel değişken de static değişken de her seferinde 1 verdi: sunucu kendi sürecini açık tutsa bile her istek dosyayı baştan yürütür ve önceki isteğin durumu bir sonrakine geçmez. Sunucunun hangi süreç modeliyle çalıştığı bu sonucu belirlemez; ölçülen, yerleşik sunucunun davranışıdır. Başka bir kurulumun istek başına yürütmeyi nasıl sağladığı M16/K01’in konusudur.
İlk n=0 isteği 500 verdi, çünkü 100 % 0 bir DivisionByZeroError fırlatır ve ortada yakalayan yoktur; hiçbir çıktı basılmadan fırladığı için sunucu durum kodunu 500 yapar. Hemen ardından gelen n=2 isteği kalan=0 ve 200 verdi. Hata ayrı bir yürütmenin içindeydi ve bir sonrakine hiçbir iz bırakmadı.
Susturma Kanalı Değiştirmez
Uyarıyı duymak istemeyen kod iki yol bilir: @ işleci ve error_reporting(0). İkisi de sorunu ortadan kaldırmaz; yalnız bildirimin nereye gideceğini etkiler. Bir hata işleyicisi kayıtlıysa uyarı ona hâlâ ulaşır. Aşağıdaki dosya üç okumayı yan yana koyar: susturmasız, @ ile ve error_reporting(0) sonrası. Her okuma tanımsız bir dizi anahtarını okur.
<?php // sustur.php — @ ve error_reporting(0) uyariyi kaldirmaz, islemciye ulasmasini degistirmez $bildirim = 0; set_error_handler(function (int $seviye) use (&$bildirim): bool { $bildirim++; return true; }); $dizi = []; $a = $dizi['yok']; echo 'susturmasiz: ', $bildirim, ' bildirim, deger: ', var_export($a, true), "\n"; $b = @$dizi['yok']; echo '@ ile: ', $bildirim, ' bildirim, deger: ', var_export($b, true), "\n"; error_reporting(0); $c = $dizi['yok']; echo 'error_reporting(0): ', $bildirim, ' bildirim, deger: ', var_export($c, true), "\n";
susturmasiz: 1 bildirim, deger: NULL @ ile: 2 bildirim, deger: NULL error_reporting(0): 3 bildirim, deger: NULL
KN3. Üç okuma da aynı değeri, NULL’u, verdi: sorun üç durumda da aynıydı, yalnız bildirimin görünürlüğü değişti. Sayaç hata işleyicisine ulaşan bildirimlerin sayısıdır. Susturma, uyarı sınıfındaki bir sorunu sessiz sınıfa taşımaz; yalnız ekrana ya da günlüğe yazılmasını engeller. Sorunu duymayan kodun elinde yine NULL vardır ve bu değerin yanlış anlamına gelip gelmediğini kod bilmez.
Çıkış ve Sonlandırma Fonksiyonu
exit yürütmeyi o satırda bitirir ve sonraki satırlar çalışmaz; bu durdurur sınıfındadır. Ama yürütme tamamen susmaz: register_shutdown_function() ile kaydedilen fonksiyon, exit çağrıldıktan sonra da çalışır. Aşağıdaki dosya sırayı ölçer.
<?php // cikis.php — exit yurutmeyi keser, kapanis islevi yine calisir register_shutdown_function(function () { echo "kapanis\n"; }); echo "a\n"; exit(3); echo "b\n";
a kapanis
KN4. Çıktı a ve kapanis oldu; b hiç basılmadı. Yürütme kesildi ama istek kapanmadan önce kayıtlı fonksiyon çalıştı. Bu, yakalanmayan bir hatanın ya da exit’in bir isteği keserken bile temizlik kodunun çalışabildiği bir pencere bıraktığını gösterir. Aynı pencere, yürütmenin neden kesildiğini göremeyen kod için de açılır: sonlandırma fonksiyonu sorunun nedenini bilmeden çalışır.
Sınırlayıcı Ölçüm
Durdurur sınıfının kapsamı bir yürütmedir, programın tamamı değil. KN5. Yerleşik sunucuda bir istek bir yürütmedir: yakalanmayan hata yalnız o isteği keser ve sunucu ayakta kalır, sonraki istek temiz başlar. Komut satırında ise yürütme bütün programdır: aynı bolme.php dosyası orada cikis=255 verdi ve sonrasında çalışacak bir şey kalmadı. Kanal aynı, kapsam farklıdır.
Bu yüzden “durdurur” sözü iki kayıtla okunmalıdır. Birincisi, durdurur yürütme sınırının içinde geçerlidir: sınırın nerede olduğunu çalıştırma biçimi belirler. İkincisi, durum kodu hatanın çıktıya göre nerede fırladığına bağlıdır; bu derste hata hiçbir şey basılmadan önce fırladı ve 500 geldi. Sözdizim ve Etiketler dersi bu sıranın ne değiştirdiğini ölçer.
Bir yürütmenin içinde de sınır çizilebilir: try ve catch ile yakalanan bir Throwable durdurur olmaktan çıkar ve yakalayanın denetimine geçer. Çekirdekteki dinle() bunu yapar; tablodaki üç durdurur satırından ikisinin kanıtı yine de çıkış kodudur, çünkü o iki gözlem ayrı bir yürütmede koşar.
Özet
- PHP’de bir sorun sessiz, dönüş, uyarı ya da durdurur kanallarından biriyle duyulur; sınıfı etiket değil
dinle()’nin sorduğu sıra belirler. - On dört gözlemin 3’ü sessiz, 4’ü dönüşle, 4’ü uyarıyla duyuldu, 3’ü yürütmeyi durdurdu.
- Bu dersin ölçü sınıfı durdurur: yakalanmayan bir hata yürütmeyi orada keser.
- Yerleşik sunucuda her istek dosyayı baştan yürütür; küresel değişken ve
staticher istekte yeniden başlar. - Durdurur yalnız o yürütmeyi keser: sunucuda sonraki istek temiz başlar, komut satırında yürütme bütün programdır.
Sonraki Adım
Bir yürütmenin hata ile kesilebildiği ölçüldü, ama kesilmenin iki ayrı biçimi tabloda yan yana duruyor: durdurulan yurutme bir satır basıp 255 ile çıktı, sozdizim hatasi hiçbir satır basmadan 255 ile çıktı. Sözdizim ve Etiketler dersi bu farkı ölçüyor: bir dosya ne zaman hiç çalışmaya başlamıyor?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.