Ders 03 / 14
Değişkenler ve Veri Tipleri
PHP'de bir değerin tipinin sinyal vermeden değişebilmesi: atama, tam sayı sınırı, kayan noktalı sayılar, açık dönüşümler, tanımsız değişken ve tip bildirimlerinin katı tip kipine göre davranışı.
Yayın Güncelleme
Bu derste
- Bir değişkenin tipinin atanan değere göre sinyalsiz değiştiğini ölçerek gösterir
- Tam sayı sınırının aşılmasının ve bölmenin tipi nasıl değiştirdiğini sayarak doğrular
- Tanımsız bir değişkene erişimin sıfır değer değil, uyarılı bir boş değer olduğunu ayırt eder
- Tip bildirimlerinin çağıranın dosyasındaki katı tip kipine göre davrandığını iki dosyalı bir kurguyla gösterir
İçindekiler
Sözdizim ve Etiketler dersi, bir dosyanın ya hiç çalışmadığını ya da çalışırken kesildiğini ölçtü; ikisinde de bir sinyal vardı: çıkış kodu, durum kodu, bir istisna. Bu ders sinyalin hiç gelmediği yere iniyor: değer çalışıyor, yürütme sürüyor, ama değerin tipi değişmiş olabilir. Soru, tipin sinyal vermeden değişip değişmediği.
PHP’de bir değişken $ ile başlayan bir adtır ve tip bildirimi gerektirmez. Programlama Temelleri kursunda (M01/K02) değişken ve tip kavramı kurulmuştu; burada yalnız PHP’nin bu kavramlara nasıl davrandığı ölçülüyor. Bu düzene gevşek tipleme (loose typing) denir. Tipi okumak için get_debug_type() kullanılır: int, float, string, bool, array, null gibi adlar verir. Her ölçümde aynı yöntem izlenir: dosya bir hata işleyicisi (error handler) kurar, işleyici her bildirimde bir sayacı artırır ve dosya sonunda sayaç basılır. Sayaç sıfırsa dil hiçbir şey söylememiştir.
Tip Değere Bağlıdır
PHP’de tip, değişkenin değil o anda taşıdığı değerin özelliğidir. Aynı $x adı peş peşe bir tam sayı, bir dizgi (string), bir dizi ve bir boş değer taşıyabilir ve dil bunun için bir şey söylemez. Aşağıdaki dosya dört atama yapar ve her atamadan sonra tipi okur.
<?php // atama.php — ayni ad art arda dort farkli tip tasir, sinyal gelmez $bildirim = 0; set_error_handler(function (int $seviye) use (&$bildirim): bool { $bildirim++; return true; }); $x = 5; echo get_debug_type($x), "\n"; $x = 'bes'; echo get_debug_type($x), "\n"; $x = [1, 2]; echo get_debug_type($x), "\n"; $x = null; echo get_debug_type($x), "\n"; echo 'bildirim: ', $bildirim, "\n";
int string array null bildirim: 0
KN16. Dört atama dört ayrı tip üretti ve bildirim sayacı sıfır kaldı. Bu, gevşek tiplemenin en temel sonucudur: bir değişkene bakarak değerinin tipini söylemek için o satıra kadar olan atamaların hepsini izlemek gerekir. Ölçülen, serbestliğin kendisi değil, hiçbir sinyalle işaretlenmemesidir. Bu dersin ölçü sınıfı sessizdir.
Tam Sayı Sınırı
Bir int değerinin bir üst sınırı vardır ve PHP_INT_MAX sabiti onu verir. Sabitin değeri yapıya bağlıdır ve bu derste basılmaz; ölçülen, sınırın aşılınca ne olduğudur. Aşağıdaki dosya sınıra bir ekler ve sonuçtan önce ve sonra tipi okur.
<?php // tasma.php — tam sayi sinirini asmak tipi sessizce degistirir $uyarilar = 0; set_error_handler(function (int $seviye) use (&$uyarilar): bool { $uyarilar++; return true; }); $sinir = PHP_INT_MAX; $sonraki = $sinir + 1; echo get_debug_type($sinir), ' -> ', get_debug_type($sonraki), "\n"; echo 'bildirilen uyari: ', $uyarilar, "\n";
int -> float bildirilen uyari: 0
KN17. Sınırın bir fazlası hata fırlatmadı, uyarı bildirmedi ve kırpılmadı: değer float oldu ve işlem sürdü. Çıktıdaki bildirilen uyari: 0 satırı sinyalin gelmediğini gösterir. Sonuç yanlış değil, başka türlüdür: float olarak saklanan değer artık bütün tam sayıları ayırt edemez. Çekirdek tablosundaki tam sayi tasmasi satırı bu sonucu sessiz sınıfına koymuştu; ölçüm onu doğruladı.
Kayan Noktalı Sayılar ve Bölme
Tipin değişmesi yalnız sınırda olmaz. Bölme işleci / iki tam sayıyı böldüğünde sonucun tipi, bölümün tam çıkıp çıkmamasına bağlıdır. Kayan noktalı sayılar ise ondalık kesirlerin çoğunu tam olarak saklayamaz. Aşağıdaki dosya üç şeyi ölçer: bölmenin sonuç tipini, 0.1 + 0.2 toplamının 0.3 ile eşitliğini ve tam sayı bölmesi intdiv()’in tipini.
<?php // kesir.php — bolme tipi degistirir, kayan nokta tam saklamaz $bolumler = [ '6 / 3' => 6 / 3, '7 / 2' => 7 / 2, 'intdiv(7, 2)' => intdiv(7, 2), ]; foreach ($bolumler as $ifade => $sonuc) { printf("%-14s%-7s%s\n", $ifade, get_debug_type($sonuc), var_export($sonuc, true)); } $toplam = 0.1 + 0.2; echo '0.1 + 0.2 === 0.3: ', var_export($toplam === 0.3, true), "\n"; echo 'fark 1e-9 icinde: ', var_export(abs($toplam - 0.3) < 1e-9, true), "\n";
6 / 3 int 2 7 / 2 float 3.5 intdiv(7, 2) int 3 0.1 + 0.2 === 0.3: false fark 1e-9 icinde: true
KN18. 6 / 3 bir int verdi, 7 / 2 bir float: aynı işlecin sonuç tipi operandların değerine göre değişti. 0.1 + 0.2 toplamı 0.3’e katı olarak eşit çıkmadı, ama aradaki fark çok küçük. Bu ikisi de sessizdir; kayan noktalı karşılaştırmada eşitlik yerine bir eşik kullanmak programcının işidir. Tam bölme isteniyorsa tipi her zaman int olan intdiv() vardır.
Doğruluk Değeri Dönüşümü
Bir değer bir koşulda ya da (bool) ile bir doğruluk değerine (boolean) dönüştürüldüğünde hangi değerlerin yanlış sayıldığı, tip konusunun bir başka yüzüdür. Aşağıdaki dosya on bir değeri dönüştürür.
<?php // dogruluk.php — hangi degerler yanlis sayilir $degerler = [ '0' => 0, '0.0' => 0.0, "''" => '', "'0'" => '0', "'0.0'" => '0.0', "' '" => ' ', "'false'" => 'false', '[]' => [], '[0]' => [0], 'null' => null, '-1' => -1, ]; foreach ($degerler as $ad => $deger) { printf("%-9s%s\n", $ad, var_export((bool) $deger, true)); }
0 false 0.0 false '' false '0' false '0.0' true ' ' true 'false' true [] false [0] true null false -1 true
KN19. Sıfır, boş dizgi, '0', boş dizi ve null yanlış sayıldı; '0.0', bir boşluk, 'false' dizgisi, içinde bir eleman taşıyan dizi ve -1 doğru sayıldı. Yanlış sayılan '0' ile doğru sayılan '0.0' arasındaki fark, dönüşümün değerin anlamına değil biçimine baktığını gösterir. Hiçbiri bildirim üretmez.
Sayı Yazımları
Aynı tam sayı kaynak kodda birden çok biçimde yazılabilir: onaltılık, ikilik, sekizlik ve okunurluk için alt çizgili. Hepsi aynı tipi, yani int üretir. Aşağıdaki dosya beş yazımı yan yana koyar ve tipleriyle değerlerini basar; ölçülen, yazımın tipi etkileyip etkilemediğidir.
<?php // yazim.php — sayi yazimlari ayni tipi uretir $yazimlar = [ '0x1F' => 0x1F, '0b101' => 0b101, '0o17' => 0o17, '017' => 017, '1_000_000' => 1_000_000, '1e3' => 1e3, ]; foreach ($yazimlar as $yazim => $deger) { printf("%-10s%-6s%s\n", $yazim, get_debug_type($deger), var_export($deger, true)); }
0x1F int 31 0b101 int 5 0o17 int 15 017 int 15 1_000_000 int 1000000 1e3 float 1000.0
KN20. Beş yazım int verdi; yalnız 1e3 bir float oldu, çünkü üstel gösterim her zaman kayan noktalı bir sayı üretir ve değeri tam sayıya eşit olsa bile tipi değişmez. Başında 0 olan 017 sekizlik okundu, yani 15 oldu; baştaki sıfırı süs sanan biri sessizce yanlış bir değer üretir. Bu yazım sınıfı da sessizdir.
Boş Değer Sınamaları
Bir değişkenin değeri yokken bunu anlamanın üç yolu vardır ve üçü farklı sonuç verir: isset(), empty() ve ??. Farkları ölçmek için aynı beş değer üçüne de verilir. Beşinci değişken hiç tanımlanmamıştır; sınamaların hiçbiri bu yüzden uyarı üretmez, bu da kendi başına ölçülecek bir sonuçtur.
<?php // bos.php — isset, empty ve ?? farkli sorulara cevap verir $bildirim = 0; set_error_handler(function (int $seviye) use (&$bildirim): bool { $bildirim++; return true; }); $sifir = 0; $bos = ''; $yok = null; $dolu = 'a'; printf("%-7s%-7s%-7s%s\n", 'ad', 'isset', 'empty', '??'); foreach (['sifir', 'bos', 'yok', 'dolu', 'hic'] as $ad) { $deger = $$ad ?? null; printf( "%-7s%-7s%-7s%s\n", $ad, var_export(isset($$ad), true), var_export(empty($$ad), true), var_export($deger, true) ); } echo 'bildirim: ', $bildirim, "\n";
ad isset empty ?? sifir true true 0 bos true true '' yok false true NULL dolu true false 'a' hic false true NULL bildirim: 0
KN21. isset() yalnız “tanımlı ve null değil” sorusuna cevap verir: 0 ve boş dizgi için true, null ve tanımsız için false. empty() ters soruyu sorar ve 0 ile boş dizgiyi de boş sayar. ?? ise sol tarafın değerini ya da null’ı taşır. Tanımsız $hic üç sınamada da bildirim üretmedi; uyarıyı yalnız doğrudan okuma üretir. Sınamayı yanlış seçmek, 0’ı “yok” saymak gibi mantık hatalarına yol açar ve bu hatalar kimseye bildirilmez.
Açık Dönüşümler
Tip, işlemin sonucunda değiştiği gibi programcının isteğiyle de değiştirilebilir. Parantez içinde bir tip adı yazmak, değeri o tipe dönüştürür; buna açık dönüşüm (cast) denir. Aşağıdaki dosya altı dönüşümü çalıştırır ve sonuçlarını tipleriyle birlikte basar. Girdiler kısmen sayı olan bir dizgiyi, hiç sayı olmayan bir dizgiyi ve kesirli bir sayıyı içerir.
<?php // donusum.php — acik donusumler sonucu degistirir ve sinyal vermez $donusumler = [ '(int) "12abc"' => (int) '12abc', '(int) "abc"' => (int) 'abc', '(int) 3.99' => (int) 3.99, '(bool) "0"' => (bool) '0', '(string) false' => (string) false, '(float) "1e3"' => (float) '1e3', ]; foreach ($donusumler as $ifade => $sonuc) { printf("%-18s%-8s%s\n", $ifade, get_debug_type($sonuc), var_export($sonuc, true)); }
(int) "12abc" int 12 (int) "abc" int 0 (int) 3.99 int 3 (bool) "0" bool false (string) false string '' (float) "1e3" float 1000.0
KN22. Dizgideki sayı öneki alındı, kalanı atıldı ve bunun için hiçbir şey söylenmedi. "12abc" 12 oldu, "abc" 0 oldu, 3.99 kesilerek 3 oldu. Çıktının hiçbir satırında bir uyarı yok; açık dönüşüm, “ne istediğini biliyorum” demenin bir biçimidir ve dil buna sinyal eklemez. Programcı dönüşümü yazdı, ama sonucun kayıpsız olup olmadığını dil söylemez.
Tanımsız Değişken
Bir değişkene değer atanmadan erişmek ayrı bir durumdur. M09/K01 “yazılmayanın karşılığı ne” sorusunu ölçmüştü: orada atanmamış bir değerin yerine sıfır değer geçiyordu. PHP’de tanımsız bir değişken sıfır değer vermez: boş değer null verir ve bir E_WARNING bildirir. Aşağıdaki dosya hata işleyicisinde yalnız seviye adını basar; ileti metni sürüme bağlı olduğu için yazdırılmaz.
<?php // tanimsiz.php — tanimsiz degisken null verir ve E_WARNING bildirir set_error_handler(function (int $seviye): bool { echo 'bildirilen: ', $seviye === E_WARNING ? 'E_WARNING' : 'baska', "\n"; return true; }); $sonuc = $yok; echo 'deger: ', var_export($sonuc, true), "\n"; echo 'tip: ', get_debug_type($sonuc), "\n";
bildirilen: E_WARNING deger: NULL tip: null
KN23. Değer null, tip null ve işlem sürdü; ama bu kez bir uyarı geldi. Tanımsız değişkenin sınıfı uyarıdır: duyuluyor, durdurmuyor. Bir değişkenin 0 ya da boş dizgi yerine null taşıması, sonraki işlemlerde başka bir sürprize açık bir yol bırakır; bu, İşleçler dersinin konusu olan dönüşüm kurallarına varır.
Tip Bildirimleri
Gevşek tipleme, tipin bir parametreye yazılmasıyla sınırlanabilir. int $n yazmak, parametrenin bir tam sayı olmasını ister. Dil bu isteği iki yoldan karşılar: değer uygunsa kabul eder, uymayana göre ya zorlar (coerce) ya da reddeder. Aşağıdaki dosya üç girdiyi sınar: tam sayı gibi görünen bir dizgi, kesirli bir sayı ve sayı olmayan bir sonek taşıyan bir dizgi.
<?php // tip.php — int parametre: '5' zorlanir, 1.5 E_DEPRECATED, '5x' TypeError set_error_handler(function (int $seviye): bool { echo 'bildirilen: ', $seviye === E_DEPRECATED ? 'E_DEPRECATED' : 'baska', "\n"; return true; }); function iki_kati(int $n): int { return $n * 2; } foreach (['5', 1.5, '5x'] as $girdi) { try { $sonuc = iki_kati($girdi); echo var_export($girdi, true), ' -> ', $sonuc, "\n"; } catch (TypeError $hata) { echo var_export($girdi, true), ' -> ', get_class($hata), "\n"; } }
'5' -> 10 bildirilen: E_DEPRECATED 1.5 -> 2 '5x' -> TypeError
KN24. Üç girdi üç ayrı kanaldan duyuldu. '5' sessizce 5 oldu ve sonuç 10 çıktı. 1.5 kesirli olduğu için E_DEPRECATED bildirildi ve değer kesilerek çalıştı. '5x' sayısal olmayan bir sonek taşıdığı için TypeError fırlattı. Tip bildirimi bu yüzden bir garanti değil bir kuraldır: kural kapsamında kalan değerleri zorlar, kapsam dışı olanları reddeder, arada kalanlar için uyarır.
Sınırlayıcı Ölçüm
Zorlamayı kapatan düzenek declare(strict_types=1) bildirimidir ve ölçümün asıl sınırı onun kimi bağladığıdır. KN25. Katı tip kipi çağıranın dosyasına uygulanır, çağrılan fonksiyonun dosyasına değil. İki dosyalı bir kurgu bunu gösterir: iki fonksiyon dosyası, biri katı bildirimli biri bildirimsiz, aynı int parametreli fonksiyonu taşır; iki çağıran dosya da ona '5' gönderir.
<?php // islev_kipsiz.php — kipsiz dosyada tanimli int parametreli islev function iki_kati_kipsiz(int $n): int { return $n * 2; }
<?php // islev_kati.php — kati tip kipi bildirilmis dosyada tanimli islev declare(strict_types=1); function iki_kati_kati(int $n): int { return $n * 2; }
<?php // cagiran_kati.php — kati dosyadan kipsiz dosyadaki islevi cagirir declare(strict_types=1); require 'islev_kipsiz.php'; try { echo iki_kati_kipsiz('5'), "\n"; } catch (TypeError $hata) { echo get_class($hata), "\n"; }
TypeError
<?php // cagiran_kipsiz.php — kipsiz dosyadan kati dosyadaki islevi cagirir require 'islev_kati.php'; echo iki_kati_kati('5'), "\n";
10
Katı dosyadan kipsiz bir fonksiyona '5' gönderilince TypeError geldi; kipsiz dosyadan katı bildirimli bir fonksiyona aynı değer gidince 10 çıktı. Kural fonksiyonun bulunduğu dosyada değil, çağrının yapıldığı dosyada işler. Bu yüzden bir fonksiyonun parametresine bakarak çağrının nasıl davranacağı söylenemez: aynı fonksiyon, hangi dosyadan çağrıldığına göre zorlar ya da reddeder.
Özet
- PHP’de tip değere bağlıdır: aynı değişken art arda dört tip taşıyabilir ve bildirim gelmez; bu dersin ölçü sınıfı sessizdir.
- Tam sayı sınırı aşılınca değer
floatolur,/işleci bölümün tam çıkıp çıkmadığına göreintya dafloatverir ve hiçbiri uyarı üretmez. - Açık dönüşümler
"12abc"gibi bir dizgiden sayı önekini alır, kalanı atar ve bunu duyurmaz. - Tanımsız bir değişken sıfır değer değil,
nullverir veE_WARNINGbildirir. - Tip bildirimi
intparametreye'5'için zorlama,1.5içinE_DEPRECATED,'5x'içinTypeErrorverir. declare(strict_types=1)çağıranın dosyasını bağlar: katı dosyadan'5'reddedilir, kipsiz dosyadan katı fonksiyona aynı değer zorlanır.
Sonraki Adım
Tipin atamada, bölmede ve dönüşümde sessizce değiştiği görüldü. İşleçler dersi aynı soruyu işleçlere soruyor: aritmetik ve karşılaştırma işleçleri iki farklı tipi buluşturduğunda ne söylüyor?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.