Ders 11 / 14
Süper Küresel Değişkenler
İstek verisinin PHP'ye süper küresel değişkenlerle gelmesi: eksik anahtarın uyarı kanalından duyulması, değerlerin her zaman dizgi olması ve istemcinin değerin tipini seçebilmesi.
Yayın Güncelleme
Bu derste
- Eksik bir sorgu değişkeninin hangi kanaldan duyulduğunu ölçerek gösterir
- İstek verisinin PHP'ye dizgi ya da dizi olarak geldiğini yerleşik sunucuyla doğrular
- İstemcinin değerin tipini seçebildiğini ve durum kodunun kanal için güvenilir bir ölçü olmadığını açıklar
İçindekiler
Dizgi İşleme dersi, bozuk veri çözülünce json_decode’un null döndürdüğünü ve dönüş kanalının bu null’u geçerli bir null değerinden ayıramadığını ölçtü. Ayrımı yapmak çağıranın ek bir soru sormasına kalmıştı. O derse kadar verinin tamamı programın içindeydi: dizi değişmezleri, dizgi değişmezleri, bir JSON metni. Bu derste veri dışarıdan geliyor ve soru değişmiyor: istek verisi eksik ya da beklenmedik bir biçimde geldiğinde ne duyuluyor?
HTTP isteğinin yapısını M04, bir isteğin sunucu tarafındaki yaşam döngüsünü M16/K01 kurdu; burada yalnız PHP’nin bu veriyi hangi düzenekle verdiği ölçülüyor. Düzenek süper küresel değişken (superglobal) adını taşır: PHP’nin istek başında doldurduğu, her kapsamda tanımlanmadan erişilebilen önceden tanımlı dizilerdir. İstek verisi için dört tanesi gerekir: $_GET sorgu dizgisini, $_POST istek gövdesindeki form alanlarını, $_SERVER yöntem gibi istek bilgilerini, $_FILES yüklenen dosyaları taşır. Sonuncusu üçüncü dersin konusudur.
Eksik Anahtar Uyarı Verir
İlk ölçüm komut satırında yapılır. Orada hiç istek yoktur ve $_GET boştur; olmayan bir anahtarı okumak, bir sorgu dizgisinde olmayan bir değişkeni istemekle aynı davranışı verir. Ölçüm, kursun çekirdeğindeki kanal.php dosyasını kullanır; dosya bu derste de birebir verilir.
<?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 // eksik.php — bos $_GET'ten okumanin hangi kanaldan duyuldugu require 'kanal.php'; $olcumler = [ 'eksik sorgu degiskeni' => fn() => $_GET['sayfa'], 'bosluk birlestirme' => fn() => $_GET['sayfa'] ?? 1, 'filter_input' => fn() => filter_input(INPUT_GET, 'sayfa'), ]; foreach ($olcumler as $ad => $olcum) { [$sinif, $kanit] = dinle($olcum); printf("%-24s%-10s%s\n", $ad, $sinif, $kanit); }
eksik sorgu degiskeni uyari E_WARNING, sonuc null bosluk birlestirme sessiz sonuc int 1 filter_input donus sonuc null
İlk satır çekirdeğin eksik sorgu degiskeni gözlemidir: sonuç null, ama E_WARNING bildirildi ve yürütme sürdü. Bu uyarı sınıfıdır. Çağıranın bir şey sormasına gerek kalmadı; kanal kendiliğinden konuştu. Okuma bir fonksiyon içinde, global bildirimi olmadan yapıldı: süper küresel değişkenin tanımı budur. IY1. Bu ölçümde $_GET boştur çünkü yürütme komut satırındadır; satırın sınıfı, anahtarın dizide bulunmamasının sonucudur ve istek kaynağından bağımsızdır.
İkinci satır karşıt kanaldır. Boşluk birleştirme işleci uyarıyı yok eder ve aynı eksik anahtar sessiz kanaldan duyulur; İşleçler dersindeki ölçümün istek verisindeki karşılığıdır. Eksikliği bir varsayılanla karşılamak bilinçli bir tercihse bu doğru araçtır, ama eksikliği hiçbir kanal bildirmez. Üçüncü satır filter_input’tur: komut satırında istek olmadığı için null döner ve sinyal yoktur; sorun yalnız dönüş değerine bakan çağırana duyulur. Bu dönüş kanalıdır ve sonraki dersin fonksiyonları aynı kanalı kullanır.
Sunucuda Gelen Veri
Gerçek bir istek için PHP’nin dağıtımıyla gelen yerleşik sunucu (built-in server) kullanılır: php -S komutu belirtilen adreste bir sunucu başlatır ve her istek için ilgili betiği baştan yürütür. Ölçüm iki parçadır: istek verisini basan bir betik ve sunucuyu başlatıp istekleri gönderen, sonra kapatan bir kabuk betiği. IY2. Her php -S çağrısı display_errors kapalı olarak başlatılır; ayar kapalıyken yakalanmayan hata durum kodu olarak 500 verir, ders çıktısına hata metni düşmez.
<?php // istek.php — gelen verinin tipini ve istek yontemini basar foreach ($_GET as $ad => $deger) { $uzunluk = is_array($deger) ? count($deger) : strlen($deger); echo 'get ', $ad, '=', get_debug_type($deger), ' ', $uzunluk, "\n"; } foreach ($_POST as $ad => $deger) { echo 'post ', $ad, '=', get_debug_type($deger), ' ', strlen($deger), "\n"; } echo 'yontem=', $_SERVER['REQUEST_METHOD'], "\n";
# sunucu.sh — yerlesik sunucuyu baslatir, istekleri gonderir, kapatir 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/istek.php && break; sleep 0.1; done echo '--- GET ?n=5&b=' curl -sg -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/istek.php?n=5&b=' echo '--- GET ?a[]=1&a[]=2' curl -sg -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/istek.php?a[]=1&a[]=2' echo '--- POST govde x=1, sorgu n=5' curl -sg -d 'x=1' -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/istek.php?n=5' kill $pid
--- GET ?n=5&b= get n=string 1 get b=string 0 yontem=GET durum=200 --- GET ?a[]=1&a[]=2 get a=array 2 yontem=GET durum=200 --- POST govde x=1, sorgu n=5 get n=string 1 post x=string 1 yontem=POST durum=200
Üç sonuç ders boyunca geçerlidir. Birincisi, her değer dizgidir: ?n=5 ile gelen 5 bir int değil, uzunluğu 1 olan bir string’dir ve ?b= ile gelen boş değer null ya da false değil, uzunluğu 0 olan bir string’dir. İstek metin olarak taşınır ve PHP tip kararı vermez; bir sayı beklendiğinde dizgiyi dönüştürmek kodun işidir. İkincisi, adın sonuna eklenen köşeli parantez (a[]) değeri diziye çevirir: aynı ad iki kez gelince array olur ve eleman sayısı 2 görünür. Üçüncüsü, POST isteğinde sorgu dizgisi yine $_GET’e düşer, gövdedeki alan $_POST’a gider ve REQUEST_METHOD yöntemi POST olarak bildirir; iki kaynağın karışmaması için hangisinden okunduğu kodda açıkça yazılır.
IY3. Basılan şey değerlerin kendisi değil tipi ve uzunluğudur; bu, çıktıyı ortamdan bağımsız tutar. Durum kodu 200’dür çünkü istek.php hata üretmedi.
Başlıklar ve Ham Sorgu Dizgisi $_SERVER İçindedir
Sorgu dizgisinin ayrıştırılmış hâli $_GET’tedir; ham hâli ve istek başlıkları ise $_SERVER dizisindedir. QUERY_STRING anahtarı ayrıştırılmamış metni, HTTP_ önekiyle başlayan anahtarlar ise büyük harfe çevrilmiş ve kısa çizgileri alt çizgiye dönüştürülmüş başlıkları taşır. Başlığı istemci seçer; bu yüzden olmayabilir ve eksik başlığın okunması da eksik sorgu değişkeniyle aynı kanalı kullanır.
<?php // baslik.php — ham sorgu dizgisi ve ozel bir istek basligi echo 'yontem=', $_SERVER['REQUEST_METHOD'], "\n"; echo 'sorgu=', $_SERVER['QUERY_STRING'], "\n"; echo 'baslik=', $_SERVER['HTTP_X_ISTEMCI'] ?? 'yok', "\n"; echo 'ayrisan=', count($_GET), "\n";
# baslik.sh — baslikli ve basliksiz iki istek gonderir 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/baslik.php && break; sleep 0.1; done echo '--- baslikli' curl -s -H 'X-Istemci: deneme' 'http://127.0.0.1:8081/baslik.php?a=1&b=2&a=3' echo '--- basliksiz' curl -s 'http://127.0.0.1:8081/baslik.php' kill $pid
--- baslikli yontem=GET sorgu=a=1&b=2&a=3 baslik=deneme ayrisan=2 --- basliksiz yontem=GET sorgu= baslik=yok ayrisan=0
Ham dizgide a iki kez vardır, ama $_GET yalnız iki anahtar sayar: yinelenen ad, son değerle tek anahtara iner. Başlık, istemci gönderdiğinde HTTP_X_ISTEMCI anahtarında görünür; göndermediğinde anahtar yoktur ve boşluk birleştirme işleci varsayılanı verir. IY4. Başlık değeri istemcinin elindedir, yani güvenilir bir kaynak değildir; bu dersin ölçüsü başlığın gelip gelmediğinin hangi kanaldan duyulduğudur ve burada kullanılan boşluk birleştirme işleci onu sessiz kanala taşır.
Boş Değer Üç Sınamada Üç Sonuç Verir
İstek verisi dizgi geldiği için “değer var mı” sorusu üç ayrı işlemle sorulabilir: isset, empty ve boşluk birleştirme işleci. Üçü de eksik anahtar için uyarı vermez; yani üçü de sessiz kanaldır. Ayrım, sorunun ne olduğunda çıkar: ?n=0 ile gelen 0 ve ?b= ile gelen boş dizgi, anahtarın var olduğu ama değerin boş göründüğü iki durumdur.
<?php // bos.php — var olan ama bos gorunen degerlerin uc sinamadaki sonucu foreach (['n', 'b', 'yok'] as $ad) { $mevcut = isset($_GET[$ad]) ? 'evet' : 'hayir'; $bos = empty($_GET[$ad]) ? 'evet' : 'hayir'; $varsayilan = $_GET[$ad] ?? 'varsayilan'; printf("%-5s isset=%-6s empty=%-6s birlestirme=[%s]\n", $ad, $mevcut, $bos, $varsayilan); }
# bos.sh — bos.php'yi sifir, bos dizgi ve eksik anahtarla cagirir 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/bos.php && break; sleep 0.1; done curl -s -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/bos.php?n=0&b=' kill $pid
n isset=evet empty=evet birlestirme=[0] b isset=evet empty=evet birlestirme=[] yok isset=hayir empty=evet birlestirme=[varsayilan] durum=200
isset iki var olan anahtar için de evet der, eksik anahtar için hayir. empty ise 0 dizgisini de, boş dizgiyi de boş sayar; n değeri geçerli bir sayı olsa bile empty onu “değer yok” diye sınıflar. Boşluk birleştirme işleci boş dizgiyi korur ve yalnız eksik anahtarda varsayılana düşer. IY5. Üç sınama aynı girdide üç farklı karar verir ve hiçbiri bir sinyal üretmez: yanlış sınamayı seçen kod, geçerli 0 değerini sessizce eksik sayar. Bu, dönüş kanalındaki değilleme işleci sorununun (Form İşleme dersinde ölçülür) istek verisindeki karşılığıdır.
Birleşik Dizi İni Değerine Bağlıdır
$_REQUEST dizisi $_GET ve $_POST içeriğini, ayara göre $_COOKIE’yi de tek dizide toplar. Aynı ad iki kaynakta da varsa hangisinin kazandığını request_order ini değeri belirler. Ders hiçbir ini değerine güvenmediği için ayar -d ile verilir ve iki sıra da ölçülür.
<?php // birlesik.php — ayni ad hem sorgu dizgisinde hem govdede gelirse $_REQUEST ne verir echo 'get=', $_GET['x'], ' post=', $_POST['x'], ' request=', $_REQUEST['x'], "\n";
# birlesik.sh — request_order degerinin iki farkli sirasini dener for sira in GP PG; do php -S 127.0.0.1:8081 -d display_errors=0 -d request_order=$sira >/dev/null 2>&1 & pid=$! for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8081/birlesik.php && break; sleep 0.1; done echo "--- request_order=$sira" curl -s -d 'x=govde' 'http://127.0.0.1:8081/birlesik.php?x=sorgu' kill $pid wait $pid 2>/dev/null done true
--- request_order=GP get=sorgu post=govde request=govde --- request_order=PG get=sorgu post=govde request=sorgu
$_GET ve $_POST her iki sırada da aynı değerleri verir, ama $_REQUEST değişir: sıra GP iken gövdedeki değer, PG iken sorgu dizgisindeki değer kazanır. IY6. Aynı istek, ayarın değerine göre farklı bir veri okur ve hiçbir kanal bunu duyurmaz. Bu yüzden kod, verinin hangi kaynaktan geldiğini önemsiyorsa $_GET ya da $_POST içinden birini açıkça okur.
Sınırlayıcı Ölçüm
Sunucuda Gelen Veri bölümündeki ikinci bulgu bir sorun doğurur: istemci her ada dizgi yerine dizi gönderebilir. Betik bir dizgi bekliyorsa ve istemci ?u[]=x ile bir dizi gönderirse, strlen bu dizi argümanına TypeError fırlatır. Bu yakalanmayan bir hatadır ve durdurur kanalındandır. Yani kodu yazan eksik anahtarın uyarı kanalından duyulacağını varsaymışken, istemci kanalı seçmiştir. Kodun kanalı hiçbir zaman yalnız koda bağlı değildir, çünkü girdiyi istemci belirler.
Aynı hatanın nasıl görüneceği, hatanın çıktıdan önce mi sonra mı geldiğine bağlıdır. İki betik aynı strlen çağrısını yapar; biri çağrıyı çıktıdan önce, öteki bir echo’dan sonra yapar.
<?php // erken.php — hata cikti baslamadan once gelir $u = strlen($_GET['u']); echo "uzunluk=$u\n";
<?php // gec.php — ayni hata bir echo'dan sonra gelir echo "basladi\n"; $u = strlen($_GET['u']); echo "uzunluk=$u\n";
# tip.sh — ayni dizi girdisinin iki betikte verdigi durum kodu 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/erken.php && break; sleep 0.1; done echo '--- erken.php dizgi' curl -sg -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/erken.php?u=abc' echo '--- erken.php dizi' curl -sg -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/erken.php?u[]=x' echo '--- gec.php dizi' curl -sg -w 'durum=%{http_code}\n' 'http://127.0.0.1:8081/gec.php?u[]=x' kill $pid
--- erken.php dizgi uzunluk=3 durum=200 --- erken.php dizi durum=500 --- gec.php dizi basladi durum=200
Dizgi gönderilince her şey çalışır. Dizi gönderilince erken.php hiçbir şey basmadan 500 verir; gec.php ise basladi satırını basmış ve başlıkları göndermiş olduğu için 200 ile kısmi gövde döndürür. Aynı istek, aynı hata sınıfı, iki farklı durum kodu. IY7. Hata, yanıt gövdesinin başlamasından önce geliyorsa sunucu başlıkları henüz göndermemiştir ve 500 üretebilir; başlıklar gittikten sonra durum kodu değişemez. M04’te kurulan durum kodları bir isteğin sonucunu bildirir, ama burada kodun söylediği şey hatanın yürütmenin neresinde doğduğudur, hatanın varlığı değil. Bu yüzden durum kodu kanalın güvenilir ölçüsü değildir: 200 gören bir istemci, sunucuda yakalanmayan bir hata çıktığını bilemez.
Özet
- Süper küresel değişkenler (
$_GET,$_POST,$_SERVER,$_FILES) her kapsamda tanımlanmadan erişilir ve PHP’nin istek verisini verdiği düzenektir. - Bu dersin ölçü sınıfı uyarıdır: boş
$_GET’ten olmayan bir anahtarı okumaknullverir veE_WARNINGbildirir; yürütme sürer. - Boşluk birleştirme işleci aynı eksikliği sessiz kanala taşır;
filter_inputkomut satırındanulldöndürerek dönüş kanalından duyurur. - İstek verisi
stringgelir,a[]biçimindeki adarrayyapar; POST isteğinde sorgu dizgisi$_GET’e, gövde$_POST’a düşer. - İstemci tipi seçebildiği için kanal da istemcinin elindedir: dizi gönderilince
strlenTypeErrorfırlatır ve durum kodu hatanın çıktıdan önce ya da sonra gelmesine göre 500 veya 200 olur.
Sonraki Adım
Bu derste anahtar ya eksikti ya da istemcinin seçtiği tipte gelmişti, ama değerin içeriği hiç sorgulanmadı. Dizgi olarak gelen bir değerin sayı olup olmadığına kim karar veriyor? Form İşleme dersi, 12a gibi geçersiz bir değerin filter_var ile nasıl süzüldüğünü ve fonksiyonun başarısızlığı hangi dönüş değeriyle bildirdiğini ölçüyor; geçerli bir 0’ın aynı değerle karışıp karışmadığı da o dersin sorusudur.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.