---
title: 'PHP Temelleri'
source: 'https://academia.sh/tr/kurslar/php-temelleri'
language: tr
updated: '2026-10-21T05:03:33+00:00'
license: 'CC BY-SA 4.0'
lessons: 14
outcomes:
    - 'İstek başına yürütme modelini açıklar ve bir isteğin durumunun sonrakine taşınmadığını ölçer'
    - 'Bir sorunun sessiz, dönüş, uyarı ya da durdurur kanalından hangisiyle duyulduğunu ayırt eder'
    - 'Tipler, işleçler, denetim akışı ve fonksiyonlarla program yazar'
    - 'Dizi ve dizgi işlemleriyle veri dönüştürür'
    - 'Form verisini alır, doğrular ve yanıt üretir'
---

# PHP Temelleri

PHP'de bir sorun her zaman aynı yoldan haber vermez: kimi işlem sessizce bir değer üretip devam eder, kimi false ya da null döndürür, kimi bir uyarı bırakıp sürer, kimi yürütmeyi durdurur. Kurs her düzeneği tek bir soruyla ölçer — bu sorun hangi kanaldan duyuluyor? Yürütme modeli, sözdizim, tipler, işleçler, fonksiyonlar, diziler, dizgiler ve istek verisi aynı çerçeveden geçirilir.

## Dil ve Yürütme

Her isteğin baştan kurulan bir süreçte koşması, kod ile metnin etiketlerle ayrılması, değişkenlerin ve tiplerin dönüşüm kuralları, işleçlerin tipe göre davranması, denetim akışı biçimleri ve fonksiyon imzalarının neyi zorladığı.

### PHP'nin Yürütme Modeli

Source: https://academia.sh/tr/kurslar/php-temelleri/phpnin-yurutme-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.

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
<?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
<?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
<?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
<?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.

```bash
# 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
<?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
<?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 `static` her 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?

### Sözdizim ve Etiketler

Source: https://academia.sh/tr/kurslar/php-temelleri/sozdizim-ve-etiketler

Sözdizim hatalı bir dosyanın hiç çalışmaya başlamaması, yürütme sırasında fırlayan hatadan farkı ve PHP dosyasında kod ile şablonun etiketlerle nasıl karıştığı.

Yürütme Modeli dersi çekirdeğin tablosunda iki satırı yan yana bıraktı: `durdurulan yurutme` bir satır basıp 255 ile çıktı, `sozdizim hatasi` hiçbir satır basmadan 255 ile çıktı. İkisi de durdurur sınıfındadır, ama aynı anda söylenen iki ayrı şey vardır: biri çalışmakta olan bir yürütmenin kesilmesi, öteki yürütmenin hiç başlayamaması. Bu ders farkı ölçüyor.

Fark, PHP'nin bir dosyayı ele alış biçiminden gelir. Dosya çalıştırılmadan önce bütünüyle okunur ve dil kurallarına göre ayrıştırılır (parse). Ayrıştırma başarısız olursa dosyanın tek bir satırı bile çalışmaz. Dosyanın nerede başlayıp nerede bittiğini ise etiketler belirler: PHP yalnız açılış ve kapanış etiketlerinin arasındaki bölümü kod sayar, dışarıda kalan her şeyi olduğu gibi çıktıya yazar. Bu derste iki konu birlikte ölçülüyor, çünkü ikisi de bir dosyanın ne zaman çalışmaya başladığıyla ilgili.

#### Çalışmaya Başlamayan Dosya

İki küçük dosya aynı iki satırı taşır: önce bir satır basmak, sonra bir sorun. Birincisinde sorun yürütmede çıkar: bir `RuntimeException` fırlatılır ve yakalanmaz. İkincisinde sorun dilin kendisine aykırıdır: `$x = ;` ifadesi bir değer beklerken noktalı virgülle karşılaşır. Üçüncü dosya sağlamdır ve karşılaştırma için kullanılır. Üçü de çıktı bloğu olmadan, yalnız dosya olarak verilir.

```php
<?php
// fatal.php — once bir satir basar, sonra yakalanmayan istisna firlatir
echo "once\n";
throw new RuntimeException('dur');
```

```php
<?php
// bozuk.php — sozdizim hatasi: $x = ; ifadesinde deger yok
echo "once\n";
$x = ;
```

```php
<?php
// saglam.php — sozdizimi dogru, yalnizca bir satir basar
echo "once\n";
```

Aşağıdaki betik her dosyayı ayrı bir yürütmede çalıştırır. `display_errors=0` hata metnini çıktıdan uzak tutar; geriye yalnız dosyanın bastığı satırlar ve çıkış kodu kalır. `php -l` dosyayı çalıştırmadan yalnız ayrıştırır ve çıkış kodunu verir.

```bash
# ayri.sh — ayni iki satir, iki ayri durus: yurutmede firlayan ve ayristirmada kalan
php -d display_errors=0 fatal.php; echo "fatal cikis=$?"
php -d display_errors=0 bozuk.php; echo "bozuk cikis=$?"
php -l saglam.php >/dev/null 2>&1; echo "saglam -l=$?"
php -l bozuk.php >/dev/null 2>&1; echo "bozuk -l=$?"
```

```
once
fatal cikis=255
bozuk cikis=255
saglam -l=0
bozuk -l=255
```

**KN6.** İki dosya da 255 ile çıktı, ama `fatal.php` ilk satırını basabildi, `bozuk.php` basamadı. Ölçülen şey çıktının varlığıdır, hata iletisinin metni değil: ileti sürümle değişir ve dosya yolu taşır. `php -l` sağlam dosyada 0, bozuk dosyada 255 verdi; yani ayrıştırma sonucu, dosya hiç çalıştırılmadan öğrenilebilir.

Aynı fark yerleşik sunucuda durum koduna dönüşür. `bozuk.php` istendiğinde dosya çalışmaya başlayamadığı için sunucunun elinde bir yanıt gövdesi yoktur ve durum kodu `500` olur. `fatal.php` ise önce `once` satırını basar; gövde başlamıştır, sonra gelen hata artık durum kodunu değiştiremez. Sunucu `-d display_errors=0` ile başlar ve gövdede hata metni görünmez.

```bash
# sunucu.sh — sozdizim hatali dosya 500, yurutmede firlatan dosya kismi govdeyle 200
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/saglam.php && break; sleep 0.1; done
curl -s -w 'durum=%{http_code}\n' http://127.0.0.1:8081/saglam.php
curl -s -w 'durum=%{http_code}\n' http://127.0.0.1:8081/fatal.php
curl -s -w 'durum=%{http_code}\n' http://127.0.0.1:8081/bozuk.php
kill $pid
```

```
once
durum=200
once
durum=200
durum=500
```

**KN7.** Üç istek üç ayrı sonuç verdi: sağlam dosya `once` ve `200`, yürütmede hata veren dosya aynı `once` satırıyla birlikte yine `200`, sözdizim hatalı dosya gövdesiz `500`. İkinci satırın `200` olması hatanın kaybolduğu anlamına gelmez; hata, gövde başladıktan sonra geldiği için durum koduna yansımadı. Durum kodu bu yüzden bir sorunun duyulup duyulmadığının güvenilir ölçüsü değildir, yalnızca hatanın çıktıdan önce mi sonra mı geldiğini yansıtır.

#### Kod ve Şablon Karışımı

Bir PHP dosyası kodu ve düz metni iç içe taşıyabilir. Açılış etiketi `<?php` kod bölümünü başlatır, kapanış etiketi `?>` bitirir; ikisinin arasındaki her şey ayrıştırılır, dışındaki her şey değiştirilmeden çıktıya gider. `<?=` ise bir değeri yazdırmak için kullanılan kısa biçimdir: `<?= $urun ?>` yazımı `<?php echo $urun; ?>` ile aynı işi görür. Böylece bir dosya hem betik hem şablon (template) olarak yazılabilir. Aşağıdaki örnek HTML listesini bir dizi üzerinde döngüyle üretir. `foreach (...):` ve `endforeach;` yazımı, süslü parantez yerine etiketler arasına yayılan bloklar için hazırlanmış biçimdir.

```php
<?php
// sablon.php — kod ve sablon karisimi, kisa yazdirma etiketiyle
$urunler = ['kalem', 'defter', 'silgi'];
?>
<ul>
<?php foreach ($urunler as $urun): ?>
  <li><?= $urun ?></li>
<?php endforeach; ?>
</ul>
```

```
<ul>
  <li>kalem</li>
  <li>defter</li>
  <li>silgi</li>
</ul>
```

**KN8.** Her `?>` etiketinden hemen sonra gelen tek bir satır sonu çıktıya yazılmaz; PHP onu kapanış etiketinin parçası sayar. Bu yüzden çıktıda kod satırlarından kalan boş satır yoktur ve üç `<li>` satırı arada boşluk olmadan alt alta dizilir. Bu davranış yalnız tek bir satır sonu için geçerlidir; etiketten sonra iki satır sonu varsa ikincisi çıktıya girer.

Bu sonuç dosyanın en sonunda önem kazanır. Yalnız PHP kodu taşıyan bir dosyada kapanış etiketi yazılmaz; açılış etiketinin kapanışı olmadan da dosya geçerlidir. Sebep ölçülebilir: kapanış etiketinden sonra gözden kaçan bir satır sonu ya da boşluk çıktıya girer. Aşağıdaki betik aynı tek satırlık programı üç biçimde yazar ve her birinin ürettiği bayt sayısını ölçer.

```bash
# kapanis.sh — dosya sonundaki kapanis etiketinden sonra kalan satir sonu bayt ekler
printf '<?php\necho "a";\n' > acik.php
printf '<?php\necho "a";\n?>\n' > kapali1.php
printf '<?php\necho "a";\n?>\n\n' > kapali2.php
for d in acik kapali1 kapali2; do
  printf '%s %s\n' "$d" "$(php $d.php | wc -c | tr -d ' ')"
done
```

```
acik 1
kapali1 1
kapali2 2
```

**KN9.** Etiketsiz dosya ve tek satır sonlu kapanışlı dosya bir bayt üretti, `echo "a"`'nın kendisi; iki satır sonlu kapanışlı dosya iki bayt üretti. Fazladan baytın zararı görünmez olmasındadır: yanıt başlıkları gönderilmeden önce çıktıya giren her bayt, başlık ayarlamayı geç bırakır. Bu yüzden dosya yalnız kod taşıyorsa son etiket yazılmaz.

#### Satır Yorumu da Kapanış Etiketinde Biter

Etiketlerin ayrıştırmadan önce işlediği bir sonuç daha vardır: kapanış etiketi, bir satır yorumunun içinde de kod bölümünü bitirir. Yorumun geri kalanı dahil, etiketten sonraki her şey düz metindir; sonraki satırlar kod olmaktan çıkar. Sözdizim hatası bu durumda çıkmaz, çünkü ortada ayrıştırılacak bir kod kalmamıştır ve dosya sorunsuz biçimde çalışır.

```php
<?php
// yorum.php — satir yorumundaki kapanis etiketi kod bolumunu bitirir
echo "a\n"; // yorum ?> bu kisim metin
echo "b\n";
```

```
a
 bu kisim metin
echo "b\n";
```

**KN10.** Çıktıda `a` satırı, ardından yorumun etiketten sonraki kısmı ve en sonda `echo "b\n";` satırının kendisi, yani kaynak metni, düz metin olarak yer aldı; `b` hiç basılmadı. Hiçbir sinyal yoktu: ne uyarı ne durum kodu. Bu gözlem tablodaki sınıflardan sessiz sınıfa girer ve dersin ölçü sınıfını değiştirmez; yalnız etiketlerin ayrıştırmadan önce, yorumun anlamından bağımsız işlendiğini gösterir.

#### Harf Duyarlılığı

PHP'de bir adın büyük ve küçük harfe duyarlı olup olmadığı adın türüne bağlıdır. Anahtar sözcükler ve fonksiyon adları duyarsızdır; değişken adları ve `define()` ile tanımlanan sabit adları duyarlıdır. Bir yazım hatası bu yüzden bir yerde fark edilmez, başka yerde fark edilir. Aşağıdaki dosya dört adı büyük ve küçük harf farkıyla dener; hata işleyicisi bir sayaç tutar.

```php
<?php
// harf.php — fonksiyon adi harfe duyarsiz, degisken ve sabit adi duyarli
$bildirim = 0;
set_error_handler(function (int $seviye) use (&$bildirim): bool {
    $bildirim++;
    return true;
});

$ad = 'Ada';
define('SABIT', 5);

ECHO 'anahtar sozcuk: buyuk harfle de calisti', "\n";
echo 'STRLEN:     ', STRLEN('abc'), "\n";
echo 'bildirim:   ', $bildirim, "\n";

$gorulen = $Ad;
echo 'bildirim:   ', $bildirim, ' ($Ad okununca)', "\n";
echo 'defined(sabit): ', var_export(defined('sabit'), true), "\n";
echo 'defined(SABIT): ', var_export(defined('SABIT'), true), "\n";
```

```
anahtar sozcuk: buyuk harfle de calisti
STRLEN:     3
bildirim:   0
bildirim:   1 ($Ad okununca)
defined(sabit): false
defined(SABIT): true
```

**KN11.** `ECHO` ve `STRLEN` büyük harfle yazılsa da çalıştı ve hiçbir bildirim üretmedi. `$Ad`, `$ad`'dan ayrı bir değişkendir; okunduğunda bir uyarı duyuldu ve sayaç bire çıktı. Sabit adında da `sabit` ile `SABIT` farklıdır: yalnız tam yazım bulundu. Yani aynı yazım hatası fonksiyon adında sessizdir, değişken adında uyarıdır; hatanın hangi kanaldan duyulacağına, yanlış yazılan adın türü karar verir.

#### Tek Tırnak ve Çift Tırnak

PHP'de dizgi iki biçimde yazılır ve ikisi aynı değildir. Tek tırnaklı dizgide değişken ve kaçış dizisi yorumlanmaz; yalnız `\'` ve `\\` özel anlam taşır. Çift tırnaklı dizgide değişken yerine konur ve `\n` gibi kaçış dizileri çözülür. Aşağıdaki dosya aynı metni iki biçimde yazar ve uzunluklarını sayar.

```php
<?php
// dizgi.php — tek tirnak degisken ve kacis cozmez, cift tirnak cozer
$ad = 'Ada';
$tek = 'merhaba $ad\n';
$cift = "merhaba $ad\n";
$suslu = "merhaba {$ad}x";

echo 'tek:   ', $tek, "|\n";
echo 'cift:  ', $cift, "|\n";
echo 'suslu: ', $suslu, "|\n";
echo 'tek uzunluk:  ', strlen($tek), "\n";
echo 'cift uzunluk: ', strlen($cift), "\n";
echo 'ters bolu cizgi: ', strlen('a\\b'), ' ', strlen('a\nb'), ' ', strlen("a\nb"), "\n";
```

```
tek:   merhaba $ad\n|
cift:  merhaba Ada
|
suslu: merhaba Adax|
tek uzunluk:  13
cift uzunluk: 12
ters bolu cizgi: 3 4 3
```

**KN12.** Tek tırnaklı dizgi `$ad` ve `\n` olduğu gibi çıktı, uzunluğu bu yüzden on üç oldu. Çift tırnaklı dizgide `$ad` yerine `Ada` geldi ve `\n` tek bir satır sonu karakteri oldu. Süslü parantez, değişken adının nerede bittiğini belirler. Son satır farkı karakter düzeyinde gösterir: tek tırnakta `\n` iki karakterdir, çift tırnakta bir. İkisi de bildirim üretmez; tek tırnakla yazılmış bir `\n` beklenen satır sonunu sessizce vermez.

#### Yorum Biçimleri

PHP üç yorum biçimi tanır: `#` ve `//` satır sonuna kadar, `/* */` ise kapanışına kadar sürer. Çok satırlı yorumlar iç içe geçmez: ilk `*/` yorumu bitirir. Aşağıdaki dosya beş `echo`'yu farklı yorumlarla çevreler.

```php
<?php
// yorum_turu.php — yorum biciminin nerede bittigini olcer
echo "1\n"; # satir sonu yorumu
echo "2\n"; // satir sonu yorumu
echo "3\n"; /* satir ici yorum */ echo "4\n";
/* dis yorum /* ic yorum */ echo "5\n";
```

```
1
2
3
4
5
```

**KN13.** Beş satırın beşi de basıldı. Dördüncü satırdaki `/*` ikinci kez açıldığında yorum derinleşmedi; ilk `*/` yorumu kapattı ve `echo "5\n"` kod olarak çalıştı. Yorum yazarken bir bloğu iç içe kapatmaya güvenen kod, kapanıştan sonrasını çalıştırır. Bu da sessiz sınıftadır: hata ya da uyarı yoktur, çalışan kodun miktarı artmıştır.

#### echo ve print

PHP'de ekrana yazan iki dil yapısı vardır ve ikisi aynı değildir. `echo` virgülle ayrılmış birden çok değer alır ve bir değer döndürmez. `print` tek değer alır ve her zaman `1` döndürür; bu yüzden bir ifadenin içinde kullanılabilir. Aşağıdaki dosya iki davranışı ve dizi elemanının çift tırnak içindeki yazımını ölçer.

```php
<?php
// yazdir.php — print deger dondurur, echo cok argumanlidir, dizi elemani cift tirnakta cozulur
$sonuc = print "a\n";
echo 'print dondurdu: ', $sonuc, "\n";
echo 'bir', 'iki', "\n";

$dizi = [10, 20];
echo "dizi: $dizi[0]-$dizi[1]\n";
echo 'tek tirnakta: $dizi[0]', "\n";
```

```
a
print dondurdu: 1
biriki
dizi: 10-20
tek tirnakta: $dizi[0]
```

**KN14.** `print` satırı yazdı ve `1` döndürdü; `echo` ise iki dizgiyi araya hiçbir şey koymadan art arda yazdı. Çift tırnaklı dizgide `$dizi[0]` elemanın değeriyle yer değiştirdi, tek tırnaklıda olduğu gibi kaldı. Hiçbiri bildirim üretmez; farkı yalnız çıktıya bakarak görmek mümkündür.

#### Sınırlayıcı Ölçüm

Sözdizim hatalı dosya hiç çalışmaz kuralı, her dosya için ayrı geçerlidir. **KN15.** Bir dosya başka bir dosyayı `include` ile çekiyorsa, hatalı olan çekilen dosyadır ve ana dosya çoktan çalışmaya başlamıştır. Çekilen dosya yürütme sırasında ayrıştırıldığı için hata, o noktada bir `ParseError` olarak fırlatılır ve `try` ile `catch` yakalayabilir. Aşağıdaki dosya, daha önce yazılan `bozuk.php`'yi çeker.

```php
<?php
// ana.php — sozdizim hatali dosya dahil edilir; ana dosya calismaya baslamistir
echo "ana basladi\n";
try {
    include 'bozuk.php';
} catch (ParseError $hata) {
    echo 'yakalandi ', get_class($hata), "\n";
}
echo "ana bitti\n";
```

```
ana basladi
yakalandi ParseError
ana bitti
```

Ana dosya üç satırını da bastı, `bozuk.php`'nin `once` satırı ise çıkmadı: çekilen dosya kendi içinde yine hiçbir satırını çalıştırmadı. Kural aynı kaldı, kapsamı değişti: ayrıştırma dosya başına yapılır ve bir dosyanın başarısızlığı ancak o dosyayı çekmekte olan dosyanın yakalayıcısına kadar ulaşır. Yakalayıcı yoksa `ParseError` da durdurur sınıfına düşer.

#### Özet

- Dosya çalıştırılmadan önce bütünüyle ayrıştırılır; sözdizim hatalı dosyanın tek bir satırı bile çalışmaz.
- Yürütmede fırlayan hata önceki çıktıyı korur: `fatal.php` bir satır bastı, `bozuk.php` hiç basmadı; ikisi de 255 ile çıktı.
- Bu dersin ölçü sınıfı durdurur: sözdizim hatası yürütmeyi başlamadan keser, yürütmede fırlayan hata başladıktan sonra keser.
- Yerleşik sunucuda sözdizim hatalı dosya gövdesiz `500` verir; yürütmede fırlayan hata gövde başladıktan sonra `200` ile kısmi çıktı bırakır.
- `?>` ve `<?=` etiketleri kod ile şablonu karıştırır; yalnız kod taşıyan dosyada kapanış etiketi yazılmaz, çünkü ardından gelen fazla satır sonu çıktıya bayt ekler.
- Kural dosya başınadır: dahil edilen dosyadaki sözdizim hatası, ana dosya çalıştıktan sonra yakalanabilir bir `ParseError` olarak fırlar.

#### Sonraki Adım

Dosyanın çalışmaya başladığı ve etiketlerin içinde ne olduğu belli oldu. Değişkenler ve Veri Tipleri dersi, kodun içinde bir değerin tipinin sinyal vermeden değişip değişmediğini ölçüyor: bir değer tipini kaybedebilir mi?

### Değişkenler ve Veri Tipleri

Source: https://academia.sh/tr/kurslar/php-temelleri/degiskenler-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ışı.

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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?php
// islev_kipsiz.php — kipsiz dosyada tanimli int parametreli islev
function iki_kati_kipsiz(int $n): int
{
    return $n * 2;
}
```

```php
<?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
<?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
<?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 `float` olur, `/` işleci bölümün tam çıkıp çıkmadığına göre `int` ya da `float` verir 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, `null` verir ve `E_WARNING` bildirir.
- Tip bildirimi `int` parametreye `'5'` için zorlama, `1.5` için `E_DEPRECATED`, `'5x'` için `TypeError` verir.
- `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?

### İşleçler

Source: https://academia.sh/tr/kurslar/php-temelleri/islecler

PHP işleçlerinin iki tipi buluşturduğunda ne söylediği: sayısal dizgilerle aritmetik, gevşek ve katı eşitlik, birleştirme ve öncelik, sıfıra bölme ve boş değer birleştirme işlecinin uyarıyı susturması.

Değişkenler ve Veri Tipleri dersi, bir değerin tipinin atamada, bölmede ve dönüşümde sinyal vermeden değişebildiğini ölçtü. Bu ders aynı soruyu işleçlere soruyor: bir işleç iki farklı tipi buluşturduğunda, ya da işleneni işleç için uygun olmadığında, dil ne söylüyor? Cevap işleçten işlece değişir ve bu dersin ölçü sınıfı uyarıdır: işlem çoğu zaman sürer, ama yanında bir bildirim taşır.

JavaScript'te aynı soru M06/K01'de ölçülmüştü: orada zorlama ve eşitlik kendi kurallarıyla işliyordu. PHP'nin kuralları ondan ayrıdır ve ölçüm yine yöntemi aynı tutar: her dosya bir hata işleyicisi kurar, işleyici bildirimin seviyesini adıyla basar ve ileti metni yazdırılmaz, çünkü ileti sürüme bağlıdır.

#### Sayısal Dizgilerle Aritmetik

PHP bir dizgiyi aritmetikte kullanabilir: dizgi bir sayı gibi okunuyorsa sayıya dönüştürülür. Üç durum vardır. Dizgi tamamen sayıdır, dizgi bir sayıyla başlayıp başka karakterlerle sürer, ya da dizgide hiç sayı yoktur. Aşağıdaki dosya üçünü de `1` ile toplar ve her birinin sonucunu ya da fırlattığı istisnayı basar.

```php
<?php
// aritmetik.php — sayisal dizgi: tam sayi sessiz, onek uyari, sayi olmayan TypeError
set_error_handler(function (int $seviye): bool {
    echo 'bildirilen: ', $seviye === E_WARNING ? 'E_WARNING' : 'baska', "\n";
    return true;
});

foreach (['5', '5 elma', 'elma'] as $girdi) {
    try {
        $sonuc = $girdi + 1;
        echo var_export($girdi, true), ' + 1 = ', var_export($sonuc, true), "\n";
    } catch (TypeError $hata) {
        echo var_export($girdi, true), ' + 1 -> ', get_class($hata), "\n";
    }
}
```

```
'5' + 1 = 6
bildirilen: E_WARNING
'5 elma' + 1 = 6
'elma' + 1 -> TypeError
```

**KN26.** Üç girdi üç ayrı sonuç verdi. Tamamen sayısal dizgi sessizce sayıya çevrildi. Sayıyla başlayan dizgi sayı önekiyle işlendi ve bir `E_WARNING` bildirdi; işlem yine de bitti. Hiç sayı taşımayan dizgi ise toplamayı yapmadan bir `TypeError` fırlattı. Bu ders sınıfın adını uyarı koyar, çünkü ortadaki durum hem sonuç üretir hem de bildirim taşır; ama üç durum, tek işleçte üç farklı sınıfı yan yana gösterir.

#### Gevşek ve Katı Eşitlik

`==` iki değeri karşılaştırmadan önce ortak bir tipe getirmeye çalışır; `===` hem tipe hem değere bakar. İkisi aynı çift için farklı cevap verebilir ve sinyal hiç gelmez. Aşağıdaki dosya sekiz çifti iki işleçle karşılaştırır. Çiftler, yaygın yanlış anlamaların çoğunu kapsayacak biçimde seçildi: sayısal dizgiler, boş dizgiyle `null`, sıfırla dizgi, boş diziyle `false`.

```php
<?php
// esitlik.php — gevsek ve kati esitlik ayni cift icin ayri cevap verir
$cift = [
    ["'1e3' ve '1000'", '1e3', '1000'],
    ["'abc' ve 0", 'abc', 0],
    ["'0' ve false", '0', false],
    ["'' ve null", '', null],
    ["'1' ve '01'", '1', '01'],
    ["100 ve '1e2'", 100, '1e2'],
    ['[] ve false', [], false],
    ['null ve 0', null, 0],
];
printf("%-18s%-7s%s\n", 'cift', '==', '===');
foreach ($cift as [$ad, $a, $b]) {
    printf("%-18s%-7s%s\n", $ad, var_export($a == $b, true), var_export($a === $b, true));
}
```

```
cift              ==     ===
'1e3' ve '1000'   true   false
'abc' ve 0        false  false
'0' ve false      true   false
'' ve null        true   false
'1' ve '01'       true   false
100 ve '1e2'      true   false
[] ve false       true   false
null ve 0         true   false
```

**KN27.** `'1e3' == '1000'` doğru çıktı: iki dizgi de sayısal olduğu için sayı olarak karşılaştırıldı ve `1e3` bin demektir. `'abc' == 0` yanlış çıktı: sayısal olmayan bir dizgi, sayıyla karşılaştırılırken `0` ile eşit sayılmıyor. `'0' == false`, `'' == null`, `[] == false` ve `null == 0` doğru çıktı. Katı eşitlik sekiz çiftin hiçbirinde doğru vermedi, çünkü sekizinde de tipler ayrıdır. Bu tablo, `==`'in bir sinyali olmadığını ama sonucunun değerin biçimine bağlı olduğunu gösterir.

#### Karşılaştırma, Birleştirme ve Öncelik

`<=>` işleci (uzay gemisi işleci, spaceship operator) iki değeri karşılaştırıp `-1`, `0` ya da `1` verir. `.` işleci dizgi birleştirir. `and` ve `&&` aynı mantığı farklı öncelikle yapar. Aşağıdaki dosya dört ifadeyi çalıştırır: sayısal dizgiler `<=>` ile, sayısal olmayan bir dizgi `<=>` ile, `+` ile `.` karışımı ve `and` ile atama.

```php
<?php
// oncelik.php — karsilastirma ve oncelik sonucu degistirir
echo "'10' <=> '9':  ", '10' <=> '9', "\n";
echo "'10' <=> '9a': ", '10' <=> '9a', "\n";
echo "1 + 2 . '3':   ", 1 + 2 . '3', "\n";
echo "'1' . 2 + 3:   ", '1' . 2 + 3, "\n";

$a = true and false;
$b = (true and false);
echo 'a: ', var_export($a, true), ' b: ', var_export($b, true), "\n";
```

```
'10' <=> '9':  1
'10' <=> '9a': -1
1 + 2 . '3':   33
'1' . 2 + 3:   15
a: true b: false
```

**KN28.** İki sayısal dizge sayı olarak karşılaştırıldı: `'10'` büyüktür `'9'`. `'9a'` sayısal olmadığı için karşılaştırma dizgi olarak yapıldı ve `'10'` küçük çıktı. `+` ile `.` karışımında toplama önce yapıldı: `1 + 2 . '3'` ifadesi `'33'`, `'1' . 2 + 3` ifadesi `'15'` verdi. Son iki satır en sinsi olandır: `and` atamadan sonra işlendiği için `$a = true and false` ifadesinde `$a` doğru kaldı, parantezli `$b` ise yanlış oldu. Hiçbiri bildirim üretmedi.

#### Artırma ve Azaltma

`++` ve `--` işleçleri sayılarda beklenen işi yapar, ama operand bir dizgi ya da `null` olduğunda davranışları ayrışır. Dizgiler Perl'deki gibi alfabetik olarak artırılır; `null` artırılınca `1` olur, azaltılınca `null` kalır. Aşağıdaki dosya altı durumu çalıştırır, sonucu ve tipini basar ve işleyici bir sayaç tutar.

```php
<?php
// artirma.php — ++ ve -- dizgi ve null uzerinde ayri davranir
set_error_handler(function (int $seviye): bool {
    echo $seviye === E_DEPRECATED ? "  [E_DEPRECATED]\n" : ($seviye === E_WARNING ? "  [E_WARNING]\n" : "  [baska]\n");
    return true;
});

$durumlar = ['5', 'a', 'z', 'Az', 'a9', ''];
foreach ($durumlar as $baslangic) {
    $x = $baslangic;
    $x++;
    printf("%-6s++ -> %-7s%s\n", var_export($baslangic, true), var_export($x, true), get_debug_type($x));
}

$bos = null;
$bos++;
$yok = null;
$yok--;
echo 'null++ -> ', var_export($bos, true), ' null-- -> ', var_export($yok, true), "\n";
```

```
'5'   ++ -> 6      int
  [E_DEPRECATED]
'a'   ++ -> 'b'    string
  [E_DEPRECATED]
'z'   ++ -> 'aa'   string
  [E_DEPRECATED]
'Az'  ++ -> 'Ba'   string
  [E_DEPRECATED]
'a9'  ++ -> 'b0'   string
  [E_DEPRECATED]
''    ++ -> '1'    string
  [E_WARNING]
null++ -> 1 null-- -> NULL
```

**KN29.** `'5'` artırılınca bir tam sayı oldu ve sessiz kaldı. `'a'` `'b'` oldu, `'z'` `'aa'`, `'Az'` `'Ba'`, `'a9'` `'b0'`: sayısal olmayan dizgilerde artırma alfabetik bir kuraldır ve beş dizginin hepsi bir `E_DEPRECATED` bildirdi; bu bildirim PHP 8.5 ile sayısal olmayan bütün dizgilere yayıldı. `null` artırılınca `1` verdi ve sessiz kaldı; azaltılınca `null` kaldı ve bir `E_WARNING` bildirdi. Aynı işleç, operandın tipine göre sessiz, kullanımdan kalkmış ya da uyarılı bir sonuç verdi.

#### Kalan ve Üs

`%` işleci kalanı verir ama işareti bölünenin işaretidir; operandlar tam sayıya çevrilir ve kesir atılır. `**` işleci üs alır ve sonuç `int` sınırını aşarsa `float` olur. Aşağıdaki dosya beş ifadeyi basar.

```php
<?php
// kalan.php — % isareti bolunenden alir, ** tasarsa float olur
$bildirim = 0;
set_error_handler(function (int $seviye) use (&$bildirim): bool {
    $bildirim++;
    return true;
});

$ifadeler = [
    '-7 % 3'   => -7 % 3,
    '7 % -3'   => 7 % -3,
    '7.9 % 3'  => 7.9 % 3,
    '2 ** 10'  => 2 ** 10,
    '2 ** -1'  => 2 ** -1,
];
foreach ($ifadeler as $ifade => $sonuc) {
    printf("%-10s%-6s%s\n", $ifade, get_debug_type($sonuc), var_export($sonuc, true));
}
echo '2 ** 200: ', get_debug_type(2 ** 200), "\n";
echo 'bildirim: ', $bildirim, "\n";
```

```
-7 % 3    int   -1
7 % -3    int   1
7.9 % 3   int   1
2 ** 10   int   1024
2 ** -1   float 0.5
2 ** 200: float
bildirim: 1
```

**KN30.** `-7 % 3` ifadesi `-1`, `7 % -3` ifadesi `1` verdi: işaret bölünenden gelir. `7.9 % 3` ifadesinde `7.9` önce `7`'ye kesildi, sonuç `1` oldu ve bir bildirim sayıldı; kesirli sayının tam sayıya örtük çevrilmesi kayıp uyarısı verir. `2 ** 10` bir `int`, `2 ** -1` bir `float`; `2 ** 200` taşınca `float` oldu ve tip bildirim taşımadan değişti. Son satır bu dersin Değişkenler dersiyle buluştuğu yerdir: tam sayı sınırının aşılması burada da sessizdir.

#### Birleşik Atama İşleçleri

`+=`, `.=` ve `??=` gibi birleşik atama işleçleri, değişkeni okuyup yeniden yazar. Değişken tanımsızsa okuma kısmı uyarı üretir; `??=` ise okumayı güvenli yapar ve yalnız değer yoksa atar. Aşağıdaki dosya tanımsız bir sayaca ve tanımsız bir dizgiye ekleme yapar, sonra aynı işi `??=` ile yapar. İşleyici seviye adını basar; kaçıncı bildirimin hangi satırdan geldiği çıktıdaki sırayla okunur.

```php
<?php
// birlesik.php — += ve .= tanimsizi okurken uyarir, ??= uyarmaz
set_error_handler(function (int $seviye): bool {
    echo $seviye === E_WARNING ? "  [E_WARNING]\n" : "  [baska]\n";
    return true;
});

echo "+= tanimsiz:\n";
$sayac += 5;
echo '  sayac: ', var_export($sayac, true), "\n";

echo ".= tanimsiz:\n";
$yazi .= 'a';
echo '  yazi: ', var_export($yazi, true), "\n";

echo "??= tanimsiz:\n";
$sure ??= 30;
echo '  sure: ', var_export($sure, true), "\n";

echo "??= tanimli:\n";
$sure ??= 99;
echo '  sure: ', var_export($sure, true), "\n";
```

```
+= tanimsiz:
  [E_WARNING]
  sayac: 5
.= tanimsiz:
  [E_WARNING]
  yazi: 'a'
??= tanimsiz:
  sure: 30
??= tanimli:
  sure: 30
```

**KN31.** `+=` ve `.=` tanımsız değişkeni okurken birer `E_WARNING` bildirdi, ama işlem sürdü: sayaç `5`, dizgi `'a'` oldu. `??=` ilk çağrıda değişkeni atadı ve bildirim üretmedi; ikinci çağrıda değişken tanımlı olduğu için hiçbir şey yapmadı ve `30` kaldı. Uyarıdan kaçınmanın dürüst yolu budur: değişkenin yokluğunu bir karar olarak ifade etmek, uyarıyı susturmak yerine ihtiyacı adlandırır.

#### Sıfıra Bölme

Sıfıra bölme, bu dersin ilk durdurur davranışıdır. `/` ve `%` işleçleri ile `intdiv()` sıfıra bölünce bir `DivisionByZeroError` fırlatır; `fdiv()` ise işlemi hatasız yapar ve kayan noktalı sonsuzluk verir. Aşağıdaki dosya dört çağrıyı sınar. Sonuç hesaplanıp ayrıca yazdırılır, çünkü virgüllü bir `echo` işlemin ortasında kesilirse kısmi çıktı bırakır.

```php
<?php
// sifir.php — sifira bolme durdurur, fdiv sonsuzluk verir
$sifir = 0;
$cagrilar = [
    '1 / 0'         => fn() => 1 / $sifir,
    '1 % 0'         => fn() => 1 % $sifir,
    'intdiv(1, 0)'  => fn() => intdiv(1, $sifir),
    'fdiv(1, 0)'    => fn() => fdiv(1, $sifir),
];
foreach ($cagrilar as $ad => $cagri) {
    try {
        $sonuc = $cagri();
        $yazi = var_export($sonuc, true);
    } catch (DivisionByZeroError $hata) {
        $yazi = get_class($hata);
    }
    printf("%-14s%s\n", $ad, $yazi);
}
```

```
1 / 0         DivisionByZeroError
1 % 0         DivisionByZeroError
intdiv(1, 0)  DivisionByZeroError
fdiv(1, 0)    INF
```

**KN32.** Üç işlem `DivisionByZeroError` fırlattı, `fdiv()` ise `INF` döndü. Bu, ölçü sınıfının sınırını gösterir: dil uyarıyla yetinmeyen bir hata türüne sahip. Hata yakalanmazsa yürütme Sözdizim ve Etiketler dersindeki gibi kesilir; yakalanırsa program sürer. Sonsuzluğun kendisi ise sessiz bir değerdir ve sonraki işlemlerde `NAN`'a dönüşebilir.

#### Sınırlayıcı Ölçüm

Bu dersteki bildirimlerin hepsini susturabilen bir işleç vardır: boş değer birleştirme işleci `??`. Tanımsız bir dizi anahtarını ya da değişkeni okurken uyarı bildirilir; aynı okuma `??` ile yapılırsa bildirim hiç üretilmez ve bu bilgi kaybolur. Aşağıdaki dosya aynı eksik anahtarı üç yoldan okur ve hata işleyicisi her bildirimi sayar.

```php
<?php
// susturma.php — ?? uyariyi susturur ve eksik anahtari gizler
$bildirim = 0;
set_error_handler(function (int $seviye) use (&$bildirim): bool {
    $bildirim++;
    return true;
});

$ayarlar = ['sure' => 30];

$a = $ayarlar['sur'];
echo 'dogrudan okuma: ', var_export($a, true), ' bildirim: ', $bildirim, "\n";

$b = $ayarlar['sur'] ?? 0;
echo '?? ile okuma:   ', var_export($b, true), ' bildirim: ', $bildirim, "\n";

$c = $ayarlar['sure'] ?? 0;
echo 'dogru anahtar:  ', var_export($c, true), ' bildirim: ', $bildirim, "\n";
```

```
dogrudan okuma: NULL bildirim: 1
?? ile okuma:   0 bildirim: 1
dogru anahtar:  30 bildirim: 1
```

**KN33.** Doğrudan okuma `null` verdi ve bir bildirim üretti. Yazım hatalı anahtarın `??` ile okunması bildirim üretmedi ve `0` döndü: yazım hatası, geçerli bir varsayılan değer gibi görünür hâle geldi. Üçüncü okuma doğru anahtarı buldu ve `30` verdi; sayaç yine bir kaldı. Bu bir hata değil, ölçümün sınırıdır: `??` uyarıyı susturur, dolayısıyla uyarı sınıfı bir işlecin arkasında sessiz sınıfa dönüşür. Varsayılan değer istenen davranışsa `??` doğrudur; yazım hatasını yakalamak istenen davranışsa, kullanılmamalıdır.

#### Özet

- İşleçlerin ölçü sınıfı uyarıdır: sayıyla başlayan dizgi `E_WARNING` bildirir ve işlem sürer; tamamen sayısal dizgi sessizdir; sayı olmayan dizgi `TypeError` fırlatır.
- `==` iki değeri ortak bir tipe getirerek karşılaştırır, `===` tipi de arar; sekiz çiftin hiçbirinde ikisi birden doğru çıkmadı ve hiçbir karşılaştırma bildirim üretmedi.
- `+`, `.` işlecinden önce işlenir ve `and` atamadan sonra çalışır; `$a = true and false` ifadesi `$a`'ya doğru atar.
- `/`, `%` ve `intdiv()` sıfıra bölünce `DivisionByZeroError` fırlatır; `fdiv()` `INF` döndürür.
- `??` tanımsız anahtar ve değişken uyarısını susturur; yazım hatası böylece sessiz bir varsayılana dönüşür.

#### Sonraki Adım

İşleçlerin tipleri buluşturduğunda neyi bildirdiği ve neyi susturduğu görüldü. Denetim Akışı dersi aynı soruyu dallara soruyor: bir koşulun hiçbir kolu eşleşmediğinde, yani karşılıksız bir dal kaldığında dil ne yapıyor?

### Denetim Akışı

Source: https://academia.sh/tr/kurslar/php-temelleri/denetim-akisi

PHP'de karşılıksız kalan dalların davranışı: switch ile match arasındaki fark, gevşek ve katı karşılaştırma, döngü değişkenlerinin ömrü, foreach ve referans tuzağı ve match içinde default kolunun durdurmayı sessize çevirmesi.

İşleçler dersi, tipleri buluşturan işleçlerin neyi bildirdiğini ve neyi susturduğunu ölçtü. Bu ders aynı soruyu dallara soruyor: bir koşulun, bir durumun ya da bir `match` kolunun hiçbiri eşleşmediğinde, yani karşılıksız bir dal kaldığında, dil ne yapıyor? Cevap iki deyimde farklıdır ve bu dersin ölçü sınıfı durdurur: en az bir deyim, eşleşme yoksa yürütmeyi keser.

PHP'de koşullu dallanma için `if`, `elseif` ve `else`, çoklu seçim için `switch` ve `match`, tekrar için `for`, `while`, `do while` ve `foreach` vardır. Programlama Temelleri kursunda (M01/K02) bu yapıların mantığı kurulmuştu; burada yalnız PHP'nin eşleşme ve çıkış davranışı ölçülüyor. Her dosya bir hata işleyicisi kurar ve işleyici, sinyal gelirse onu görünür kılar.

#### Karşılıksız Dal: switch ve match

`switch` bir değeri sırayla `case` etiketleriyle karşılaştırır ve hiçbiri eşleşmezse, `default` yoksa, hiçbir şey yapmadan geçer. `match` aynı işi bir ifade olarak yapar ve bir değer döndürür; hiçbir kol eşleşmezse bir `UnhandledMatchError` fırlatır. Aşağıdaki dosya üç değeri iki deyimden geçirir. `$sonuc` değişkeni her turda önceki bir değerle başlar; karşılıksız dalda bu değerin ne olduğu ölçülür.

```php
<?php
// karsiliksiz.php — switch sessizce gecer, match durdurur
$bildirim = 0;
set_error_handler(function (int $seviye) use (&$bildirim): bool {
    $bildirim++;
    return true;
});

foreach ([1, 2, 3] as $kod) {
    $sonuc = 'onceki';
    switch ($kod) {
        case 1:
            $sonuc = 'bir';
            break;
        case 2:
            $sonuc = 'iki';
            break;
    }
    echo "switch($kod): $sonuc\n";

    $sonuc = 'onceki';
    try {
        $sonuc = match ($kod) {
            1 => 'bir',
            2 => 'iki',
        };
        echo "match($kod):  $sonuc\n";
    } catch (UnhandledMatchError $hata) {
        echo "match($kod):  ", get_class($hata), ' (sonuc: ', $sonuc, ")\n";
    }
}
echo 'bildirim: ', $bildirim, "\n";
```

```
switch(1): bir
match(1):  bir
switch(2): iki
match(2):  iki
switch(3): onceki
match(3):  UnhandledMatchError (sonuc: onceki)
bildirim: 0
```

**KN34.** Üçüncü turda `switch` hiçbir şey yapmadı: değişken önceki değerinde kaldı ve bildirim sayacı artmadı. `match` aynı girdide `UnhandledMatchError` fırlattı ve atama gerçekleşmediği için `$sonuc` yine önceki değerinde kaldı; fark, dilin bu durumda artık sessiz kalmamasıdır. `match` ifadesi, eşleşmeyen bir değeri bir hata olarak tanımlar; `switch` ise bunu normal bir yürütme yolu sayar.

#### Gevşek ve Katı Karşılaştırma

İkisi arasındaki ikinci fark, karşılaştırmanın sıkılığıdır. `switch` etiketleri `==` ile, yani gevşek eşitlikle karşılaştırır; `match` kolları `===` ile, yani katı eşitlikle. Aynı girdi iki deyimde farklı kola gidebilir. Aşağıdaki dosya beş girdiyi iki deyime verir; etiket ve kol listeleri aynıdır.

```php
<?php
// karsilastirma.php — switch gevsek, match kati karsilastirir
$girdiler = [
    ["'1'", '1'],
    ['null', null],
    ["''", ''],
    ['0', 0],
    ["'a'", 'a'],
];
printf("%-6s%-13s%s\n", 'girdi', 'switch', 'match');
foreach ($girdiler as [$ad, $girdi]) {
    switch ($girdi) {
        case 1:
            $s = 'case 1';
            break;
        case false:
            $s = 'case false';
            break;
        case 'a':
            $s = "case 'a'";
            break;
        default:
            $s = 'default';
    }
    $m = match ($girdi) {
        1 => 'kol 1',
        false => 'kol false',
        'a' => "kol 'a'",
        default => 'default',
    };
    printf("%-6s%-13s%s\n", $ad, $s, $m);
}
```

```
girdi switch       match
'1'   case 1       default
null  case false   default
''    case false   default
0     case false   default
'a'   case 'a'     kol 'a'
```

**KN35.** `'1'` dizgisi `switch`'te `case 1`'e gitti, `match`'te ise `1 =>` koluna uymayıp `default`'a düştü. `null`, boş dizgi ve `0` `switch`'te `case false`'e gitti, çünkü gevşek eşitlikte `false`'la eşittirler; `match`'te hiçbiri `false =>` koluna uymadı. Yalnız `'a'` iki deyimde aynı yere gitti. Bu, İşleçler dersindeki eşitlik tablosunun denetim akışındaki yansımasıdır: `switch` bildirim üretmeden gevşek eşitliğin bütün sürprizlerini devralır.

#### İç İçe Döngülerde Çıkış

`break` bir döngüyü bitirir, `continue` bir sonraki tura geçer ve ikisi de bir sayı alabilir: `break 2` iki iç içe döngüden birden çıkar, `continue 2` dıştaki döngünün sonraki turuna geçer. Dil bu sayıyı geçerli bir düzey sınırı dışında kabul etmez, ama sayının neyi atlattığı programcının okuması gereken bir ayrıntıdır. Aşağıdaki dosya üç iç içe durumu çalıştırır ve döngü değişkenlerinin döngü bittikten sonraki değerini de basar.

```php
<?php
// cikis.php — break 2 ve continue 2 dis donguyu etkiler
$satirlar = [];
for ($i = 1; $i <= 3; $i++) {
    for ($j = 1; $j <= 3; $j++) {
        if ($j === 2) {
            continue;
        }
        if ($i === 2) {
            continue 2;
        }
        if ($i === 3 && $j === 3) {
            break 2;
        }
        $satirlar[] = $i . $j;
    }
}
echo implode(',', $satirlar), "\n";
echo "i: $i j: $j\n";
```

```
11,13,31
i: 3 j: 3
```

**KN36.** Çıktıdaki üç çift, her çıkışın etkisini gösterir: `j` ikiye eşit olduğunda iç tur atlandı, `i` ikiye eşit olduğunda dış turun geri kalanı atlandı, `i` üç ve `j` üç olduğunda iki döngü birden bitti. Son satır, PHP'de bir döngü değişkeninin döngü bittikten sonra da yaşadığını gösterir: `i` ve `j` hâlâ tanımlıdır ve çıkış anındaki değerlerini korur. Döngü değişkeninin kapsamı döngü değil, onu içeren fonksiyondur; bu, aynı adı sonradan kullanan koda kalıntı bırakabilir.

#### foreach ve Referans Tuzağı

`foreach` bir dizinin elemanlarını sırayla bir değişkene kopyalar; kopya değiştirilirse dizi etkilenmez. Değişken `&` ile bir referans olarak bağlanırsa dizi değişir ve döngü bittikten sonra bu referans yaşamaya devam eder. Referansı temizlemeden aynı adı yeniden kullanan ikinci bir `foreach`, dizinin son elemanının üzerine yazar. Aşağıdaki dosya üç durumu ölçer.

```php
<?php
// referans.php — foreach referansi donguden sonra da yasar
$a = [1, 2, 3];
foreach ($a as $v) {
    $v *= 10;
}
echo 'kopya:         ', implode(',', $a), "\n";

$b = [1, 2, 3];
foreach ($b as &$w) {
    $w *= 10;
}
foreach ($b as $w) {
}
echo 'referans:      ', implode(',', $b), "\n";

$c = [1, 2, 3];
foreach ($c as &$x) {
    $x *= 10;
}
unset($x);
foreach ($c as $x) {
}
echo 'unset ile:     ', implode(',', $c), "\n";
```

```
kopya:         1,2,3
referans:      10,20,20
unset ile:     10,20,30
```

**KN37.** Kopya ile dönen `foreach` diziyi değiştirmedi. Referansla dönen, her elemanı on katına çıkardı. Ardından aynı adı bir kopya olarak kullanan ikinci döngü, referans hâlâ son elemana bağlı olduğu için dizinin son elemanını sessizce ezdi ve `10,20,20` çıktı. `unset($x)` referansı koparttı ve `10,20,30` kaldı. Bu davranış sessizdir: bildirim yok, istisna yok, yalnız yanlış bir dizi.

#### Koşulun İçinde Atama

Tek bir karakter farkı, bir koşulu bir atamaya çevirir: `=` ile `==`. PHP bunu sözdizim hatası saymaz, çünkü atama bir ifadedir ve değeri atanan değerdir. Koşul o değere göre karar verir. Aşağıdaki dosya aynı koşulu üç biçimde yazar; hata işleyicisi bir sayaç tutar.

```php
<?php
// kosul_atama.php — if icinde = atama yapar ve degeri kosul sayilir
$bildirim = 0;
set_error_handler(function (int $seviye) use (&$bildirim): bool {
    $bildirim++;
    return true;
});

$durum = 'bos';
if ($durum = 'dolu') {
    echo "atama: dal dogru, durum: $durum\n";
}

$durum = 'bos';
if ($durum == 'dolu') {
    echo "karsilastirma: dal dogru\n";
} else {
    echo "karsilastirma: dal yanlis, durum: $durum\n";
}

$deger = 5;
if ($deger = 0) {
    echo "sifir atama: dal dogru\n";
} else {
    echo "sifir atama: dal yanlis, deger: $deger\n";
}
echo 'bildirim: ', $bildirim, "\n";
```

```
atama: dal dogru, durum: dolu
karsilastirma: dal yanlis, durum: bos
sifir atama: dal yanlis, deger: 0
bildirim: 0
```

**KN38.** `'dolu'` atandığında koşul doğru çıktı ve `$durum` değişti; karşılaştırma ise değeri korudu. `0` atanınca koşul yanlış çıktı ve `$deger` beşten sıfıra düştü. Üç durumda da sayaç sıfır kaldı: dil atamayı koşul içinde meşru sayar ve bir sinyal vermez. Hata, yalnız değişkenin değerinin koşuldan sonra değişmiş olmasından anlaşılır.

#### Döngü Kaç Kez Döner

Bir döngünün kaç tur attığı koşuldan çıkarılabilir, ama üç durumda beklenti ile gerçek ayrışır. `do while` koşulu turdan sonra sınar, bu yüzden yanlış bir koşulda bile bir kez döner. Kayan noktalı bir sayaç, `0.1` adımlarla `1`'e ulaştığında beklenenden bir tur fazla atabilir. Döngü sırasında diziye eleman eklemek, `foreach`'in dönüş sayısını değiştirmez, çünkü `foreach` başlangıç anındaki diziyi dolaşır. Aşağıdaki dosya üçünü sayar.

```php
<?php
// tur_sayisi.php — do while bir kez doner, kayan nokta sayaci fazla doner
$say = 0;
do {
    $say++;
} while (false);
echo "do while:    $say\n";

$say = 0;
for ($x = 0; $x < 1; $x += 0.1) {
    $say++;
}
echo "for 0.1 adim: $say\n";

$dizi = [1, 2];
$say = 0;
foreach ($dizi as $eleman) {
    $dizi[] = $eleman * 10;
    $say++;
}
echo "foreach:      $say tur, dizi: ", count($dizi), " eleman\n";
```

```
do while:    1
for 0.1 adim: 11
foreach:      2 tur, dizi: 4 eleman
```

**KN39.** `do while` yanlış koşulda bile bir tur attı. Kayan noktalı sayaç on değil on bir tur attı, çünkü onuncu toplamda sayaç tam `1` olmadı, `1`'in hemen altında kaldı; bu, Değişkenler dersindeki `0.1 + 0.2` ölçümünün döngü tarafıdır. `foreach` iki tur attı ve dizi dört elemana çıktı: dönülen dizi ilk andaki kopyadır. Üçü de sessizdir.

#### foreach ve Dizi Olmayan Değer

`foreach` bir dizi ya da bir nesne bekler. Başka bir değer verilirse döngü hiç dönmez ve dil bir `E_WARNING` bildirir; yani bu durum, bu dersin ölçü sınıfından farklı olarak, durdurmaz ama sessiz de değildir. Aşağıdaki dosya `null`, bir dizgi ve boş bir diziyi aynı döngüye verir.

```php
<?php
// dizi_degil.php — foreach dizi olmayan degerde uyarir ve donmez
set_error_handler(function (int $seviye): bool {
    echo $seviye === E_WARNING ? "  [E_WARNING]\n" : "  [baska]\n";
    return true;
});

$girdiler = [
    'null'      => null,
    "'abc'"     => 'abc',
    '[]'        => [],
    '[1, 2]'    => [1, 2],
];
foreach ($girdiler as $ad => $girdi) {
    $say = 0;
    foreach ($girdi as $eleman) {
        $say++;
    }
    echo "$ad: $say tur\n";
}
```

```
  [E_WARNING]
null: 0 tur
  [E_WARNING]
'abc': 0 tur
[]: 0 tur
[1, 2]: 2 tur
```

**KN40.** `null` ve dizgi döngüye girmedi ve her biri bir `E_WARNING` bildirdi; boş dizi ve iki elemanlı dizi bildirimsiz döndü. Boş dizi ile `null` arasındaki bu fark, bir fonksiyonun "sonuç yok" için hangisini döndürdüğünü bir sözleşme hâline getirir: `null` döndürüp `foreach`'e vermek her çağrıda bir uyarı üretir.

#### Döngü Sırasında Değişen Dizi

`foreach` değerle çalışırken diziyi başlangıçtaki hâliyle dolaşır; döngü içinde dizi büyütülse bile tur sayısı değişmez. `for` ise koşulu her turda yeniden hesaplar; koşul `count()` ise dizi büyüdükçe tur da uzar. Aşağıdaki dosya aynı büyütmeyi iki döngüde sayar.

```php
<?php
// buyume.php — foreach baslangic dizisini dolasir, for kosulu her turda yeniden hesaplar
$a = [1, 2, 3];
$tur_a = 0;
foreach ($a as $v) {
    if ($tur_a < 5) {
        $a[] = $v;
    }
    $tur_a++;
}
echo 'foreach: ', $tur_a, ' tur, ', count($a), " eleman\n";

$b = [1, 2, 3];
$tur_b = 0;
for ($i = 0; $i < count($b); $i++) {
    if ($tur_b < 3) {
        $b[] = 9;
    }
    $tur_b++;
}
echo 'for:     ', $tur_b, ' tur, ', count($b), " eleman\n";
```

```
foreach: 3 tur, 6 eleman
for:     6 tur, 6 eleman
```

**KN41.** `foreach` üç tur döndü ve dizi altı elemana çıktı: dolaşılan, başlangıçtaki üç elemanlı kopyaydı. `for` altı tur döndü, çünkü `count($b)` her turda yeni uzunluğu verdi. Biri büyümeyi görmez, öbürü görür; ikisi de bildirim üretmez. Döngünün döndüğü dizi döngü içinde değişiyorsa tur sayısı, hangi döngü yapısının seçildiğine bağlıdır.

#### Sınırlayıcı Ölçüm

`match` karşılıksız girdide durdurduğu için, bir kolun `default` taşıması durdurmayı sessize çevirir. Bu ders sınıfının sınırı budur: aynı deyim, tek bir kol eklenince durdurur sınıfından sessiz sınıfa geçer. Aşağıdaki dosya, `default` kolu olan ve olmayan iki fonksiyonu aynı girdilerle çalıştırır. Sonuç önce hesaplanır, sonra yazdırılır.

```php
<?php
// varsayilan.php — default kolu match'in durdurmasini sessize cevirir
function ad_katisi(int $kod): string
{
    return match ($kod) {
        1 => 'bir',
        2 => 'iki',
    };
}

function ad_varsayilan(int $kod): string
{
    return match ($kod) {
        1 => 'bir',
        2 => 'iki',
        default => 'bilinmiyor',
    };
}

foreach ([1, 7] as $kod) {
    try {
        $katisi = ad_katisi($kod);
    } catch (UnhandledMatchError $hata) {
        $katisi = get_class($hata);
    }
    $varsayilan = ad_varsayilan($kod);
    printf("%d  katisi: %-20s varsayilan: %s\n", $kod, $katisi, $varsayilan);
}
```

```
1  katisi: bir                  varsayilan: bir
7  katisi: UnhandledMatchError  varsayilan: bilinmiyor
```

**KN42.** `1` girdisinde iki fonksiyon de `'bir'` döndürdü. `7` girdisinde `default` kolu olmayan fonksiyon `UnhandledMatchError` fırlattı, olan fonksiyon ise `'bilinmiyor'` döndürdü ve hiçbir şey bildirmedi. Bu durumda `default` iki şeyi birden yapar: eşleşmeyen girdiyi sessizce kabul eder ve girdinin eşleşmediğini programdan saklar. Hangi seçimin doğru olduğu, eşleşmeyen girdinin beklenen mi yoksa bir hata mı olduğuna bağlıdır; ölçüm yalnız seçimin sonucunu gösterir.

#### Özet

- Denetim akışının ölçü sınıfı durdurur: `match` karşılıksız kolda `UnhandledMatchError` fırlatır, `switch` ise sessizce geçer ve değişkeni önceki değerinde bırakır.
- `switch` etiketleri gevşek, `match` kolları katı eşitlikle karşılaştırır: `'1'` `switch`'te `case 1`'e gider, `match`'te `default`'a düşer.
- `break 2` ve `continue 2` iç içe döngülerde dış döngüyü etkiler; döngü değişkenleri döngüden sonra da tanımlı kalır.
- Referansla çalışan bir `foreach` döngüden sonra referansı bırakır; `unset` olmadan aynı adla ikinci bir döngü dizinin son elemanını ezer.
- `match`'e bir `default` kolu eklemek durdurmayı sessize çevirir ve eşleşmeyen girdiyi gizler.

#### Sonraki Adım

Karşılıksız bir dalın ne yaptığı görüldü: `switch` geçer, `match` durdurur. Fonksiyonlar dersi aynı soruyu çağrılara soruyor: bir fonksiyona beklenenden fazla argüman verildiğinde, fazlalık nereye gidiyor?

### Fonksiyonlar

Source: https://academia.sh/tr/kurslar/php-temelleri/fonksiyonlar

PHP fonksiyonlarında argüman ve dönüş davranışı: fazla argümanın sinyalsiz yutulması, eksik ve bilinmeyen argüman hataları, varsayılan değer ve static, değer ve referans geçişi, dönüş tipi ve ...$x ile fazlalığın görünür kılınması.

Denetim Akışı dersi, karşılıksız bir dalın `switch`'te sessizce geçtiğini, `match`'te ise yürütmeyi durdurduğunu ölçtü. Bu ders aynı soruyu çağrılara soruyor: bir fonksiyona beklenenden fazla argüman verildiğinde, fazlalık nereye gidiyor, eksik verildiğinde ne oluyor? Bu dersin ölçü sınıfı sessizdir: kullanıcının yazdığı bir fonksiyona fazla verilen argüman, hiçbir bildirim üretmeden yok sayılır.

PHP'de `function` anahtar sözcüğüyle bildirilen fonksiyon, `return` ile bir değer döndürür; parametrelerine tip ve varsayılan değer yazılabilir. Programlama Temelleri kursunda (M01/K02) fonksiyon kavramı kurulmuştu; burada yalnız PHP'nin bu kavrama nasıl davrandığı ölçülüyor. Bir değişikliğin çağırana ve başkasına görünüp görünmediği sorusu M11/K03'te nesneler için ayrıca ölçülmüştü; bu derste yalnız skaler ve dizi değerleri için gerektiği kadar anılıyor.

#### Fazla Argüman

Kullanıcı fonksiyonuna parametre sayısından fazla argüman verilirse PHP fazlayı hata saymaz: fonksiyon yine çalışır ve fazla argümanlar yok sayılır. Yerleşik fonksiyonlar bu konuda katıdır. Aşağıdaki dosya iki parametreli bir fonksiyona üç argüman verir ve aynı şeyi yerleşik `strlen()` ile dener; hata işleyicisi bir sayaç tutar.

```php
<?php
// fazla.php — kullanici fonksiyonu fazla argumani yutar, yerlesik reddeder
$bildirim = 0;
set_error_handler(function (int $seviye) use (&$bildirim): bool {
    $bildirim++;
    return true;
});

function iki_topla($a, $b)
{
    return $a + $b;
}

echo 'iki_topla(1, 2, 3):  ', iki_topla(1, 2, 3), "\n";

try {
    $uzunluk = strlen('abc', 'x');
    echo 'strlen(abc, x):     ', $uzunluk, "\n";
} catch (ArgumentCountError $hata) {
    echo 'strlen(abc, x):     ', get_class($hata), "\n";
}
echo 'bildirim: ', $bildirim, "\n";
```

```
iki_topla(1, 2, 3):  3
strlen(abc, x):     ArgumentCountError
bildirim: 0
```

**KN43.** `iki_topla(1, 2, 3)` ifadesi `3` döndürdü, yani üçüncü argüman toplamda yer almadı ve bunun için hiçbir şey söylenmedi: sayaç sıfır kaldı. Aynı fazlalığa yerleşik `strlen()` bir `ArgumentCountError` ile karşılık verdi. Bu asimetri bir tasarım kararıdır: kullanıcı fonksiyonlarında fazla argümanın hata sayılmaması, bir fonksiyonun sonradan fazladan bir isteğe bağlı parametre kazanabilmesine izin verir. Bedeli, bir çağrıdaki yazım hatasının yutulabilmesidir.

#### Eksik ve Bilinmeyen Argüman

Fazlalığın aksine eksik argüman bir hatadır: zorunlu bir parametre doldurulmazsa `ArgumentCountError` fırlar. PHP, argümanların konumla verilmesinin yanında ada göre de verilmesine izin verir (adlandırılmış argüman, named argument). Aşağıdaki dosya beş çağrı yapar: eksik, tam, adlandırılmış, bilinmeyen adlı ve zaten konumla doldurulmuş bir parametreyi yeniden adlandıran bir çağrı.

```php
<?php
// eksik.php — eksik argumani ArgumentCountError, bilinmeyen adi Error reddeder
function selamla(string $ad, string $unvan = 'bay'): string
{
    return $unvan . ' ' . $ad;
}

$cagrilar = [
    'selamla()'                      => fn() => selamla(),
    "selamla('Ada')"                 => fn() => selamla('Ada'),
    "selamla(unvan: 'dr', ad: 'Ada')" => fn() => selamla(unvan: 'dr', ad: 'Ada'),
    "selamla('Ada', soyad: 'X')"     => fn() => selamla('Ada', soyad: 'X'),
    "selamla('Ada', ad: 'Ece')"      => fn() => selamla('Ada', ad: 'Ece'),
];
foreach ($cagrilar as $ad => $cagri) {
    try {
        $yazi = var_export($cagri(), true);
    } catch (Error $hata) {
        $yazi = get_class($hata);
    }
    printf("%-34s%s\n", $ad, $yazi);
}
```

```
selamla()                         ArgumentCountError
selamla('Ada')                    'bay Ada'
selamla(unvan: 'dr', ad: 'Ada')   'dr Ada'
selamla('Ada', soyad: 'X')        Error
selamla('Ada', ad: 'Ece')         Error
```

**KN44.** Eksik argüman `ArgumentCountError` verdi. Varsayılan değeri olan `$unvan` atlanınca `'bay Ada'` üretildi. Adlandırılmış argümanlar sıradan bağımsız çalıştı: `unvan` önce yazıldığı hâlde `'dr Ada'` çıktı. Fonksiyonda tanımlı olmayan `soyad` adı ve zaten konumla doldurulmuş `ad` parametresi birer `Error` fırlattı. Yani fazla konumlu argüman sessiz, fazla adlandırılmış argüman gürültülüdür; adlandırma, kullanıcıya fazla argümanın yutulmasından daha sıkı bir sözleşme verir.

#### Varsayılan Değer ve static

Varsayılan değer her çağrıda yeniden hesaplanır; yani bir parametrenin varsayılanı olarak yazılan boş dizi, her çağrıda yeni bir boş dizidir ve önceki çağrının eklediği elemanları taşımaz. Bir fonksiyon içinde `static` ile bildirilen değişken ise çağrılar arasında yaşar. Aşağıdaki dosya iki davranışı yan yana koyar.

```php
<?php
// varsayilan.php — varsayilan her cagrida yenilenir, static cagrilar arasinda yasar
function ekle(int $eleman, array $liste = []): array
{
    $liste[] = $eleman;
    return $liste;
}

function say(): int
{
    static $sayac = 0;
    $sayac++;
    return $sayac;
}

echo 'ekle(1):  ', count(ekle(1)), "\n";
echo 'ekle(2):  ', count(ekle(2)), "\n";

say();
say();
echo 'say():    ', say(), "\n";
```

```
ekle(1):  1
ekle(2):  1
say():    3
```

**KN45.** `ekle` iki çağrıda da bir eleman döndürdü: varsayılan boş dizi her çağrıda yenilendi. `say` üçüncü çağrıda `3` verdi: `static` değişken fonksiyon her bittiğinde silinmedi. İkisi aynı ayrımı iki yönden gösterir: parametre ve yerel değişken çağrıya bağlıdır, `static` değişken fonksiyona bağlıdır. Hiçbiri bildirim üretmez.

#### Değer ve Referans

PHP argümanları varsayılan olarak değerle geçirir: fonksiyon kendi kopyası üzerinde çalışır ve çağıranın değişkenine dokunmaz. Parametre `&` ile bildirilirse fonksiyon çağıranın değişkenini değiştirir. Aşağıdaki dosya aynı değişikliği iki bildirimle yapar ve çağıranın dizisini sayar.

```php
<?php
// deger.php — deger kopyalar, referans cagiranin degiskenini degistirir
function degerle(array $dizi, int $sayi): void
{
    $dizi[] = 99;
    $sayi++;
}

function referansla(array &$dizi, int &$sayi): void
{
    $dizi[] = 99;
    $sayi++;
}

$liste = [1, 2];
$n = 10;

degerle($liste, $n);
echo 'degerle:    ', count($liste), ' eleman, n: ', $n, "\n";

referansla($liste, $n);
echo 'referansla: ', count($liste), ' eleman, n: ', $n, "\n";
```

```
degerle:    2 eleman, n: 10
referansla: 3 eleman, n: 11
```

**KN46.** Değerle geçirilen çağrıdan sonra dizi iki eleman, sayı on kaldı: fonksiyon kopyayı değiştirdi. Referansla geçirilen çağrıdan sonra dizi üç eleman, sayı on bir oldu. Bu ayrımın nesneler için sonucu M11/K03'te ölçülmüştü; burada gözlenen, dil `&` yazılmadıkça bir değişikliğin çağırana görünmediğidir. Referans parametre, fonksiyonun imzasından okunur: çağrıda hiçbir işaret yoktur.

#### Dönüş Değeri

`return` yazılmayan bir fonksiyon `null` döndürür. Dönüş tipi bildirilmişse PHP dönen değeri o tipe zorlar, tıpkı parametrelerde olduğu gibi: Değişkenler dersindeki zorlama kuralı burada da işler. Aşağıdaki dosya dönüş davranışını dört durumda gösterir.

```php
<?php
// donus.php — return yoksa null, donus tipi zorlar veya reddeder
function bos()
{
}

function sayiya(): int
{
    return '5';
}

function bozuk(): int
{
    return 'bes';
}

function bosluk(): void
{
}

echo 'bos():    ', var_export(bos(), true), "\n";
echo 'bosluk(): ', var_export(bosluk(), true), "\n";

$sonuc = sayiya();
echo 'sayiya(): ', var_export($sonuc, true), ' ', get_debug_type($sonuc), "\n";

try {
    bozuk();
} catch (TypeError $hata) {
    echo 'bozuk():  ', get_class($hata), "\n";
}
```

```
bos():    NULL
bosluk(): NULL
sayiya(): 5 int
bozuk():  TypeError
```

**KN47.** `return` olmayan fonksiyon `null` döndürdü, `void` bildirilmiş olan da. `: int` bildirimli fonksiyon `'5'` dizgisini sessizce `5`'e çevirdi; `'bes'` ise zorlanamadığı için `TypeError` verdi. Dönüş tipi bu yüzden parametre tipiyle aynı kuralı izler: sayısal dizgi zorlanır, değilse reddedilir.

#### Fonksiyonun Tanımlandığı An

PHP fonksiyonu bir dosya yürütülmeye başlamadan önce tanıyabilir, ama her fonksiyon için değil. Dosyanın en üst düzeyinde koşulsuz bildirilen fonksiyon, dosya okunurken tanınır ve bildirimden önceki satırda çağrılabilir. Bir `if` bloğunun içinde bildirilen fonksiyon ise yalnız o blok yürütüldüğünde var olur. Hiç bildirilmemiş bir fonksiyonun çağrısı ise bir `Error` fırlatır. Aşağıdaki dosya üç durumu `function_exists()` ile ölçer.

```php
<?php
// tanim.php — ust duzey fonksiyon onceden bilinir, kosullu olan calisinca tanimlanir
echo 'onceden:  ', var_export(function_exists('ust_duzey'), true), "\n";
echo 'kosullu:  ', var_export(function_exists('kosullu'), true), "\n";
echo 'cagri:    ', ust_duzey(), "\n";

function ust_duzey(): string
{
    return 'calisti';
}

if (true) {
    function kosullu(): string
    {
        return 'x';
    }
}
echo 'sonra:    ', var_export(function_exists('kosullu'), true), "\n";

try {
    yok_boyle_bir_sey();
} catch (Error $hata) {
    echo 'tanimsiz: ', get_class($hata), "\n";
}
```

```
onceden:  true
kosullu:  false
cagri:    calisti
sonra:    true
tanimsiz: Error
```

**KN48.** Koşulsuz bildirilen `ust_duzey` bildirim satırından önce de vardı ve çağrı çalıştı. Koşullu bildirilen `kosullu` blok yürütülmeden önce yoktu, yürütüldükten sonra oldu. Tanımsız bir ad ise `Error` verdi, yani durdurur sınıfındadır. Sessiz olan, çağrının hangi satırda yapıldığına bağlı olarak aynı adın bir yerde çalışıp başka yerde çalışmamasıdır: fonksiyonun varlığı dosyanın yapısına, koşullu olanın varlığı yürütme sırasına bağlıdır.

#### Kapanış ve Yakalanan Değişken

Anonim fonksiyon (closure) dış kapsamdaki değişkene kendiliğinden erişemez; `use` ile açıkça yakalaması gerekir. `use ($x)` değeri bildirim anında kopyalar, `use (&$x)` ise değişkenin kendisini bağlar. Ok fonksiyonu (arrow function) `fn` ise dış değişkeni oluşturulduğu anda değerle yakalar. Aşağıdaki dosya üç yakalama biçimini, yakalanan değişken sonradan değiştirildikten sonra çağırarak ölçer.

```php
<?php
// kapanis.php — use degeri yakalar, & degiskeni baglar, ok fonksiyonu olustugu andaki degeri alir
$x = 1;
$degerle = function () use ($x) {
    return $x;
};
$referansla = function () use (&$x) {
    return $x;
};
$ok = fn() => $x;

$x = 2;
echo 'use ($x):  ', $degerle(), "\n";
echo 'use (&$x): ', $referansla(), "\n";
echo 'fn:        ', $ok(), "\n";

$artir = fn() => $x++;
$artir();
echo 'fn icinde artirma sonrasi x: ', $x, "\n";
```

```
use ($x):  1
use (&$x): 2
fn:        1
fn icinde artirma sonrasi x: 2
```

**KN49.** `$x` iki yapıldıktan sonra değerle yakalayan iki biçim eski değeri, yani `1`'i gördü; yalnız `&` ile bağlanan `2`'yi gördü. Ok fonksiyonunun içindeki artırma dışarıdaki `$x`'i değiştirmedi: değişken ok fonksiyonuna kopya olarak girdi. Ayrım, değer ve referans geçişinin kapanışlardaki karşılığıdır ve bildirim üretmez: yanlış biçimi seçen kod hata vermez, eski bir değerle çalışır.

#### Boş Değer Kabul Eden Parametre

Bir parametreye `null` verilebilmesi iki ayrı şeydir: parametrenin tipinin `null` kabul etmesi ve parametrenin atlanabilmesi. `?int $x` null kabul eder ama zorunludur; `?int $x = null` hem null kabul eder hem atlanabilir. Birleşik tip (`int|string`) ise gevşek kipte hangi tipe zorlanacağını bir sıraya göre seçer. Aşağıdaki dosya altı çağrıyı bir tabloda ölçer.

```php
<?php
// bos_deger.php — ?int zorunludur, = null atlanabilir, birlesik tip zorlama sirasini secer
function zorunlu(?int $x)
{
    return var_export($x, true);
}

function atlanabilir(?int $x = null)
{
    return var_export($x, true);
}

function birlesik(int|string $x)
{
    return get_debug_type($x);
}

$cagrilar = [
    'zorunlu(null)'      => fn() => zorunlu(null),
    'zorunlu()'          => fn() => zorunlu(),
    'atlanabilir()'      => fn() => atlanabilir(),
    "birlesik('5')"      => fn() => birlesik('5'),
    'birlesik(5.0)'      => fn() => birlesik(5.0),
    'birlesik([])'       => fn() => birlesik([]),
    'birlesik(true)'     => fn() => birlesik(true),
];
foreach ($cagrilar as $ad => $cagri) {
    try {
        $yazi = $cagri();
    } catch (Error $hata) {
        $yazi = get_class($hata);
    }
    printf("%-20s%s\n", $ad, $yazi);
}
```

```
zorunlu(null)       NULL
zorunlu()           ArgumentCountError
atlanabilir()       NULL
birlesik('5')       string
birlesik(5.0)       int
birlesik([])        TypeError
birlesik(true)      int
```

**KN50.** `zorunlu(null)` çalıştı ama `zorunlu()` `ArgumentCountError` verdi: `?` null kabul etmeyi sağlar, atlanabilirliği sağlamaz. `atlanabilir()` `NULL` döndürdü. Birleşik tipte tam eşleşen değer olduğu gibi kaldı; tam eşleşme yoksa PHP bir sıra izledi ve sonucu sessizce seçti; dizi gibi hiçbir üyeye çevrilemeyen değer ise `TypeError` verdi. Bu, tip bildirimi dersindeki gevşek kip gözleminin birleşik tiplerdeki karşılığıdır.

#### Geri Çağrılan Fonksiyon

Bir fonksiyona argüman olarak verilen fonksiyon (geri çağrı, callback) çağıran tarafından, bildiriminin beklediğinden fazla argümanla çağrılabilir. Kullanıcı fonksiyonu bu fazlalığı yine sessizce yutar; yerleşik fonksiyon ise yutmaz. Aşağıdaki dosya `array_filter()`'ı iki argüman gönderen kipte, tek parametreli bir kapanışla çalıştırır ve aynı sınamayı yerleşik `strlen` ile `array_map()` üzerinden yapar.

```php
<?php
// geri_cagri.php — geri cagrilan kullanici fonksiyonu fazla argumani yutar, yerlesik yutmaz
$sayilar = [1, 2, 3, 4];

$tek_parametre = array_filter($sayilar, fn($v) => $v > 2, ARRAY_FILTER_USE_BOTH);
echo 'tek parametreli: ', implode(',', $tek_parametre), "\n";

$iki_parametre = array_filter($sayilar, fn($v, $k) => $k % 2 === 0, ARRAY_FILTER_USE_BOTH);
echo 'iki parametreli: ', implode(',', $iki_parametre), "\n";

try {
    $sonuc = array_map('strlen', ['a'], ['b']);
    echo 'strlen: ', count($sonuc), "\n";
} catch (ArgumentCountError $hata) {
    echo 'strlen: ', get_class($hata), "\n";
}
```

```
tek parametreli: 3,4
iki parametreli: 1,3
strlen: ArgumentCountError
```

**KN51.** İki argümanla çağrılan tek parametreli kapanış anahtarı hiç görmeden çalıştı ve `3,4` verdi. İki parametreli olan anahtarı kullandı ve çift indeksli iki elemanı, `1,3`'ü seçti. İki diziyle çağrılan `array_map()` ise `strlen`'e iki argüman gönderdi ve `ArgumentCountError` aldı. Aynı fazla argüman, geri çağrının türüne göre bir yerde sessiz, bir yerde durdurucu oldu.

#### Sınırlayıcı Ölçüm

Kullanıcı fonksiyonundaki sessiz yutmanın sınırı, fazlalığı görünür kılmanın mümkün olmasıdır. İki yol vardır: `func_num_args()` fonksiyon içinde gerçekte kaç argüman verildiğini söyler; `...$x` sözdizimi ise kalan bütün argümanları bir dizide toplar ve fazlalık o dizinin uzunluğunda görünür. Aşağıdaki dosya üç yolu aynı çağrıyla ölçer.

```php
<?php
// gorunur.php — func_num_args ve ...$x fazlaligi gorunur kilar
function iki_topla($a, $b)
{
    return $a + $b;
}

function say_argumani($a, $b)
{
    return func_num_args();
}

function topla(int ...$sayilar): int
{
    return array_sum($sayilar);
}

function kac_tane(...$x): int
{
    return count($x);
}

echo 'iki_topla(1, 2, 3):    ', iki_topla(1, 2, 3), "\n";
echo 'say_argumani(1, 2, 3): ', say_argumani(1, 2, 3), "\n";
echo 'topla(1, 2, 3):        ', topla(1, 2, 3), "\n";
echo 'kac_tane(1, 2, 3):     ', kac_tane(1, 2, 3), "\n";
```

```
iki_topla(1, 2, 3):    3
say_argumani(1, 2, 3): 3
topla(1, 2, 3):        6
kac_tane(1, 2, 3):     3
```

**KN52.** `iki_topla` üçüncü argümanı yine yuttu ve `3` verdi. `func_num_args()` aynı çağrıda `3` saydı: yutulan argüman, fonksiyonun içinden ölçülebilir. `...$sayilar` ile yazılan fonksiyon üç sayıyı da topladı ve `6` verdi; `...$x` ise fazlalığı bir dizi olarak teslim etti ve `count` `3` söyledi. Sessiz sınıf bu yüzden kaçınılmaz değildir: fonksiyon yazarı, fazlalığın görünür olacağı bir imza seçebilir.

#### Özet

- Fonksiyonların ölçü sınıfı sessizdir: kullanıcı fonksiyonuna verilen fazla konumlu argüman bildirim üretmeden yok sayılır, yerleşik fonksiyon ise `ArgumentCountError` fırlatır.
- Eksik zorunlu argüman `ArgumentCountError`, bilinmeyen ya da çakışan adlandırılmış argüman `Error` verir; adlandırma konumdan sıkıdır.
- Varsayılan değer her çağrıda yenilenir, `static` değişken çağrılar arasında yaşar.
- Argümanlar değerle geçer; `&` yalnız imzada yazılırsa çağıranın değişkeni değişir.
- `return` olmayan fonksiyon `null` döndürür; dönüş tipi sayısal dizgiyi zorlar, olmayanı reddeder.
- `func_num_args()` ve `...$x` yutulan fazlalığı görünür kılar.

#### Sonraki Adım

Fazla argümanın nereye gittiği görüldü: yok sayıldı ya da bir dizide toplandı. İndeksli ve İlişkisel Diziler dersi o diziye bakıyor: iki dizi birleştirildiğinde, aynı anahtarı taşıyan elemanlar için anahtarlar nasıl birleşiyor?

## Diziler ve Dizgiler

Tek bir sıralı eşlemenin hem liste hem sözlük olarak kullanılması, anahtarların normalleşmesi, iç içe dizilerde eksik anahtar, dizi fonksiyonlarının bulamadığını nasıl bildirdiği ve dizgi işlemenin bayt üzerinden yürümesi.

### İndeksli ve İlişkisel Diziler

Source: https://academia.sh/tr/kurslar/php-temelleri/indeksli-ve-iliskisel-diziler

Tek bir dizi yapısının hem liste hem sözlük olarak kullanılması; '1', 1 ve true anahtarlarının tek anahtara inmesi; ondalık anahtarın kesilmesi; normalleşmenin yalnız kanonik ondalık tam sayı dizgilerine uygulanması.

Fonksiyonlar dersi, fazladan verilen bir argümanın hiçbir sinyal vermeden atıldığını ölçtü: `iki_topla(1, 2, 3)` sonuç olarak `3` üretti ve çağıran tarafa hiçbir haber gitmedi. Bu ders aynı sessizliğin bir veri yapısında da çıkıp çıkmadığını soruyor: aynı diziye farklı yazımlarla verilen anahtarlar birbirine karışırsa bunu kim söylüyor? Kursun ölçü sorusu değişmiyor — bu sorun hangi kanaldan duyuluyor — ve sınıf adları bu derste yalnız kullanılıyor: **sessiz**, **dönüş**, **uyarı**, **durdurur**.

PHP'de `array` tek bir yapıdır ve iki kullanımı karşılar. Anahtarları ardışık tam sayılar olan dizi indeksli dizi (indexed array) olarak okunur; anahtarları dizgi olan dizi ilişkisel dizi (associative array) olarak. İkisi arasında tip farkı yoktur, fark yalnız anahtarların ne olduğundadır. Programlama Temelleri kursunda (M01/K02) kurulan değişken ve tip kavramları burada olduğu gibi geçerlidir; yeni olan, anahtarın yazıldığı gibi saklanmayıp bir kuraldan geçmesidir.

#### Tek Yapıda İki Kullanım

Aşağıdaki dosya aynı yapıyı üç biçimde kullanıyor: liste, sözlük ve ikisinin karışımı. Her dizinin anahtarları `array_keys` ile yan yana basılıyor.

```php
<?php
// diziler.php — ayni yapi hem liste hem sozluk olarak kullanilir
$liste = ['elma', 'armut', 'ayva'];
$liste[] = 'nar';

$kisi = ['ad' => 'Ada', 'yas' => 36];
$kisi['sehir'] = 'Izmir';

$karisik = ['bir', 'iki', 'ozel' => 'x'];
$karisik[] = 'uc';

$sirali = [3 => 'c', 1 => 'a'];
$sirali[] = 'd';

$silinmis = ['x', 'y', 'z'];
unset($silinmis[1]);
$silinmis[] = 'w';

echo 'liste: ', implode(',', array_keys($liste)), "\n";
echo 'kisi: ', implode(',', array_keys($kisi)), "\n";
echo 'karisik: ', implode(',', array_keys($karisik)), "\n";
echo 'sirali: ', implode(',', array_keys($sirali)), "\n";
echo 'silinmis: ', implode(',', array_keys($silinmis)), "\n";
echo 'liste mi: ', var_export(array_is_list($liste), true), ' ',
    var_export(array_is_list($kisi), true), ' ',
    var_export(array_is_list($silinmis), true), "\n";
```

```
liste: 0,1,2,3
kisi: ad,yas,sehir
karisik: 0,1,ozel,2
sirali: 3,1,4
silinmis: 0,2,3
liste mi: true false false
```

Köşeli parantezle ekleme, anahtar vermeden eleman ekler ve PHP o elemana bir sonraki tam sayı anahtarı verir: karışık dizide `ozel` anahtarından sonra eklenen `'uc'` değeri `2` anahtarını aldı. `sirali` satırı sıranın anahtara değil eklenmeye bağlı olduğunu gösteriyor: `3` anahtarı önce yazıldığı için önce geliyor, ardından gelen `1` ve eklenen `4`. `silinmis` satırı bir eleman silindiğinde anahtarların yeniden numaralanmadığını gösteriyor; `array_is_list` bu yüzden `false` dönüyor. İndeksli görünen bir dizi, bir `unset`'ten sonra indeksli olmaktan çıkabilir ve bunu hiçbir şey bildirmez.

**DZ1.** Bu derste "sıra", elemanların eklenme sırasıdır; `foreach` dizi üzerinde bu sırayla dolaşır. Sıranın anahtar değerinden bağımsız olduğu yukarıdaki koşumla gösterilir ve sayısal sıralama gerektiren her iş açıkça sıralama yapar. Dizinin başka bir değişkene atanınca kopyalanıp kopyalanmadığı ise bu dersin sorusu değildir: o, M11/K03'te ölçülen "bu değişikliği kim görüyor" sorusudur ve burada sorulmaz.

#### Anahtar Normalleşmesi

Bir dizi anahtarı yalnız `int` ya da `string` olabilir. Başka bir tipte yazılan anahtar bu ikisinden birine **normalleşir** (normalization): `true` anahtarı `1` olur, ondalık bir sayı tam sayıya kesilir, ondalık tam sayı biçimindeki bir dizgi tam sayıya dönüşür. Tanım bundan ibarettir; ilginç olan, bu dönüşümün yazım düzeyinde görünmemesidir. Aşağıdaki dosya `kanal.php`'yi çeker; kanal sınıflandırıcısı kursun ortak yardımcısıdır ve bu derste kesiti kullanılıyor.

```php
<?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
<?php
// normallesme.php — farkli yazilan anahtarlarin tek anahtara inmesi
require 'kanal.php';

$gozlemler = [
    'uc yazim, tek anahtar' => fn() => ['1' => 'a', 1 => 'b', true => 'c'],
    'ondalik anahtar'       => function () {
        $anahtar = 1.7;
        return [$anahtar => 'x'];
    },
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-22s%-9s%s\n", $ad, $sinif, $kanit);
}

$dizi = ['1' => 'a', 1 => 'b', true => 'c'];
echo 'kalan anahtar: ', array_key_first($dizi), ', deger: ', $dizi[1], "\n";
```

```
uc yazim, tek anahtar sessiz   sonuc dizi(1)
ondalik anahtar       uyari    E_DEPRECATED, sonuc dizi(1)
kalan anahtar: 1, deger: c
```

İlk satır kursun `anahtar normallesmesi` gözlemidir ve **sessiz** sınıftadır. `'1'`, `1` ve `true` üç ayrı yazımdır; üçü de `1` anahtarına indi, dizide tek eleman kaldı ve o elemanın değeri son yazılan `'c'` oldu. `dinle()` soruyu sırayla sordu: fırlatma yok, uyarı bildirilmedi, sonuç `false` ya da `null` değil; geriye sessizlik kalıyor. Sorun yalnız sonuca bakılarak görülebilir — burada `dizi(1)` yerine `dizi(3)` beklemiş biri, eksik iki elemanı hiçbir yerden duymadı. Bu, fazla argümanın atılmasıyla aynı sınıftır; fark, burada atılanın bir değer olması ve anahtarın onu ezmesidir.

İkinci satır aynı mekanizmanın başka bir yüzü: `1.7` anahtarı `1`'e kesildi ve bu kez bir hata seviyesi bildirildi. `E_DEPRECATED` kullanımdan kaldırma seviyesidir ve yürütme sürüyor; sınıf bu yüzden **uyarı**. `true` ve `'1'` değeri kaybetmeden `1`'e iniyor, `1.7` ise ondalık kısmı atıyor. Sessizliğin sınırını bilgi kaybı çiziyor: bilgi kaybeden dönüşüm duyuruluyor, kayıpsız görünen dönüşüm duyurulmuyor.

**DZ2.** `E_DEPRECATED` bu derste `uyarı` sınıfına yazılır, çünkü çekirdeğin tanımı yalnız iki şey soruyor: bir hata seviyesi bildirildi mi, yürütme sürdü mü. Seviyenin anlamı — davranışın ileride değişebileceği — sınıfı değiştirmez; yalnız bu uyarının gelecekte bir `durdurur` olabileceğini söyler. Ders bu geleceği ölçmüyor.

#### Anahtar Tipleri ve Birleştirme

Normalleşme yalnız dizgi ile tam sayı arasında işlemez; mantıksal değerler, boş değer ve ondalık sayılar da anahtar yerine yazılabilir. Hangi anahtara indiklerini ölçmek için her aday bir değişkende tutuluyor ve tek elemanlı bir dizinin anahtarı olarak kullanılıyor.

```php
<?php
// tipler.php — farkli tipteki adaylarin indigi anahtar
require 'kanal.php';

function anahtar_of(mixed $aday): string
{
    $dizi = [$aday => 'x'];
    return var_export(array_key_first($dizi), true);
}

$adaylar = [
    'null'        => null,
    'false'       => false,
    'true'        => true,
    'tam ondalik' => 2.0,
    'bos dizgi'   => '',
];

foreach ($adaylar as $ad => $aday) {
    [$sinif, $kanit] = dinle(fn() => anahtar_of($aday));
    printf("%-12s%-9s%s\n", $ad, $sinif, $kanit);
}

[$sinif, $kanit] = dinle(function () {
    $bos = null;
    return count([$bos => 'a', '' => 'b']);
});
echo 'null ve bos dizgi birlikte: ', $sinif, ' ', $kanit, "\n";
```

```
null        uyari    E_DEPRECATED, sonuc string ''
false       sessiz   sonuc string 0
true        sessiz   sonuc string 1
tam ondalik sessiz   sonuc string 2
bos dizgi   sessiz   sonuc string ''
null ve bos dizgi birlikte: uyari E_DEPRECATED, sonuc int 1
```

`null` anahtarı boş dizgiye iner ve `E_DEPRECATED` bildirir (**uyarı**); `false` `0`'a, `true` `1`'e, kesri olmayan `2.0` ise `2`'ye iner ve bu üçü ile boş dizgi **sessiz**dir. Son satır sonucu bağlıyor: `null` ile `''` aynı anahtardır ve ikisini birlikte yazan bir dizi tek eleman taşır. Bu dönüşümlerin çoğunda bilgi kaybı yok gibi görünür; oysa `null` ile boş dizgiyi ayrı veri olarak taşıyan bir kaynak, diziye girdiğinde ayrımı yitirir. Kesri olan ondalık sayı ise önceki bölümde ölçüldüğü gibi uyarı ile kesilir.

**DZ3.** Tablodaki beş aday dilin dönüşüm kuralını örnekler; anahtar olarak geçerli olmayan tipler (başka bir dizi, bir nesne) bu dersin dışındadır ve burada ölçülmez. Ders hangi tipin hangi anahtara indiğini yazılı kuraldan değil, koşturulan çıktıdan okur.

İki dizi birleştirilirken anahtarların çakışması ikinci bir normalleşme noktasıdır. Aynı iki diziye üç ayrı birleştirme uygulandığında anahtarların yazgısı da üç ayrı çıkar.

```php
<?php
// birlestir.php — ayni iki diziyi uc farkli yolla birlestirmek
$a = ['ad' => 'Ada', 5 => 'x', 6 => 'y'];
$b = ['ad' => 'Can', 5 => 'z'];

echo 'toplama: ', json_encode($a + $b), "\n";
echo 'array_merge: ', json_encode(array_merge($a, $b)), "\n";
echo 'array_replace: ', json_encode(array_replace($a, $b)), "\n";
echo 'yayma: ', json_encode([...$a, ...$b]), "\n";
echo 'anahtarlar: ', implode(',', array_keys(array_merge($a, $b))), "\n";
```

```
toplama: {"ad":"Ada","5":"x","6":"y"}
array_merge: {"ad":"Can","0":"x","1":"y","2":"z"}
array_replace: {"ad":"Can","5":"z","6":"y"}
yayma: {"ad":"Can","0":"x","1":"y","2":"z"}
anahtarlar: ad,0,1,2
```

Toplama işleci sol dizinin değerini korur ve yalnız sağdaki yeni anahtarları ekler: `ad` anahtarı `'Ada'` kaldı, `5` anahtarı `'x'` kaldı. `array_merge` dizgi anahtarlarında sağ değeri alır (`'Can'`) ama tam sayı anahtarları yeniden numaralar: `5` ve `6` anahtarları kayboldu, iki tarafın üç elemanı da korunarak `0, 1, 2` anahtarlarına indi. `array_replace` ise tam sayı anahtarlarına dokunmadan sağ değeri yazar. Yayma sözdizimi `array_merge` ile aynı sonucu verir. Dört yazım, aynı iki dizi için üç farklı sonuç üretti ve hiçbiri bir seviye bildirmedi: sınıf **sessiz**.

**DZ4.** `array_merge` ile toplama arasındaki fark bu derste yalnız anahtar çakışmasıyla ölçülür. Hangisinin hızlı ya da hangisinin tercih edilmesi gerektiği ölçülmez; ders bir fonksiyonu diğerine üstün tutmaz, yalnız aynı girdinin farklı sonuç verdiğini gösterir.

#### Anahtar Var mı Sorusu

Bir anahtarın dizide bulunup bulunmadığı iki ayrı soruyla sorulabilir ve sorular aynı cevabı vermez. `isset` anahtarın var ve değerinin `null` olmadığını sorar, `array_key_exists` yalnız anahtarın var olup olmadığını, `??` ise değer yoksa ya da `null` ise yedeği verir.

```php
<?php
// varmi.php — isset, array_key_exists ve ?? farki
$veri = ['dolu' => 'x', 'bos' => null, 'sifir' => 0];

foreach (['dolu', 'bos', 'sifir', 'yok'] as $anahtar) {
    printf(
        "%-6s isset=%-5s var=%-5s birlesim=%s\n",
        $anahtar,
        var_export(isset($veri[$anahtar]), true),
        var_export(array_key_exists($anahtar, $veri), true),
        var_export($veri[$anahtar] ?? 'yedek', true)
    );
}
```

```
dolu   isset=true  var=true  birlesim='x'
bos    isset=false var=true  birlesim='yedek'
sifir  isset=true  var=true  birlesim=0
yok    isset=false var=false birlesim='yedek'
```

`bos` satırı ayrımı gösteriyor: anahtar var, değer `null`. `isset` bunu yok sayar, `array_key_exists` var sayar, `??` yedeği verir. `sifir` satırında üçü de değeri var kabul eder; `0` boş değer değildir. `yok` satırında anahtarın gerçekten bulunmadığı durumda `isset` ve `array_key_exists` ikisi de `false` verir. Üç sorgunun üçü de uyarı üretmeden bir `bool` ya da yedek değer döndürdü; bir anahtarın eksikliğini uyarıdan duymak isteyen kod yalın okumayı seçmek zorundadır, ve yalın okuma bir sonraki derste ölçülür.

#### Sınırlayıcı Ölçüm

Normalleşme her dizgiye uygulanmaz. Hangi dizgilerin tam sayı anahtara dönüştüğünü ölçmek için dizgi anahtarları tek tek denenebilir: her biri tek elemanlı bir dizinin anahtarı yapılıyor ve `array_key_first` o anahtarın tipini söylüyor.

```php
<?php
// sinir.php — hangi dizgi anahtarlar tam sayiya donusur
$adaylar = ['0', '1', '-1', '01', '1.5', '-0', ' 1'];

foreach ($adaylar as $aday) {
    $dizi = [$aday => 'x'];
    $anahtar = array_key_first($dizi);
    printf("%-6s%s\n", "'" . $aday . "'", get_debug_type($anahtar));
}

$iki = ['1' => 'a', '01' => 'b'];
echo 'iki yazim, eleman sayisi: ', count($iki), "\n";
```

```
'0'   int
'1'   int
'-1'  int
'01'  string
'1.5' string
'-0'  string
' 1'  string
iki yazim, eleman sayisi: 2
```

Kural çıktıdan okunuyor: yalnız **ondalık, kanonik tam sayı biçimindeki** dizgiler tam sayı anahtara dönüşür. `'1'` ve `'-1'` tam sayı olur; baştaki sıfır (`'01'`), ondalık nokta (`'1.5'`), eksi işaretli sıfır (`'-0'`) ve boşluk (`' 1'`) taşıyan dizgiler dizgi kalır. Bu ayrım, önceki bölümdeki sessizliğin sınırını çiziyor: `'1'` ile `1` aynı anahtarsa `'01'` farklı bir anahtardır ve son satırdaki iki yazım bu kez iki eleman bırakıyor. Normalleşme "aynı sayıyı farklı yazanları birleştirme" kuralı değil, "kanonik yazımı tam sayıya çevirme" kuralıdır; aynı sayıyı iki biçimde yazan bir veri kaynağı, dizide iki ayrı eleman üretir ve bunu da hiçbir sinyal bildirmez.

**DZ5.** Bu bölümdeki tipler ve anahtarlar dilin kendi kuralının sonucudur; tek bir sayıya değil sınıfa dayanır. Ölçülen yedi aday dilin belgelediği sınıfı örnekler ve tamamlayıcı olmayı iddia etmez. Bir dizgi anahtar tam sayıya dönüşmüyorsa, bunun nedeni o yapının kanonik ondalık biçimde olmamasıdır; ders başka bir tabana ya da çok büyük tam sayılara geçmiyor.

#### Özet

- PHP'de `array` tek yapıdır: anahtarları ardışık tam sayılar olan dizi indeksli, dizgi anahtarlı olan ilişkisel dizi olarak okunur; sıra eklenme sırasıdır.
- `unset` anahtarları yeniden numaralamaz; indeksli görünen dizi `array_is_list` ile sınanmadıkça indeksli kalıp kalmadığı bildirilmez.
- `'1'`, `1` ve `true` tek anahtara normalleşir ve dizide tek eleman kalır; bu dersin ölçü sınıfı **sessiz**dir (`anahtar normallesmesi`).
- `1.7` gibi ondalık anahtar kesilir ve `E_DEPRECATED` bildirilir; bilgi kaybeden dönüşüm **uyarı** kanalından duyulur.
- Normalleşme yalnız ondalık, kanonik tam sayı biçimindeki dizgilere uygulanır; `'01'`, `'1.5'` ve `'-0'` dizgi kalır.

#### Sonraki Adım

Bu ders tek düzeyli bir dizinin anahtarlarını ölçtü. Bir dizinin elemanı başka bir dizi olduğunda anahtar tek değil bir yol olur, ve yolun bir halkası eksikse `null` gelir. Çok Boyutlu Diziler dersi eksik bir iç anahtarın hangi kanaldan duyulduğunu ve aynı yola yazmanın neden başka bir kanaldan duyulduğunu ölçüyor.

### Çok Boyutlu Diziler

Source: https://academia.sh/tr/kurslar/php-temelleri/cok-boyutlu-diziler

Dizilerin dizilerle iç içe kurulması; eksik bir iç anahtarın E_WARNING ile null vermesi; null üzerinde dizi erişimi; boşluk birleştirme işlecinin yolun tamamını sessize çevirmesi; okuma ile yazmanın ayrı kanal olması.

İndeksli ve İlişkisel Diziler dersi, aynı anahtarın farklı yazımlarının tek elemana inmesini ve bunun hiçbir kanaldan duyulmadığını ölçtü. Anahtar tek bir değerdi. Bir elemanın kendisi başka bir dizi olduğunda erişim bir yola dönüşür: `$a[0]['soyad']` iki ayrı arama yapar ve ikincisinin dayanağı birincinin sonucudur. Bu ders yolun bir halkası eksik olduğunda bunun nereden duyulduğunu ölçüyor.

Çok boyutlu dizi (multidimensional array) ayrı bir tip değildir; elemanları dizi olan sıradan bir dizidir. Derinlik için dilde bir sınır konmaz, yalnız bellek sınırlar. Ders sınıf adlarını önceki derslerdeki anlamıyla kullanır: **sessiz**, **dönüş**, **uyarı**, **durdurur**.

#### İç İçe Veri Modelleme

Bir öğrenci listesi, her öğrencinin adı ve not tablosuyla birlikte iç içe bir yapıya oturur. Aşağıdaki dosya bu yapıyı kuruyor, iki düzeyden okuyor ve `count` ile düz sayımı özyinelemeli sayımla karşılaştırıyor.

```php
<?php
// ogrenciler.php — dizi icinde dizi ile veri modelleme
$ogrenciler = [
    ['ad' => 'Ada', 'notlar' => ['mat' => 80, 'fen' => 90]],
    ['ad' => 'Can', 'notlar' => ['mat' => 70, 'fen' => 60]],
];

foreach ($ogrenciler as $sira => $ogrenci) {
    $ortalama = array_sum($ogrenci['notlar']) / count($ogrenci['notlar']);
    echo $sira, ' ', $ogrenci['ad'], ' ', $ortalama, "\n";
}

echo 'Can fen: ', $ogrenciler[1]['notlar']['fen'], "\n";
echo 'duz sayim: ', count($ogrenciler), "\n";
echo 'ozyinelemeli sayim: ', count($ogrenciler, COUNT_RECURSIVE), "\n";
```

```
0 Ada 85
1 Can 65
Can fen: 60
duz sayim: 2
ozyinelemeli sayim: 10
```

Her düzey kendi anahtar uzayına sahiptir. Dış dizi indeksli, öğrenci kaydı ilişkisel, not tablosu yine ilişkisel; `$ogrenciler[1]['notlar']['fen']` üç anahtarın sırayla uygulanmasıdır. `count` yalnız dış düzeyi sayar, `COUNT_RECURSIVE` bayrağı her iç diziyi hem bir eleman olarak hem içeriğiyle sayar; bu yüzden iki sayı arasındaki fark yapının derinliğini yansıtır, veri sayısını değil.

**DZ6.** Bu derste kayıtlar sabit biçimlidir: her öğrencide `ad` ve `notlar` anahtarı vardır. Yolun bozulduğu durumlar bir sonraki bölümde ayrıca kurulur. Bir kaydın biçiminin kullanıcıdan ya da dış bir kaynaktan geldiği durumlar M16/K01'in konusudur ve burada ele alınmaz.

#### Eksik İç Anahtar

Yol bir halkada kopunca ne olduğu, dinleyicinin kanal sırasıyla ölçülür. Dersin ortak yardımcısı `kanal.php` aşağıda tam metniyle veriliyor; çıktı bloğu olmadığı için yalnız diske yazılır.

```php
<?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
<?php
// eksik.php — eksik ic anahtar ve null uzerinde dizi erisimi
require 'kanal.php';

$kayitlar = [['ad' => 'Ada']];

$gozlemler = [
    'eksik ic anahtar'   => fn() => $kayitlar[0]['soyad'],
    'eksik dis anahtar'  => fn() => $kayitlar[5]['ad'],
    'null uzerinde erisim' => fn() => $kayitlar[5]['ad']['x'],
    'dizgi uzerinde erisim' => fn() => $kayitlar[0]['ad']['x'],
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-23s%-9s%s\n", $ad, $sinif, $kanit);
}
```

```
eksik ic anahtar       uyari    E_WARNING, sonuc null
eksik dis anahtar      uyari    E_WARNING, sonuc null
null uzerinde erisim   uyari    E_WARNING, sonuc null
dizgi uzerinde erisim  durdurur TypeError
```

Birinci satır kursun `eksik ic anahtar` gözlemidir: `$kayitlar[0]['soyad']` değeri `null` verdi ve `E_WARNING` bildirdi. Sınıf **uyarı**. Yürütme sürdü; sonuç `null` olduğu için çağıran kod hata olduğunu ancak değerle birlikte gelen uyarıdan anlar. Önceki dersin sessizliğiyle bu ders arasındaki ayrım budur: anahtar yazımındaki kayıp duyulmuyordu, anahtar okumadaki kayıp duyuluyor.

İkinci satır dış düzeyden eksik bir indeks için aynı sonucu veriyor, üçüncü satır ise zincirin ilk halkası zaten `null` iken ikinci erişimin de aynı seviyeyi bildirdiğini gösteriyor: `null` üzerinde dizi erişimi hata değil uyarıdır ve `null` yine `null` döner. Zincir ne kadar uzarsa uzasın bir kopukluk sonrasındaki her erişim sessizce `null`'a çıkar; kopukluğun hangi halkada olduğu uyarı metninden okunur, sonuçtan okunmaz. Dördüncü satır sınırı gösteriyor: yol bir dizgiye vardığında ve dizgiye `'x'` gibi bir dizgi anahtarı uygulandığında `null` dönmez, `TypeError` fırlar; sınıf **durdurur**. Uyarı kanalı yalnız `null` ya da eksik anahtar için işler, tipi yanlış bir değere uygulanan erişim için işlemez.

**DZ7.** Uyarının ileti metni ve satır bilgisi ortama bağlıdır ve ders onları basmaz; yalnız seviye adı (`E_WARNING`) ve sonucun tipi basılır. Aynı kopukluğun bir ayarla durduran bir hataya çevrilmesi mümkündür, ancak ders hiçbir ini değerine güvenmez ve bu yolu ölçmez.

#### Boşluk Birleştirme ile Sessizlik

Boşluk birleştirme işleci (null coalescing, `??`) sol tarafın değerini, o `null` ya da tanımsız ise sağ tarafı verir. Eksik yol üzerinde uygulandığında zincirin her halkasındaki uyarıyı bastırır.

```php
<?php
// birlestirme.php — ?? eksik yolu sessize cevirir
require 'kanal.php';

$veri = ['a' => 1];
$kayitlar = [['ad' => 'Ada']];

$gozlemler = [
    'uyarili okuma'     => fn() => $veri['b'],
    'birlestirmeli'     => fn() => $veri['b'] ?? 0,
    'derin birlestirme' => fn() => $kayitlar[3]['notlar']['fen'] ?? -1,
    'var olan deger'    => fn() => $kayitlar[0]['ad'] ?? 'yok',
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-19s%-9s%s\n", $ad, $sinif, $kanit);
}
```

```
uyarili okuma      uyari    E_WARNING, sonuc null
birlestirmeli      sessiz   sonuc int 0
derin birlestirme  sessiz   sonuc int -1
var olan deger     sessiz   sonuc string Ada
```

Aynı eksik anahtar iki kanalda duyuldu: yalın okuma **uyarı**, `??` ile **sessiz**. Üçüncü satır daha önemli: `??` yalnız son halkayı değil, yolun tamamını sarar; üç düzeyli bir yolda herhangi bir halka eksikse sonuç varsayılan değerdir ve hiçbir kanaldan haber gelmez. Bu işlecin amacıdır, ancak amaç sorunun kaybolması anlamına da gelir: yol yanlış yazılmışsa (`'not'` yerine `'notlar'`), `??` yazımı hatayı da aynı sessizlikle örter.

#### Kayıt Listesi ile Çalışmak

İç içe diziler pratikte çoğu zaman bir kayıt listesi biçimindedir: dış dizi indeksli, her eleman aynı anahtarları taşıyan ilişkisel bir dizi. Bu biçim için üç sık iş vardır: bir sütunu çekmek, kayıtları bir anahtara göre sıralamak, kayıtları o sütuna göre indekslemek.

```php
<?php
// kayitlar.php — kayit listesinde sutun, siralama ve indeksleme
$kayitlar = [
    ['id' => 7, 'ad' => 'Can', 'yas' => 31],
    ['id' => 3, 'ad' => 'Ada', 'yas' => 36],
    ['id' => 9, 'ad' => 'Efe', 'yas' => 24],
];

echo 'adlar: ', implode(',', array_column($kayitlar, 'ad')), "\n";
echo 'id ile indeksli: ', implode(',', array_keys(array_column($kayitlar, 'ad', 'id'))), "\n";

usort($kayitlar, fn($sol, $sag) => $sol['yas'] <=> $sag['yas']);
echo 'yasa gore: ', implode(',', array_column($kayitlar, 'ad')), "\n";

$kayitlar[1]['yas'] += 1;
echo 'guncel yas: ', $kayitlar[1]['yas'], "\n";
echo 'ortalama yas: ', array_sum(array_column($kayitlar, 'yas')) / count($kayitlar), "\n";
```

```
adlar: Can,Ada,Efe
id ile indeksli: 7,3,9
yasa gore: Efe,Can,Ada
guncel yas: 32
ortalama yas: 30.666666666667
```

`array_column` bir sütunu düz bir diziye çevirir; üçüncü argüman verilirse sonucun anahtarları o sütundan gelir. `usort` kayıtları verilen karşılaştırmaya göre yeniden diziler ve anahtarları yeniden numaralar. Ardından `$kayitlar[1]['yas'] += 1` yazımı iki düzeyli bir yola yazıyor; yol var olduğu için hiçbir şey bildirilmiyor. Kayıt listesi ile çalışan kodun çoğu bu üç fonksiyonla ve iki düzeyli indekslemeyle yazılır; hata, kayıtlar aynı biçimde olmadığında ortaya çıkar.

**DZ8.** Kayıtların biçimi bu derste kod içinde sabittir. Aynı listeyi bir dosyadan ya da bir istekten okuyan kodun kayıt biçimini doğrulaması gerekir; bu doğrulama burada yapılmaz; kayıt biçimini sınayan fonksiyon bu dersin kapsamı dışındadır.

Bir kaydın bir anahtarı eksik olduğunda, sütunu çekmek ile her kayıttan anahtarı tek tek okumak ayrı kanallardan duyulur.

```php
<?php
// eksiksutun.php — array_column eksik anahtari sessizce atlar
require 'kanal.php';

$kayitlar = [
    ['ad' => 'Ada', 'soyad' => 'Yilmaz'],
    ['ad' => 'Can'],
    ['ad' => 'Efe', 'soyad' => 'Kaya'],
];

$gozlemler = [
    'array_column' => fn() => array_column($kayitlar, 'soyad'),
    'tek tek okuma' => fn() => array_map(fn($k) => $k['soyad'], $kayitlar),
    'birlesimli okuma' => fn() => array_map(fn($k) => $k['soyad'] ?? '-', $kayitlar),
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-17s%-9s%s\n", $ad, $sinif, $kanit);
}

echo 'sutun uzunlugu: ', count(array_column($kayitlar, 'soyad')), "\n";
echo 'kayit sayisi: ', count($kayitlar), "\n";
```

```
array_column     sessiz   sonuc dizi(2)
tek tek okuma    uyari    E_WARNING, sonuc dizi(3)
birlesimli okuma sessiz   sonuc dizi(3)
sutun uzunlugu: 2
kayit sayisi: 3
```

`array_column` eksik anahtarı olan kaydı sessizce atladı: sütun üç yerine iki eleman taşıyor ve **sessiz** sınıfındadır. Tek tek okuma aynı kayıtta `E_WARNING` verdi ve **uyarı** sınıfındadır; `??` ile okuma ise yedek değeri verip sessizleşti. Üç yazım aynı eksikliğe üç farklı tepki gösterdi. Dikkat edilecek yer şudur: `array_column` sonucu ile kayıt listesi artık aynı uzunlukta değildir, bu yüzden ikisini indeksle eşleştiren bir kod sessizce yanlış kaydı eşleştirir.

#### Dizi Açma ve Özyinelemeli Birleştirme

İç içe bir dizinin parçalarını değişkenlere dağıtan dizi açma (destructuring) yazımı, yolun eksik olduğu durumda da kendi kanalını kullanır. İç içe iki dizinin birleştirilmesi ise düz birleştirmeden ayrı fonksiyonlarla yapılır.

```php
<?php
// acma.php — dizi acma ve ozyinelemeli birlestirme
require 'kanal.php';

$gozlemler = [
    'tam acma'       => fn() => (function () { [$x, $y] = [1, 2]; return $x + $y; })(),
    'eksik indeks'   => fn() => (function () { [$x, $y] = [1]; return $y; })(),
    'eksik anahtar'  => fn() => (function () { ['ad' => $ad, 'yas' => $yas] = ['ad' => 'Ada']; return $yas; })(),
    'ic ice acma'    => fn() => (function () { [[$p, $q], [$r]] = [[1, 2], [3]]; return $p + $q + $r; })(),
    'eksik ic acma'  => fn() => (function () { [[$p], [$r]] = [[1]]; return $r; })(),
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-15s%-9s%s\n", $ad, $sinif, $kanit);
}

$sol = ['a' => ['x' => 1, 'y' => 2], 'b' => 3];
$sag = ['a' => ['y' => 20, 'z' => 30], 'b' => 4];
echo 'array_merge: ', json_encode(array_merge($sol, $sag)), "\n";
echo 'array_replace: ', json_encode(array_replace($sol, $sag)), "\n";
echo 'array_replace_recursive: ', json_encode(array_replace_recursive($sol, $sag)), "\n";
echo 'array_merge_recursive: ', json_encode(array_merge_recursive($sol, $sag)), "\n";
```

```
tam acma       sessiz   sonuc int 3
eksik indeks   uyari    E_WARNING, sonuc null
eksik anahtar  uyari    E_WARNING, sonuc null
ic ice acma    sessiz   sonuc int 6
eksik ic acma  uyari    E_WARNING, sonuc null
array_merge: {"a":{"y":20,"z":30},"b":4}
array_replace: {"a":{"y":20,"z":30},"b":4}
array_replace_recursive: {"a":{"x":1,"y":20,"z":30},"b":4}
array_merge_recursive: {"a":{"x":1,"y":[2,20],"z":30},"b":[3,4]}
```

Dizi açma, eksik indekste ya da eksik anahtarda `E_WARNING` verir ve değişken `null` kalır; yani doğrudan indeksle okumanın uyarı sınıfı açmada da geçerlidir. Tam eşleşen açma ve iç içe açma sessizdir.

Özyinelemeli birleştirmede dört fonksiyon üç ayrı sonuç verir. `array_merge` ve `array_replace` iç dizileri bütün olarak değiştirir, `x` anahtarı kaybolur. `array_replace_recursive` iç dizilere iner ve `x`'i korur. `array_merge_recursive` ise çakışan skaler değerleri dizi hâline getirir: `y` için `[2,20]`, `b` için `[3,4]`. Toplam değerin tipinin sessizce değişmesi, sonradan `b`'yi sayı varsayan kodu bozabilecek bir ayrıntıdır.

#### Sınırlayıcı Ölçüm

Eksik yola okumak uyarı veriyordu. Aynı eksik yola yazmak ise ayrı bir kanaldır.

```php
<?php
// yazma.php — eksik yola yazma ve okuma ayri kanal
require 'kanal.php';

$gozlemler = [
    'bos diziye derin yazma' => function () {
        $a = [];
        $a['x']['y'] = 1;
        return $a;
    },
    'ayni yoldan okuma' => function () {
        $a = [];
        return $a['x']['y'];
    },
    'varolan skalere yazma' => function () {
        $a = ['x' => 5];
        $a['x']['y'] = 1;
        return $a;
    },
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-24s%-9s%s\n", $ad, $sinif, $kanit);
}

$a = [];
$a['x']['y'] = 1;
echo 'kurulan yapi: ', json_encode($a), "\n";
```

```
bos diziye derin yazma  sessiz   sonuc dizi(1)
ayni yoldan okuma       uyari    E_WARNING, sonuc null
varolan skalere yazma   durdurur Error
kurulan yapi: {"x":{"y":1}}
```

Boş bir diziye `$a['x']['y'] = 1` yazmak iç diziyi kendiliğinden kurar ve hiçbir seviye bildirmez: sınıf **sessiz**. Aynı yoldan okumak ise uyarı verir. Okuma ve yazma ayrı kanallardır; birinde duyulan sorun diğerinde duyulmaz. Üçüncü satır ayrımın sınırını çiziyor: yol üzerindeki mevcut değer bir skaler ise, yazma artık kendiliğinden kurmaz ve durdurur.

**DZ9.** Yazmanın sessizliği bir kolaylık olarak tasarlanmıştır ve yanlış yazılmış bir anahtarda yeni bir dal kurar: `$a['notlr']['mat'] = 90` hata vermez, yanında yeni bir `notlr` dalı açar. Bu ders o dalın sonradan kim tarafından okunduğunu izlemez; ölçülen yalnız yazma anında hiçbir kanaldan haber gelmediğidir.

#### Özet

- Çok boyutlu dizi ayrı bir tip değil, elemanları dizi olan dizidir; erişim yol boyunca anahtarların sırayla uygulanmasıdır.
- `count` yalnız dış düzeyi sayar; `COUNT_RECURSIVE` iç dizileri hem eleman hem içerik olarak sayar.
- Eksik iç anahtar `null` döndürür ve `E_WARNING` bildirir; bu dersin ölçü sınıfı **uyarı**dır (`eksik ic anahtar`).
- `null` üzerinde dizi erişimi de `E_WARNING` verir ve `null` döner; kopukluktan sonraki her halka sessizce `null` üretir.
- `??` yolun tamamını sessize çevirir; yanlış yazılmış yolu da aynı sessizlikle örter.
- Okuma ile yazma ayrı kanaldır: eksik yoldan okumak uyarı verir, boş diziye derin yazmak iç diziyi sessizce kurar.

#### Sonraki Adım

Bu ders bir yolun kopukluğunu `null` ve uyarı olarak ölçtü. Dizi Fonksiyonları dersinde kanal değişiyor: bir eleman aramak, bulunamadığında uyarı vermiyor, bir değer dönüyor; ve o değer, bulunan ilk elemanın dönüşüyle gevşek karşılaştırmada karışabiliyor.

### Dizi Fonksiyonları

Source: https://academia.sh/tr/kurslar/php-temelleri/dizi-fonksiyonlari

Süzme, eşleme ve indirgeme fonksiyonları; array_search bulamayınca false döndürmesi; sıfırıncı indeksteki eşleşmenin false ile gevşek karşılaştırmada karışması; boyu uyuşmayan array_combine ve negatif str_repeat çağrısının ValueError vermesi.

Çok Boyutlu Diziler dersinde bir yolun kopukluğu `null` ve `E_WARNING` olarak duyuldu. Dizi fonksiyonlarında kanal değişiyor: aranan bulunamadığında bir seviye bildirilmiyor, fonksiyon özel bir değer döndürüyor ve bu değerin yorumlanması çağıran kodun işi oluyor. Ders sınıf adlarını önceki derslerdeki anlamıyla kullanır: **sessiz**, **dönüş**, **uyarı**, **durdurur**.

PHP'nin dizi fonksiyonları geniş bir ailedir ve ders hepsini dolaşmıyor. Ölçülen iki grup var: bir diziyi dönüştüren süzme (filter), eşleme (map) ve indirgeme (reduce) üçlüsü, ve bir diziden eleman arayan fonksiyonlar. Birincisi veri akışının kendisidir, ikincisi ise bir sorunun nereden duyulduğunu en çıplak gösteren yerdir.

#### Süzme, Eşleme ve İndirgeme

Üç fonksiyon üç ayrı soru sorar. Süzme "hangi elemanlar kalsın", eşleme "her eleman neye dönüşsün", indirgeme "hepsi tek bir değere nasıl iner" sorusunu yanıtlar. Aşağıdaki dosya üçünü aynı sayı listesi üzerinde çalıştırıyor.

```php
<?php
// donusum.php — suzme, esleme ve indirgeme
$sayilar = [1, 2, 3, 4, 5, 6];

$ciftler = array_filter($sayilar, fn($n) => $n % 2 === 0);
$kareler = array_map(fn($n) => $n * $n, $sayilar);
$toplam = array_reduce($sayilar, fn($birikim, $n) => $birikim + $n, 0);

echo 'ciftler: ', json_encode($ciftler), "\n";
echo 'kareler: ', json_encode($kareler), "\n";
echo 'toplam: ', $toplam, "\n";
echo 'ciftlerin anahtarlari: ', implode(',', array_keys($ciftler)), "\n";
echo 'yeniden numaralanmis: ', json_encode(array_values($ciftler)), "\n";
```

```
ciftler: {"1":2,"3":4,"5":6}
kareler: [1,4,9,16,25,36]
toplam: 21
ciftlerin anahtarlari: 1,3,5
yeniden numaralanmis: [2,4,6]
```

`array_filter` tutulan elemanların anahtarlarını korur. `ciftler` listesinin anahtarları `1, 3, 5` olduğu için `json_encode` onu bir liste değil bir nesne olarak yazar; `array_values` anahtarları yeniden numaralar. Bu, İndeksli ve İlişkisel Diziler dersindeki `unset` davranışının aynısıdır: indeksli görünen bir dizi süzmeden sonra indeksli olmaktan çıkabilir. `array_map` ise elemanlarla birlikte anahtarları da aynen taşır; bir tek dizi verildiğinde sonuç aynı anahtar kümesini kullanır.

**DZ10.** `array_reduce`, geri çağrının ilk parametresini bir birikim (accumulator) değerine bağlar ve başlangıç değeri üçüncü argümandır. Başlangıç değeri verilmezse birikim `null` ile başlar; boş bir dizi indirgendiğinde sonuç bu durumda `null` olur. Ders başlangıç değerini her zaman açıkça verir ve boş dizi davranışını ayrıca ölçmez.

#### Bulunamayan Eleman

Eleman arayan fonksiyonlar sorunu bir uyarıyla değil dönüş değeriyle bildirir. `array_search`, aranan değeri bulursa anahtarını, bulamazsa `false` döndürür. Dinleyici bu davranışı kanal sırasıyla sınıflandırıyor; `kanal.php` aşağıda tam metniyle veriliyor ve çıktı bloğu olmadığı için yalnız diske yazılır.

```php
<?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
<?php
// arama.php — bulunamayan eleman ve ValueError
require 'kanal.php';

$gozlemler = [
    'bulunamayan eleman' => fn() => array_search('elma', ['armut', 'ayva']),
    'bulunan eleman'     => fn() => array_search('ayva', ['armut', 'ayva']),
    'ilk eleman'         => fn() => array_search('armut', ['armut', 'ayva']),
    'negatif tekrar'     => fn() => str_repeat('a', -1),
    'boy farkli combine' => fn() => array_combine(['a', 'b'], [1]),
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-20s%-9s%s\n", $ad, $sinif, $kanit);
}
```

```
bulunamayan eleman  donus    sonuc false
bulunan eleman      sessiz   sonuc int 1
ilk eleman          sessiz   sonuc int 0
negatif tekrar      durdurur ValueError
boy farkli combine  durdurur ValueError
```

İlk satır kursun `bulunamayan eleman` gözlemidir ve **dönüş** sınıfındadır: `array_search('elma', ['armut', 'ayva'])` `false` döndürdü, hiçbir seviye bildirilmedi, yürütme sürdü. Sorun yalnız dönüş değerine bakan kod tarafından duyulur. İkinci satır başarılı aramayı gösteriyor: bulunan elemanın anahtarı olan `1` sessiz bir sonuçtur. Üçüncü satır sorunun kaynağını hazırlıyor: ilk eleman bulunduğunda dönen anahtar `0` olur.

Son iki satır aynı fonksiyon ailesinin başka bir kanalını gösteriyor. `str_repeat('a', -1)` ve boyları farklı iki diziyle `array_combine` çağrısı `ValueError` fırlatır; sınıf **durdurur**. Argümanın değeri tipi doğru olsa da kabul edilebilir aralığın dışındaysa fonksiyon sessizce yanlış bir sonuç üretmez, durur. Aynı fonksiyon ailesinde iki farklı kanal bulunur: "bulunamadı" bir dönüş değeridir, "çağrı geçersiz" bir istisnadır.

**DZ11.** `array_combine` ve `str_repeat` yalnız `ValueError` kanalının örnekleridir. Hangi fonksiyonun hangi koşulda `ValueError` ya da `false` verdiği her fonksiyonun kendi belgesinde yazılıdır ve ders onu listelemez; ölçülen, "bulunamadı"nın dönüş, "geçersiz çağrı"nın istisna kanalından geldiğidir.

#### Sıralama ve Dilimleme

Sıralama fonksiyonlarının hepsi diziyi yerinde değiştirir ve anahtarlara farklı davranır: `sort` anahtarları atar ve yeniden numaralar, `asort` değere göre sıralarken anahtarları korur, `ksort` anahtara göre sıralar. `array_slice` ise tam sayı anahtarları varsayılan olarak yeniden numaralar.

```php
<?php
// sirala.php — siralama ve dilimlemenin anahtarlara etkisi
$ham = [3 => 'c', 1 => 'a', 2 => 'b'];

$a = $ham;
sort($a);
echo 'sort: ', json_encode($a), "\n";

$b = $ham;
asort($b);
echo 'asort: ', json_encode($b), "\n";

$c = $ham;
ksort($c);
echo 'ksort: ', json_encode($c), "\n";

$d = $ham;
rsort($d);
echo 'rsort: ', json_encode($d), "\n";

echo 'dilim: ', json_encode(array_slice($ham, 1)), "\n";
echo 'dilim, anahtarlar korunur: ', json_encode(array_slice($ham, 1, null, true)), "\n";
echo 'sort dondusu: ', var_export(sort($a), true), "\n";
```

```
sort: ["a","b","c"]
asort: {"1":"a","2":"b","3":"c"}
ksort: {"1":"a","2":"b","3":"c"}
rsort: ["c","b","a"]
dilim: ["a","b"]
dilim, anahtarlar korunur: {"1":"a","2":"b"}
sort dondusu: true
```

`sort` ve `rsort` ilişkisel bilgiyi atar; `asort` ve `ksort` anahtarı korur ve `json_encode` bu yüzden onları nesne olarak yazar. `array_slice` tam sayı anahtarlarını `preserve_keys` argümanı verilmedikçe yeniden numaralar. Son satır sıralama fonksiyonlarının tuttuğu bir sözleşmeyi gösteriyor: fonksiyonlar diziyi yerinde değiştirir ve geriye `true` döndürür; sıralanmış kopyayı döndüren bir fonksiyon değildir. `$sirali = sort($dizi)` yazan kod diziyi sıralar ama `$sirali` değişkenine `true` atar.

**DZ12.** Sıralama fonksiyonlarının kararlılığı (eşit elemanların göreli sırasının korunması) bu derste ölçülmez; ders yalnız anahtarların yazgısını ve dönüş değerini sınar. Karışık tipli diziyi sıralamak dilin karşılaştırma kurallarına bağlıdır ve burada örnek verilmez.

#### Üyelik Sorgusu

`in_array` bir değerin dizide bulunup bulunmadığını sorar ve `array_search` ile aynı gevşek karşılaştırmayı kullanır. Üçüncü argüman `true` verildiğinde karşılaştırma katı olur.

```php
<?php
// uyelik.php — in_array gevsek ve kati karsilastirma
$sayilar = ['10', '20'];

echo "gevsek, '1e1': ", var_export(in_array('1e1', $sayilar), true), "\n";
echo "kati, '1e1': ", var_export(in_array('1e1', $sayilar, true), true), "\n";
echo "gevsek, 10: ", var_export(in_array(10, $sayilar), true), "\n";
echo "kati, 10: ", var_export(in_array(10, $sayilar, true), true), "\n";
echo "gevsek null, [0]: ", var_export(in_array(null, [0]), true), "\n";
echo "kati null, [0]: ", var_export(in_array(null, [0], true), true), "\n";

$tekrar = ['1', 1, 'a', 'A', 'a'];
echo 'benzersiz: ', json_encode(array_unique($tekrar)), "\n";
```

```
gevsek, '1e1': true
kati, '1e1': false
gevsek, 10: true
kati, 10: false
gevsek null, [0]: true
kati null, [0]: false
benzersiz: {"0":"1","2":"a","3":"A"}
```

Gevşek karşılaştırmada `'1e1'` dizgisi `'10'` ile eşit sayılır, çünkü ikisi de sayısal dizgidir ve değerleri `10`'dur; katı karşılaştırma ikisini ayırır. Aynı kural `10` tam sayısı ile `'10'` dizgisi için de geçerlidir. `null` ile `0` gevşek karşılaştırmada eşittir, katı karşılaştırmada değildir. Bu sonuçlar kötü yazılmış bir koşul değil, gevşek eşitliğin kuralıdır; Programlama Temelleri kursunda (M06/K01) ölçülen zorlama burada bir arama sonucunu etkiliyor. `in_array` çıktısı yalnızca `true` ya da `false` olduğu için yanlış pozitif hiçbir kanaldan duyulmaz: sınıf **sessiz**. Son satırda `array_unique` ilk görülen değeri korur ve dizgi gösterimi aynı olan `'1'` ile `1`'i tek sayar; `'a'` ile `'A'` ayrı kalır.

#### Eşleme ve Toplamada Sessiz Davranışlar

Eşleme, ters çevirme ve toplama fonksiyonları girdinin biçimi beklenenden farklı olduğunda çoğu zaman bir seviye bildirmez. Aşağıdaki gözlemler aynı dinleyiciyle sınıflanıyor.

```php
<?php
// sessiz.php — esleme, ters cevirme ve toplamada girdi sorunlari
require 'kanal.php';

$gozlemler = [
    'iki dizi, farkli boy' => fn() => array_map(fn($a, $b) => [$a, $b], [1, 2, 3], ['x']),
    'yinelenen deger flip' => fn() => array_flip(['a', 'b', 'a']),
    'yinelenen anahtar'    => fn() => array_combine(['a', 'a'], [1, 2]),
    'sayisal dizgi toplam' => fn() => array_sum(['3', '4']),
    'sayisal olmayan toplam' => fn() => array_sum([1, 'abc']),
    'bos dizi toplam'      => fn() => array_sum([]),
    'bos dizi max'         => fn() => max([]),
    'anahtarla suzme'      => fn() => array_filter(['a' => 1, 'b' => 2], fn($k) => $k === 'a', ARRAY_FILTER_USE_KEY),
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-23s%-9s%s\n", $ad, $sinif, $kanit);
}

echo 'flip: ', json_encode(array_flip(['a', 'b', 'a'])), "\n";
echo 'combine: ', json_encode(array_combine(['a', 'a'], [1, 2])), "\n";
echo 'map: ', json_encode(array_map(fn($a, $b) => [$a, $b], [1, 2, 3], ['x'])), "\n";
```

```
iki dizi, farkli boy   sessiz   sonuc dizi(3)
yinelenen deger flip   sessiz   sonuc dizi(2)
yinelenen anahtar      sessiz   sonuc dizi(1)
sayisal dizgi toplam   sessiz   sonuc int 7
sayisal olmayan toplam uyari    E_WARNING, sonuc int 1
bos dizi toplam        sessiz   sonuc int 0
bos dizi max           durdurur ValueError
anahtarla suzme        sessiz   sonuc dizi(1)
flip: {"a":2,"b":1}
combine: {"a":2}
map: [[1,"x"],[2,null],[3,null]]
```

`array_map` iki dizi aldığında kısa olanı `null` ile tamamlar ve hiçbir seviye bildirmez; `[2,null]` ve `[3,null]` çiftleri sessizce üretilir. `array_flip` ve `array_combine` yinelenen değer ya da anahtarda son değeri tutar, ilkini sessizce atar: `{"a":2,"b":1}` ve `{"a":2}`.

Toplamada sayısal dizgiler sayıya çevrilir (`'3'` ile `'4'` toplamı 7), sayısal olmayan bir eleman ise `E_WARNING` üretir ve toplam yalnız geçerli elemanları sayar. Boş dizide `array_sum` 0 verir, `max` ise `ValueError` ile durur; boş olabilecek bir dizide `max` çağrılmadan önce `count` ya da `[] !== $dizi` ile sınanmalıdır. `ARRAY_FILTER_USE_KEY` geri çağrıya değer yerine anahtarı verir ve sessiz çalışır.

#### Sınırlayıcı Ölçüm

Dönüş kanalı yalnız çağıran kod onu doğru okursa çalışır. `array_search`'ün `0` ve `false` dönüşleri gevşek eşitlikte aynı değerdir. İki yazım aynı listeyi iki farklı biçimde yorumluyor.

```php
<?php
// ayrim.php — 0 ile false gevsek karsilastirmada ayni degerdir
$liste = ['armut', 'ayva'];

$gevsek = array_search('armut', $liste) == false ? 'bulunamadi' : 'bulundu';
$kati = array_search('armut', $liste) === false ? 'bulunamadi' : 'bulundu';

echo 'gevsek: ', $gevsek, "\n";
echo 'kati: ', $kati, "\n";
echo 'gercekten yok, gevsek: ',
    (array_search('elma', $liste) == false ? 'bulunamadi' : 'bulundu'), "\n";

$karisik = array_filter([1, 0, 2, 0, 3]);
echo 'sifirlar atilir: ', json_encode($karisik), "\n";
echo 'anahtarlar: ', implode(',', array_keys($karisik)), "\n";
```

```
gevsek: bulunamadi
kati: bulundu
gercekten yok, gevsek: bulunamadi
sifirlar atilir: {"0":1,"2":2,"4":3}
anahtarlar: 0,2,4
```

İlk elemanı aratan gevşek karşılaştırma `bulunamadi` sonucunu verdi, katı karşılaştırma `bulundu`; gerçekten olmayan elemanda ise gevşek karşılaştırma doğru sonucu verdi. Aynı çağrı, aranan ilk sırada olduğunda yanlış, başka sırada olduğunda doğru yanıt verir ve hiçbir kanal bu farkı bildirmez. Dönüş kanalının ayrım gücü, çağıranın ayrım gücüyle sınırlıdır: `0` ile `false` ayırt edilmezse bulunan ilk eleman "yok" sayılır.

`array_filter` ile geri çağrı verilmeden çağrıldığında değeri `false`'a eşit olan her eleman atılır; `0` da bunlardan biridir. Çıktıdaki anahtarlar `0, 2, 4` olarak kalır: yani hem veri kaybolur hem dizi indeksli olmaktan çıkar ve ikisi de sessizdir. Süzmenin amacı buysa sorun yoktur; amaç yalnız boş değerleri atmaksa `0` beklenen bir değerken de atılmış olur.

#### Özet

- `array_filter` anahtarları korur, `array_map` anahtarları taşır, `array_reduce` başlangıç değeriyle tek bir değere iner.
- Süzmeden sonra dizi indeksli olmaktan çıkabilir; `array_values` anahtarları yeniden numaralar.
- `array_search` bulamayınca `false` döndürür; bu dersin ölçü sınıfı **dönüş**tür (`bulunamayan eleman`).
- Geçersiz argüman değeri (`str_repeat('a', -1)`, boyları farklı `array_combine`) `ValueError` fırlatır; sınıf **durdurur**.
- İlk eleman bulunduğunda dönen `0`, gevşek karşılaştırmada `false` ile aynıdır; yalnız katı karşılaştırma ikisini ayırır.
- Geri çağrısız `array_filter` `0` dahil her `false` eşdeğeri elemanı sessizce atar.

#### Sonraki Adım

Bu ders dönüş kanalının ayrım gücünü ölçtü ve `0` ile `false` karışınca ayrımın çağırana kaldığını gösterdi. Dizgi İşleme dersi aynı kanalın daha sert bir durumunu ölçüyor: iki ayrı sorun aynı dönüş değerini verirse, değer tek başına hangisinin olduğunu söyleyemez.

### Dizgi İşleme

Source: https://academia.sh/tr/kurslar/php-temelleri/dizgi-isleme

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ışı.

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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?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

- `sprintf` ve `printf` biç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')` `null` döndürür; bu dersin ölçü sınıfı **dönüş**tür (`bozuk json`). Geçersiz bayt dizisi için `json_encode` `false` döndürür.
- `JSON_THROW_ON_ERROR` aynı sorunu `JsonException` olarak fırlatır; sınıf **durdurur**.
- Geçerli `null` ile bozuk JSON ikisi de `null` döndürür; ayrımı `json_last_error()` (sıfır ya da dört) ya da `JSON_THROW_ON_ERROR` yapar.
- İç içe dizinin `implode`'u `'Array'` ve `E_WARNING` verir; dizi argümanıyla `strlen` `TypeError` fı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.

## İstek ve Yanıt

İstek verisinin süper küresel değişkenlerle gelmesi, form verisinin alınıp doğrulanması, yüklenen dosyanın sınırları ve dosya dahil etmenin iki biçimi arasındaki fark.

### Süper Küresel Değişkenler

Source: https://academia.sh/tr/kurslar/php-temelleri/super-kuresel-degiskenler

İ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.

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
<?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
<?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
<?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";
```

```bash
# 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 **dizgi**dir: `?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 **dizi**ye ç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
<?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";
```

```bash
# 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
<?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);
}
```

```bash
# 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
<?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";
```

```bash
# 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
<?php
// erken.php — hata cikti baslamadan once gelir
$u = strlen($_GET['u']);
echo "uzunluk=$u\n";
```

```php
<?php
// gec.php — ayni hata bir echo'dan sonra gelir
echo "basladi\n";
$u = strlen($_GET['u']);
echo "uzunluk=$u\n";
```

```bash
# 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ı okumak `null` verir ve `E_WARNING` bildirir; yürütme sürer.
- Boşluk birleştirme işleci aynı eksikliği sessiz kanala taşır; `filter_input` komut satırında `null` döndürerek dönüş kanalından duyurur.
- İstek verisi `string` gelir, `a[]` biçimindeki ad `array` yapar; 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 `strlen` `TypeError` fı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.

### Form İşleme

Source: https://academia.sh/tr/kurslar/php-temelleri/form-isleme

Formdan gelen dizgilerin filter_var ile doğrulanması: başarısızlığın dönüş değeriyle duyulması, null'a çevrilmesi ve geçerli 0 değerinin başarısızlıkla karışması.

Süper Küresel Değişkenler dersinde istek verisi her zaman dizgi olarak geldi ve eksik anahtar uyarı kanalından duyuldu. Bu derste anahtar var, ama içeriği beklenen biçimde değil: sayı beklenen alana `12a` gelmiş. PHP bu değerle karşılaşınca bir şey söyler mi, söylerse hangi kanaldan?

Formun HTML yapısını ve alanların nasıl kurulduğunu M14 anlattı; burada yalnız gönderilen verinin sunucu tarafında nasıl süzüldüğü ölçülüyor. Doğrulama işi için PHP'nin `filter_var` fonksiyonu kullanılır: bir değeri ve bir süzgeç sabitini alır, süzgeç değeri kabul ederse değeri uygun tipte döndürür, kabul etmezse `false` döndürür. Çıktıda kaçırma (escaping) için `htmlspecialchars` kullanılır; kaçırmanın neden gerektiği ve ona karşı alınacak önlemlerin sınıflandırması M25'in konusudur.

#### Geçersiz Değer Dönüşle Duyulur

İlk ölçüm komut satırında yapılır ve süzgeci elle verilen dizgilerle sınar.

```php
<?php
// suzgec.php — filter_var'in gecerli ve gecersiz girdideki donusleri
function yaz(mixed $deger): string
{
    return match (true) {
        $deger === null => 'null',
        is_bool($deger) => $deger ? 'true' : 'false',
        default => get_debug_type($deger) . ' ' . $deger,
    };
}

$olcumler = [
    'gecerli sayi'          => filter_var('12', FILTER_VALIDATE_INT),
    'gecersiz form sayisi'  => filter_var('12a', FILTER_VALIDATE_INT),
    'null_on_failure'       => filter_var('12a', FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE),
];

foreach ($olcumler as $ad => $sonuc) {
    printf("%-24s%s\n", $ad, yaz($sonuc));
}
```

```
gecerli sayi            int 12
gecersiz form sayisi    false
null_on_failure         null
```

`12` için fonksiyon `int 12` döndürür: dizgi tam sayıya çevrilmiştir. `12a` için `false` döndürür. Fonksiyon bir uyarı bildirmedi, bir istisna fırlatmadı ve yürütme sürdü; sorun yalnız dönüş değerine bakan kod için duyuluyor. Bu kursun **dönüş** sınıfıdır: sinyal kendiliğinden gelmez, çağıranın değeri sorgulaması gerekir. **IY8.** Bu ölçümde fonksiyonların hiçbiri `E_WARNING` üretmiyor; bu bir ayar değil, `filter_var`'ın tanımlı davranışıdır, bu yüzden ölçümde bir hata işleyicisi kurulmadı.

Üçüncü satır, `FILTER_NULL_ON_FAILURE` bayrağının başarısızlığı `false` yerine `null`'a çevirdiğini gösterir. Kanal değişmez, hâlâ dönüş değeridir; yalnız değerin kendisi farklıdır. Bayrağın anlamı sonraki bölümde netleşir: `false`, mantıksal süzgeç için geçerli bir sonuçtur ve bu bayrak onu başarısızlıktan ayırmak için vardır.

#### Formu Sunucuya Göndermek

Gerçek bir form gönderimini yerleşik sunucuyla ölçeriz. Betik iki alan okur: `yas` bir tam sayı olmalı, `ad` kullanıcı metnidir ve çıktıya kaçırılarak yazılır.

```php
<?php
// form.php — POST ile gelen yasi dogrular, adi kacirarak yazar
$yas = filter_var($_POST['yas'] ?? '', FILTER_VALIDATE_INT);
$ad = $_POST['ad'] ?? '';

if ($yas === false) {
    echo "gecersiz yas\n";
} else {
    echo 'yas=', $yas, "\n";
}
echo 'ad=', htmlspecialchars($ad), "\n";
```

```bash
# sunucu.sh — formu yerlesik sunucuya gonderir
php -S 127.0.0.1:8082 -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:8082/form.php && break; sleep 0.1; done
echo '--- gecerli'
curl -s -d 'yas=30' --data-urlencode 'ad=Ayse' -w 'durum=%{http_code}\n' http://127.0.0.1:8082/form.php
echo '--- gecersiz yas'
curl -s -d 'yas=12a' --data-urlencode 'ad=Ayse' -w 'durum=%{http_code}\n' http://127.0.0.1:8082/form.php
echo '--- etiketli ad'
curl -s -d 'yas=30' --data-urlencode 'ad=<b>Ayse</b>' -w 'durum=%{http_code}\n' http://127.0.0.1:8082/form.php
kill $pid
```

```
--- gecerli
yas=30
ad=Ayse
durum=200
--- gecersiz yas
gecersiz yas
ad=Ayse
durum=200
--- etiketli ad
yas=30
ad=&lt;b&gt;Ayse&lt;/b&gt;
durum=200
```

Geçerli gönderimde `yas=30` yazıldı. Geçersiz gönderimde betik `gecersiz yas` yazdı, ama durum kodu yine **200**: sunucu açısından istek başarıyla işlendi, çünkü doğrulama sonucu PHP için bir hata değil, kodun kendi verdiği bir karardır. Doğrulamanın başarısızlığını istemciye bildirmek, kodun bir durum kodu ya da ileti seçmesine bağlıdır; PHP bunu kendiliğinden yapmaz. **IY9.** Durum kodu `false` dönüşünün yaşandığını söylemez; 200 görmek doğrulamanın geçtiği anlamına gelmez.

Üçüncü gönderimde `ad` alanındaki etiket, `htmlspecialchars` sayesinde `&lt;b&gt;Ayse&lt;/b&gt;` olarak yazıldı. Kaçırma olmasaydı tarayıcı bu metni biçimlendirme olarak yorumlardı. Bu fonksiyon dönüş değeriyle de sessizdir: hiçbir kanal, değerin etiket içerdiğini bildirmez.

#### Süzgeç Seçenekleri Aralığı Daraltır

Tip kontrolü çoğu zaman yetmez: yaş alanına `150` ya da `-5` yazan biri geçerli bir tam sayı göndermiştir ama geçerli bir yaş göndermemiştir. `filter_var` bunu üçüncü argümandaki `options` dizisiyle çözer; `min_range` ve `max_range` anahtarları kabul edilen aralığı belirler. Ayrıca süzgeç, değerin başındaki ve sonundaki boşlukları kırpar; bu yüzden formdan gelen `' 30 '` gibi bir değer geçerli sayılır.

```php
<?php
// aralik.php — tamsayi suzgecinde aralik ve bosluk davranisi
$secenekler = ['options' => ['min_range' => 0, 'max_range' => 120]];

$girdiler = ['30', ' 30 ', '150', '-5', '3.0'];
foreach ($girdiler as $girdi) {
    $sonuc = filter_var($girdi, FILTER_VALIDATE_INT, $secenekler);
    printf("%-8s%s\n", '[' . $girdi . ']', $sonuc === false ? 'false' : 'int ' . $sonuc);
}
```

```
[30]    int 30
[ 30 ]  int 30
[150]   false
[-5]    false
[3.0]   false
```

Aralığın dışında kalan `150` ve `-5` değerleri `12a` ile aynı yolu izler: ikisi de `false` döndürür. Çağıran bakımından üç farklı sorun, yani biçim hatası, üst sınırın aşılması ve alt sınırın aşılması, tek bir değere indirgenir. Hangisinin olduğunu öğrenmek için kodun girdiyi yeniden incelemesi gerekir; dönüş kanalı yalnız başarısızlığın olduğunu söyler, nedenini taşımaz. `3.0` da reddedilir, çünkü süzgeç ondalık gösterimi tam sayı kabul etmez. **IY10.** Burada gösterilen kırpma ve ondalık davranışı ölçümün çalıştığı PHP 8 ailesine aittir; ders sınır değerlere güvenmez, aralığı her çağrıda kendisi verir.

#### Mantıksal Süzgeç `false`'u Başarısızlıktan Ayırır

`FILTER_NULL_ON_FAILURE` bayrağının asıl işi mantıksal süzgeçte görünür. `FILTER_VALIDATE_BOOLEAN` için `false` geçerli bir sonuçtur: `"false"`, `"0"` ve boş dizgi onun kabul edilen yazılışlarıdır. Bayrak olmadan, hem geçerli `false` hem tanınmayan bir metin `false` döndürür ve çağıran ikisini ayıramaz. Bayrakla tanınmayan metin `null` olur ve ayrım değerin kendisinde kalır.

```php
<?php
// mantiksal.php — mantiksal suzgecte gecerli false ile basarisizligin ayrimi
function yaz(mixed $deger): string
{
    return $deger === null ? 'null' : ($deger ? 'true' : 'false');
}

foreach (['true', 'on', 'false', '0', '', 'abc'] as $girdi) {
    $bayraksiz = filter_var($girdi, FILTER_VALIDATE_BOOLEAN);
    $bayrakli = filter_var($girdi, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
    printf("%-8s bayraksiz=%-6s bayrakli=%s\n", '[' . $girdi . ']', yaz($bayraksiz), yaz($bayrakli));
}
```

```
[true]   bayraksiz=true   bayrakli=true
[on]     bayraksiz=true   bayrakli=true
[false]  bayraksiz=false  bayrakli=false
[0]      bayraksiz=false  bayrakli=false
[]       bayraksiz=false  bayrakli=false
[abc]    bayraksiz=false  bayrakli=null
```

Bayraksız sütunda `false`, `0`, boş dizgi ve `abc` aynı görünür; yalnız `abc` gerçekten geçersizdir. Bayrak `abc` için `null` döndürür, diğerleri geçerli `false` kalır. Bu, önceki bölümlerde sözü edilip ölçülmemiş iddiadır: bayrak başarısızlığı yeni bir kanala taşımaz, mevcut dönüş kanalında `false` ile `null` arasında bir ayrım açar. **IY11.** Bu ayrımdan yararlanmak için çağıranın `=== null` sınaması yapması gerekir; değilleme işleci `null` ile `false`'u yine aynı sayar.

#### Eksik Alan Geçersiz Değerle Aynı Görünür

Doğrulamanın bir tuzağı, alanın hiç gönderilmemesidir. Önceki `form.php` eksik alanı `?? ''` ile boş dizgiye çevirir ve süzgeç boş dizgiyi reddeder; sonuç, `12a` gönderilmiş gibi `false` olur. Ayrım için anahtarın varlığı ayrıca sınanır.

```php
<?php
// eksik_alan.php — eksik alan ile gecersiz alanin ayni false'a inmesi
$yas = filter_var($_POST['yas'] ?? '', FILTER_VALIDATE_INT);
$var = array_key_exists('yas', $_POST) ? 'evet' : 'hayir';
echo 'suzgec=', $yas === false ? 'false' : $yas, ' alan_var=', $var, "\n";
```

```bash
# eksik_alan.sh — alanli ve alansiz iki POST gonderir
php -S 127.0.0.1:8082 -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:8082/eksik_alan.php && break; sleep 0.1; done
echo '--- yas=12a'
curl -s -d 'yas=12a' -w 'durum=%{http_code}\n' http://127.0.0.1:8082/eksik_alan.php
echo '--- alan yok'
curl -s -d 'ad=Ayse' -w 'durum=%{http_code}\n' http://127.0.0.1:8082/eksik_alan.php
kill $pid
```

```
--- yas=12a
suzgec=false alan_var=evet
durum=200
--- alan yok
suzgec=false alan_var=hayir
durum=200
```

İki istekte süzgeç sonucu aynıdır: `false`. Yalnız `array_key_exists` farkı gösterir. Eksik alan Süper Küresel Değişkenler dersinde uyarı kanalındaydı; `?? ''` onu sessiz kanala çekti ve doğrulama sonunda dönüş kanalına indirdi. Üç kanal, tek bir satırda zincirlenmiştir ve sonuç hiçbir noktada hangisinin devrede olduğunu söylemez. **IY12.** Hangi kanalın seçildiğini bilmeyen kod, "alan yok" ile "alan bozuk" durumlarını aynı hata iletisiyle karşılar.

#### Diğer Süzgeçler Aynı Dönüş Yolunu Kullanır

Tam sayı ve mantıksal süzgeçler tek örnek değildir. E-posta ve ondalık sayı süzgeçleri de aynı biçimde çalışır: geçerli girdide değeri, geçersiz girdide `false` döndürür. Ondalık sayıda ayırıcı nokta olarak kabul edilir; virgül kullanan bir kullanıcının girdisi reddedilir.

```php
<?php
// digerleri.php — e-posta ve ondalik sayi suzgeclerinin donusleri
$olcumler = [
    'eposta gecerli'    => filter_var('ad@ornek.test', FILTER_VALIDATE_EMAIL),
    'eposta gecersiz'   => filter_var('ad@', FILTER_VALIDATE_EMAIL),
    'ondalik nokta'     => filter_var('1.5', FILTER_VALIDATE_FLOAT),
    'ondalik virgul'    => filter_var('1,5', FILTER_VALIDATE_FLOAT),
];

foreach ($olcumler as $ad => $sonuc) {
    printf("%-18s%s\n", $ad, $sonuc === false ? 'false' : get_debug_type($sonuc) . ' ' . $sonuc);
}
```

```
eposta gecerli    string ad@ornek.test
eposta gecersiz   false
ondalik nokta     float 1.5
ondalik virgul    false
```

Dört süzgeç de aynı yoldan konuşur: sinyal dönüş değerindedir ve ayrıntı yoktur. `1,5` reddi, süzgecin yerel ayara bakmadığını gösterir; ayırıcıyı değiştirmek çağıranın görevidir. **IY13.** Bu dersin bütün ölçümleri tek bir sonuca varır: PHP girdinin geçersizliğini istemciye bildirmez, çağırana yalnız bir değer verir ve sonrası kodun kararıdır.

#### Sınırlayıcı Ölçüm

Dönüş kanalının zayıf noktası, başarısızlığı sınamanın yoludur. Başarısızlığı `false`'tan ayırmanın en kısa yolu değeri değilleme işleciyle sınamaktır, ama `0` geçerli bir tam sayıdır.

```php
<?php
// sifir.php — gecerli 0 ile basarisizligin ayni sinanmasi
$olcumler = [
    'sifir'   => filter_var('0', FILTER_VALIDATE_INT),
    'gecersiz' => filter_var('0a', FILTER_VALIDATE_INT),
];

foreach ($olcumler as $ad => $sonuc) {
    $kisa = !$sonuc ? 'basarisiz sanildi' : 'gecerli';
    $kati = $sonuc === false ? 'basarisiz' : 'gecerli';
    printf("%-10s%s: %-18s%s: %s\n", $ad, 'degilleme', $kisa, 'kati', $kati);
}
```

```
sifir     degilleme: basarisiz sanildi kati: gecerli
gecersiz  degilleme: basarisiz sanildi kati: basarisiz
```

`0` için `filter_var` geçerli bir `int 0` döndürür. Değilleme işleciyle sınanınca bu değer, `false` ile aynı görünür ve geçerli girdi başarısız sayılır. Katı karşılaştırma (`=== false`) ikisini ayırır. **IY14.** Bu bir PHP tuhaflığı değil, dönüş kanalının doğal sınırıdır: kanal tek bir değerin içine hem sonucu hem başarısızlığı sığdırır ve ayrımı çağıran yapar. Bayrak `FILTER_NULL_ON_FAILURE` ayrımı değerin kendisine taşır, ama o zaman mantıksal süzgeçte geçerli `false` ile `null` ayrışır; her durumda sinyali okuyan kodun hangi değerin hangi anlama geldiğini bilmesi gerekir.

#### Özet

- Form verisi `string` gelir ve `filter_var` onu süzgeçle sınar: geçerliyse uygun tipte değer, geçersizse `false` döner.
- Bu dersin ölçü sınıfı **dönüş**tür: geçersiz `12a` için uyarı da istisna da yoktur; sorun yalnız dönüş değerine bakan kod için duyulur.
- Doğrulama başarısızlığı durum kodunu değiştirmez: betik kendi kararıyla `gecersiz yas` yazsa da yanıt 200 kalır.
- `htmlspecialchars` etiketleri `&lt;` ve `&gt;` biçimine çevirir; kaçırmanın sınıflandırması M25'indir.
- Geçerli `0`, değilleme işlecinde başarısızlıkla aynı görünür; katı karşılaştırma (`=== false`) ikisini ayırır.

#### Sonraki Adım

Bu derste istek tek bir değer taşıyordu ve eksik ya da bozuk alan bir kontrolle yakalandı. Dosya yüklemede ise istek, dosyanın kendisi ve ona eşlik eden bir durum kodu taşır. Dosya Yükleme dersi, yüklenmemiş bir dosyanın `move_uploaded_file` ile taşınmaya çalışılınca ne döndürdüğünü ve sunucu sınırı aşıldığında `$_FILES` ile `$_POST` içinde neyin kaldığını ölçüyor.

### Dosya Yükleme

Source: https://academia.sh/tr/kurslar/php-temelleri/dosya-yukleme

Dosya yüklemede sinyalin dönüş değeri ve hata koduyla gelmesi: yüklenmemiş dosyada move_uploaded_file, sunucu sınırının aşılması ve post_max_size aşımında boşalan istek.

Form İşleme dersinde geçersiz bir değer `filter_var` fonksiyonundan `false` olarak döndü ve istek 200 ile sürdü. Dosya yükleme aynı kanalı kullanır, ama istekte iki parça vardır: dosyanın kendisi ve PHP'nin ona eşlik ettirdiği durum bilgisi. Soru da genişler: yükleme başarısız olduğunda bu bilgi nerede, ve hiç gelmezse ne olur?

Yüklemenin HTTP tarafını, yani çok parçalı gövdenin yapısını M04, bir isteğin sunucuda nasıl ele alındığını M16/K01 kurdu. Burada ölçülen, PHP'nin bu istekten ne çıkardığıdır. Yüklenen dosya `$_FILES` süper küresel değişkenine konur; her dosya alanı için `name` (istemcinin verdiği ad), `size` (bayt), `error` (durum kodu) ve geçici dosyanın yolunu veren `tmp_name` anahtarları bulunur. Geçici dosya yolu ortama bağlı olduğu için bu derste hiçbir çıktıda basılmaz.

#### Yüklenmemiş Dosya Dönüşle Duyulur

Dosyayı kalıcı bir yere taşımanın tek yolu `move_uploaded_file` fonksiyonudur ve yalnız PHP'nin o istekte kendisinin aldığı bir dosya için çalışır. İlk ölçüm komut satırında yapılır; orada hiçbir yükleme olmamıştır, bu yüzden fonksiyon var olan sıradan bir dosyaya uygulanınca da başarısız olmalıdır.

```php
<?php
// kanal_yukleme.php — yuklenmemis bir dosyanin tasinmasi hangi kanaldan duyulur
$uyarilar = [];
set_error_handler(function (int $seviye) use (&$uyarilar): bool {
    $uyarilar[] = $seviye === E_WARNING ? 'E_WARNING' : 'E_' . $seviye;
    return true;
});

file_put_contents('kaynak.txt', "merhaba\n");

try {
    $sonuc = move_uploaded_file('kaynak.txt', 'hedef.txt');
    $sinif = $uyarilar ? 'uyari' : ($sonuc === false ? 'donus' : 'sessiz');
} catch (Throwable $hata) {
    $sinif = 'durdurur';
}
printf("%-24s%-10s%s\n", 'yuklenmemis dosya', $sinif, 'sonuc ' . var_export($sonuc, true));
printf("%-24s%s\n", 'hedef olustu mu', file_exists('hedef.txt') ? 'evet' : 'hayir');
```

```
yuklenmemis dosya       donus     sonuc false
hedef olustu mu         hayir
```

Fonksiyon `false` döndürür; uyarı yok, istisna yok. Dosya hedefe taşınmadı. Bu **dönüş** sınıfıdır ve bu derste ölçülen kanal budur: yürütme, çağıran `false` değerini sorgulamazsa hiçbir şey olmamış gibi sürer. **IY15.** Ölçüm, fonksiyonun yalnız PHP'nin bir isteğin parçası olarak aldığı dosyalarda çalışmasının bilinçli bir güvenlik özelliği olduğunu gösterir: çağıran bir dosya adını elle verse bile fonksiyon onu taşımaz. Bu özelliğin neden var olduğu ve sınıflandırması M24 ile M25'in konusudur.

#### Yüklenen Dosyanın Yapısı

Gerçek bir yüklemeyi yerleşik sunucu ve `curl -F` ile ölçeriz. `-F` bayrağı isteği çok parçalı gövdeyle gönderir ve `@` öneki alanın değerini bir dosyadan okutur.

```php
<?php
// yukle.php — yuklenen dosyayi ve alan sayilarini raporlar
$adlar = [
    UPLOAD_ERR_OK => 'UPLOAD_ERR_OK',
    UPLOAD_ERR_INI_SIZE => 'UPLOAD_ERR_INI_SIZE',
    UPLOAD_ERR_NO_FILE => 'UPLOAD_ERR_NO_FILE',
];

echo 'files=', count($_FILES), ' post=', count($_POST), "\n";
foreach ($_FILES as $alan => $dosya) {
    echo $alan, ' name=', $dosya['name'], ' size=', $dosya['size'];
    echo ' error=', $adlar[$dosya['error']] ?? $dosya['error'], "\n";
    if ($dosya['error'] === UPLOAD_ERR_OK) {
        $tasindi = move_uploaded_file($dosya['tmp_name'], 'saklanan.txt');
        echo 'tasindi=', $tasindi ? 'true' : 'false', ' boyut=', filesize('saklanan.txt'), "\n";
    }
}
```

```bash
# sunucu.sh — kucuk bir dosyayi yerlesik sunucuya yukler
printf 'merhaba dunya\n' > rapor.txt
php -S 127.0.0.1:8083 -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:8083/yukle.php && break; sleep 0.1; done
curl -s -F 'dosya=@rapor.txt' -F 'baslik=ilk' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/yukle.php
kill $pid
```

```
files=1 post=1
dosya name=rapor.txt size=14 error=UPLOAD_ERR_OK
tasindi=true boyut=14
durum=200
```

`$_FILES` bir dosya alanı gördü, `$_POST` ise metin alanını (`baslik`). Alan adı, istemcinin verdiği dosya adı, bayt cinsinden boyut ve `UPLOAD_ERR_OK` hata kodu geldi; `move_uploaded_file` bu kez `true` döndü, çünkü dosya bu isteğin parçasıydı. Dönüş değerinin `true` olduğu tek durum budur: fonksiyon, yürütmenin başında doğru yoldan gelmiş bir dosyayı kabul eder. **IY16.** `tmp_name` bu çıktıda yoktur; değer her koşumda değişir ve ders onu basmaz.

#### `$_FILES` Yapısı ve Dosya Seçilmemiş Alan

Şimdiye kadar yalnız üç anahtar okundu. Bir dosya alanının tüm yapısı, anahtar adlarıyla birlikte tek bir betikle görülebilir; `tmp_name` değeri basılmaz, yalnız anahtar adı listelenir. Aynı betik, formda dosya alanı bulunduğu hâlde kullanıcının dosya seçmediği durumu da ölçer: bu durumda tarayıcı alanı boş bir dosya adıyla gönderir.

```php
<?php
// yapi.php — $_FILES alanlarinin tum anahtarlari ve hata kodlari
foreach ($_FILES as $alan => $dosya) {
    $anahtarlar = array_keys($dosya);
    sort($anahtarlar);
    echo $alan, ' anahtarlar=', implode(',', $anahtarlar), "\n";
    echo $alan, ' error=', json_encode($dosya['error']), ' size=', json_encode($dosya['size']), "\n";
}
echo 'dosya_var=', isset($_FILES['dosya']) ? 'evet' : 'hayir', "\n";
```

```bash
# yapi.sh — dolu alan ve dosya secilmemis alan
printf 'merhaba\n' > rapor.txt
php -S 127.0.0.1:8083 -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:8083/yapi.php && break; sleep 0.1; done
echo '--- dolu alan'
curl -s -F 'dosya=@rapor.txt' http://127.0.0.1:8083/yapi.php
echo '--- dosya secilmemis'
curl -s -F 'dosya=@/dev/null;filename=' http://127.0.0.1:8083/yapi.php
kill $pid
```

```
--- dolu alan
dosya anahtarlar=error,full_path,name,size,tmp_name,type
dosya error=0 size=8
dosya_var=evet
--- dosya secilmemis
dosya anahtarlar=error,full_path,name,size,tmp_name,type
dosya error=4 size=0
dosya_var=evet
```

Dolu alan `error` 0 ve `size` 8 verir; anahtar listesi ölçülen PHP 8.5 sürümünde `full_path`, `name`, `size`, `tmp_name`, `type` ve `error` anahtarlarını içerir. **IY17.** `full_path` anahtarı 8.1 sürümünden beri vardır; ders anahtar listesini yalnız ölçülen sürüm için gösterir. Dosya seçilmemiş alanda ise `error` `UPLOAD_ERR_NO_FILE` değeri olan 4 gelir ve `size` 0'dır. Bu, `$_FILES['dosya']` anahtarının var olduğu ama yüklemenin yapılmadığı durumdur: `isset` bunu `evet` diye bildirir, yani anahtarın varlığı yüklemenin başarısını göstermez. Başarı, `error` değerinin `UPLOAD_ERR_OK` olmasıyla anlaşılır.

#### Çoklu Dosya Dizisi Alan Başına Değil Anahtar Başınadır

Alan adı `dosya[]` ile verilince PHP her dosya için ayrı bir kayıt oluşturmaz. Bunun yerine her anahtarın (`name`, `size`, `error`) altında dosyaların dizisini tutar; yani yapı dosya başına bir kayıt gibi değil, anahtar başına bir dizi gibi görünür.

```php
<?php
// coklu.php — dosya[] alaninin $_FILES'taki yapisi
$dosya = $_FILES['dosya'];
echo 'name_tipi=', get_debug_type($dosya['name']), ' adet=', count($dosya['name']), "\n";
foreach ($dosya['name'] as $sira => $ad) {
    echo $sira, ' ', $ad, ' size=', $dosya['size'][$sira], ' error=', $dosya['error'][$sira], "\n";
}
```

```bash
# coklu.sh — iki dosyayi tek alan adiyla yukler
printf 'bir\n' > a.txt
printf 'iki iki\n' > b.txt
php -S 127.0.0.1:8083 -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:8083/coklu.php && break; sleep 0.1; done
curl -s -F 'dosya[]=@a.txt' -F 'dosya[]=@b.txt' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/coklu.php
kill $pid
```

```
name_tipi=array adet=2
0 a.txt size=4 error=0
1 b.txt size=8 error=0
durum=200
```

`name` bir `array` olur ve her dosyanın bilgisi aynı sıra numarasıyla üç ayrı diziden okunur. Tek dosya için yazılan `$dosya['name']` okuması, çoklu yüklemede bir diziyi dizgi gibi kullanmaya çalışır; bu, Süper Küresel Değişkenler dersindeki `strlen` ve dizi ölçümünün aynı hatasıdır. **IY18.** İstemci alan adını `dosya[]` yaparak yapıyı değiştirebildiği için, tek dosya bekleyen kod için yapı yine istemcinin seçimidir.

#### Hedef Yol Yoksa Taşıma Uyarı Verir

Taşıma fonksiyonu iki ayrı nedenle `false` döndürebilir: dosya bu isteğin parçası değildir ya da hedef yazılamaz. İlki sessizdir. İkincisi aynı dönüş değerini bir uyarıyla birlikte verir. Aşağıdaki betik gerçek bir yükleme alır ve dosyayı olmayan bir dizine taşımaya çalışır; uyarının metni basılmaz, yalnız seviye adı basılır.

```php
<?php
// hedef.php — yuklenen dosyayi olmayan bir dizine tasima denemesi
$seviyeler = [];
set_error_handler(function (int $seviye) use (&$seviyeler): bool {
    $seviyeler[] = $seviye === E_WARNING ? 'E_WARNING' : 'E_' . $seviye;
    return true;
});

$sonuc = move_uploaded_file($_FILES['dosya']['tmp_name'], 'olmayan_dizin/hedef.txt');
echo 'sonuc=', var_export($sonuc, true), ' uyari=', implode(',', $seviyeler), "\n";
```

```bash
# hedef.sh — dosyayi gonderir, tasima olmayan dizine yapilir
printf 'merhaba\n' > rapor.txt
php -S 127.0.0.1:8083 -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:8083/hedef.php && break; sleep 0.1; done
curl -s -F 'dosya=@rapor.txt' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/hedef.php
kill $pid
```

```
sonuc=false uyari=E_WARNING,E_WARNING
durum=200
```

Fonksiyon `false` döndürür ve ek olarak iki `E_WARNING` bildirir: biri hedefin açılamamasını, öteki taşımanın başarısızlığını söyler. Aynı dönüş değeri, ilk bölümdeki yüklenmemiş dosyada uyarısız gelmişti; burada sınıf, **uyarı**ya kayar. **IY19.** Bu fark dönüş değerine bakan kod için görünmezdir: ikisi de `false`'tur. Yalnız hata işleyicisi ya da kayıt dosyası olan kod, taşımanın neden başarısız olduğunu ayırt edebilir; durum kodu yine 200'dür.

#### Sunucu Sınırı Aşılırsa

PHP iki ini değeriyle yüklemeyi sınırlar: `upload_max_filesize` tek bir dosyanın, `post_max_size` ise tüm isteğin üst sınırıdır. Ders hiçbir ayarın varsayılanına güvenmez; sınır `-d` ile verilir. İlk ölçümde tek dosya sınırı küçük tutulur ve büyük bir dosya gönderilir. İkinci ölçümde dosya alanı hiç gönderilmez.

```bash
# sinir.sh — dosya sinirinin asilmasi ve dosya alaninin gonderilmemesi
head -c 3000 /dev/zero | tr '\0' 'a' > buyuk.txt
php -S 127.0.0.1:8083 -d display_errors=0 -d upload_max_filesize=1K >/dev/null 2>&1 &
pid=$!
for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8083/yukle.php && break; sleep 0.1; done
echo '--- 3000 bayt, sinir 1K'
curl -s -F 'dosya=@buyuk.txt' -F 'baslik=ikinci' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/yukle.php
echo '--- dosya alani yok'
curl -s -F 'baslik=ucuncu' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/yukle.php
kill $pid
```

```
--- 3000 bayt, sinir 1K
files=1 post=1
dosya name=buyuk.txt size=0 error=UPLOAD_ERR_INI_SIZE
durum=200
--- dosya alani yok
files=0 post=1
durum=200
```

Sınır aşıldığında alan `$_FILES` içinde yine vardır: adı korunur, `size` 0 olur ve `error` `UPLOAD_ERR_INI_SIZE` olur. Sinyal bu kez hata kodunda gelir; kod okunmazsa dosya hiç gelmemiş gibi sürülür. İstek yine 200 verir. Dosya alanı hiç gönderilmediğinde ise `$_FILES` boştur ve `$_POST` doludur: kodun `$_FILES['dosya']`'yı doğrudan okuması hem uyarı hem `null` demektir, yani Süper Küresel Değişkenler dersindeki eksik anahtardır.

Dosyayı saklarken istemcinin verdiği adı yola doğrudan koymamak, türü yalnız uzantıdan çıkarmamak ve kalıcı yeri belge köküne koymamak gerekir; bu ilkelerin gerekçesi M16/K01'de, sınıflandırması ve savunma yordamları M24 ile M25'te anlatılır. Bu derste `saklanan.txt` sabit bir ad olarak yalnız ölçümü tekrarlanabilir kılmak için kullanıldı.

#### Sınırlayıcı Ölçüm

Önceki bölümde hata koduyla dönen alan vardı. Sınırı daha da aşağı çekersek bu da kalmaz. `post_max_size` aşılınca PHP isteğin gövdesini hiç ayrıştırmaz: ne dosya ne metin alanı elde kalır.

```bash
# tavan.sh — post_max_size asildiginda ne kaliyor
head -c 5000 /dev/zero | tr '\0' 'a' > buyuk.txt
php -S 127.0.0.1:8083 -d display_errors=0 -d post_max_size=1K >/dev/null 2>&1 &
pid=$!
for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8083/yukle.php && break; sleep 0.1; done
curl -s -F 'dosya=@buyuk.txt' -F 'baslik=dorduncu' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/yukle.php
kill $pid
```

```
files=0 post=0
durum=200
```

`$_FILES` da `$_POST` da boştur ve durum kodu 200'dür. Hata koduyla gelen bir alan bile yok: istek, hiç alan göndermemiş bir istekten ayırt edilemez. **IY20.** `display_errors=0` ile ölçüldü; ayar açıkken PHP'nin başlangıçta ürettiği uyarı gövdeye düşebilir ve ders o metni basmaz. Bu, önceki bölümdeki sınıfın alt sınırıdır: dönüş kanalı, kanalın konuşacağı değerin kendisi geldiğinde işe yarar. Değer hiç gelmediğinde ise okunacak hata kodu da kalmaz. Bu durumu kodun fark etmesinin tek yolu, beklediği alanların yokluğunu ayrı bir koşul olarak sınamasıdır.

#### Özet

- `$_FILES` her dosya alanı için `name`, `size`, `error` ve `tmp_name` taşır; `move_uploaded_file` yalnız bu isteğin aldığı dosyayı taşır.
- Bu dersin ölçü sınıfı **dönüş**tür: yüklenmemiş dosyada `move_uploaded_file` uyarı vermeden `false` döndürür.
- `upload_max_filesize` aşılınca alan `$_FILES` içinde kalır, `size` 0 ve `error` `UPLOAD_ERR_INI_SIZE` olur; istek yine 200 verir.
- Dosya alanı gönderilmezse `$_FILES` boş, `$_POST` dolu kalır.
- `post_max_size` aşılınca `$_POST` ve `$_FILES` ikisi de boşalır; hata kodu taşıyan alan bile kalmaz ve istek sinyalsiz boş bir istek gibi görünür.

#### Sonraki Adım

Yükleme dersinde kod, `yukle.php` gibi bir betiğe kendi yazdığı dosyayı verip bir yanıt aldı. Bir betiğin başka bir dosyayı kendi içine çekmesi ise ayrı bir işlemdir ve aynı sorunun yeni bir biçimini getirir: çekilecek dosya bulunamazsa ne olur? Dosya Dahil Etme dersi, bulunamayan bir dosyanın dahil edilmesinin hangi kanaldan duyulduğunu ve bu kanalın dahil etme türüne göre nasıl değiştiğini ölçüyor.

### Dosya Dahil Etme

Source: https://academia.sh/tr/kurslar/php-temelleri/dosya-dahil-etme

Bulunamayan bir dosyanın dahil edilmesinin include, require ve include_once ile hangi kanaldan duyulduğu; ardından on dört dersin ölçümünü bir tabloda toplayan kurs kapanışı.

Dosya Yükleme dersi, istekle gelen bir dosyanın `false` döndüren bir fonksiyona verilmesiyle bitti. Bir betiğin başka bir dosyayı kendi içine çekmesi aynı soruyu başka bir düzenekte sorar: çekilecek dosya yoksa bu hangi kanaldan duyulur? Yanıt, dahil etmenin hangi biçimiyle yapıldığına göre değişir. Ders, çekirdeğin on dördüncü gözlemini ve onun karşıtlarını ölçer; ardından kursun tamamını tek tabloda toplar.

Ölçüm yine kursun çekirdeğindeki `kanal.php` dosyasını kullanır. Her ders kendi çalışma dizininde koştuğu için dosya burada da birebir verilir.

```php
<?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];
}
```

#### Eksik Dosya `include` ile Uyarı Verir

İlk betik dört şeyi ölçer: var olmayan bir dosyanın `include` ile çekilmesi, var olan bir dosyanın `include_once` ile iki kez çekilmesi ve o dosyanın kaç kez çalıştığı. Var olan dosyayı betik kendisi yazar; böylece ölçüm bir önceki koşumdan kalan dosyaya bağlı olmaz.

```php
<?php
// dahil.php — eksik dosya ve include_once hangi kanaldan duyulur
require 'kanal.php';

file_put_contents('parca.php', "<?php\n\$GLOBALS['yuklenme'] = (\$GLOBALS['yuklenme'] ?? 0) + 1;\nreturn 7;\n");

$olcumler = [
    'eksik dahil dosyasi' => fn() => include 'yok.php',
    'ilk include_once'    => fn() => include_once 'parca.php',
    'ikinci include_once' => fn() => include_once 'parca.php',
];

foreach ($olcumler as $ad => $olcum) {
    [$sinif, $kanit] = dinle($olcum);
    printf("%-24s%-10s%s\n", $ad, $sinif, $kanit);
}
echo 'parca.php calisma sayisi ', $GLOBALS['yuklenme'], "\n";
```

```
eksik dahil dosyasi     uyari     E_WARNING, sonuc false
ilk include_once        sessiz    sonuc int 7
ikinci include_once     sessiz    sonuc true
parca.php calisma sayisi 1
```

İlk satır çekirdeğin `eksik dahil dosyasi` gözlemidir: `include` bulamadığı dosya için `E_WARNING` bildirdi, `false` döndürdü ve yürütme sürdü. Bu **uyarı** sınıfıdır ve bu dersin ölçü sınıfıdır. **IY21.** Döndürülen `false` da vardır, ama sınıfı `dinle()`'nin sorduğu sıra belirler: uyarı dönüşten önce sorulur. Bu yüzden satır **dönüş** değil, **uyarı** yazar.

Sonraki iki satır `include_once` içindir. İlk çağrı dosyayı çalıştırır ve dosyanın `return` ettiği `7` değerini verir. İkinci çağrı dosyayı bir daha çalıştırmaz ve yalnız `true` döndürür; son satır dosyanın toplam bir kez çalıştığını doğrular. **IY22.** Bu, `include_once` ile `require_once` için aynı davranıştır ve aynı fonksiyonun iki kez tanımlanması gibi çakışmaları önler. Ama ikinci çağrının `7` değil `true` vermesi sessiz bir farktır: dosyanın dönüş değerini kullanan kod, ikinci çağrıda başka bir değer alır.

#### Require Uyarının Ardından Durdurur

`require` aynı eksikliği daha sert karşılar. Yürütmeyi sürdürmesi için bir `false` bırakmaz; dosya bulunamazsa durur. Bunun hangi biçimde olduğu ölçülür, varsayılmaz. İki ölçüm yapılır: aynı işlemde bir `try` ile ve ayrı bir yürütmede çıkış koduyla.

```php
<?php
// zorunlu.php — require ile eksik dosya hangi kanaldan duyulur
require 'kanal.php';

[$sinif, $kanit] = dinle(fn() => require 'yok.php');
printf("%-24s%-10s%s\n", 'eksik require', $sinif, $kanit);
echo 'once duyulan uyari: ', implode(',', $GLOBALS['duyulan_uyarilar']), "\n";

[$sinif, $kanit] = ayri_yurut('echo "once\n"; require "yok.php"; echo "sonra\n";');
printf("%-24s%-10s%s\n", 'ayri yurutme', $sinif, $kanit);
```

```
eksik require           durdurur  Error
once duyulan uyari: E_WARNING
ayri yurutme            durdurur  cikti 1 satir, cikis 255
```

**IY23.** Sınıf **durdurur**dur: `require` bir `Throwable` fırlattı ve ayrı yürütme 255 koduyla çıktı, `sonra` satırı basılmadı. Kanal birden fazla sinyal verdi: `require` önce `E_WARNING` bildirdi, ardından yürütmeyi kesti. Çekirdeğin sorusu en güçlü kanalı seçer, bu yüzden satırın sınıfı uyarı değil durdurur'dur. Kursun ortak kuralı budur: aynı olayda iki kanal konuşuyorsa ölçü, yürütmeyi kesen kanaldır.

#### Yol Çalışma Dizinine Mi Bağlı

Göreli bir yol, betiğin kendi dizinine değil, o anki çalışma dizinine bağlı olabilir. Aynı dosya `__DIR__` ile verilince bu bağımlılık kalkar. `__DIR__`, çalışan dosyanın bulunduğu dizinin yoludur; değeri ortama bağlıdır ve bu derste basılmaz, yalnız onunla kurulan yolun çalışıp çalışmadığı basılır.

```php
<?php
// yol.php — dahil edilen yolun calisma dizinine baglanip baglanmadigi
require 'kanal.php';

file_put_contents('parca.php', "<?php\nreturn 7;\n");
chdir('/');

$olcumler = [
    'goreli ./ yolu'  => fn() => include './parca.php',
    '__DIR__ ile yol' => fn() => include __DIR__ . '/parca.php',
];

foreach ($olcumler as $ad => $olcum) {
    [$sinif, $kanit] = dinle($olcum);
    printf("%-24s%-10s%s\n", $ad, $sinif, $kanit);
}
```

```
goreli ./ yolu          uyari     E_WARNING, sonuc false
__DIR__ ile yol         sessiz    sonuc int 7
```

**IY24.** Çalışma dizini değiştikten sonra `./` ile başlayan yol dosyayı bulamadı ve uyarı verdi; `__DIR__` ile kurulan yol bulundu ve dosyanın `7` değeri döndü. Yol dahil edilen dosyanın yerini betiğin konumuna bağlıyorsa `__DIR__` kullanılır. Bu, hatanın kanalını değiştirmez; yalnız hatanın çıkma olasılığını azaltır.

#### Sınırlayıcı Ölçüm

`include`'un uyarı kanalı bir koşula bağlıdır: dosya **bulunamazsa** geçerlidir. Dosya bulunur ve sözdizimi bozuksa durum değişir. Aşağıdaki betik bozuk bir dosyayı `include` ile çeker.

```php
<?php
// bozuk.php — bulunan ama sozdizimi bozuk dosyanin include ile duyulusu
require 'kanal.php';

file_put_contents('bozuk_parca.php', "<?php\n\$x = ;\n");

[$sinif, $kanit] = dinle(fn() => include 'bozuk_parca.php');
printf("%-24s%-10s%s\n", 'bozuk dahil dosya', $sinif, $kanit);
```

```
bozuk dahil dosya       durdurur  ParseError
```

**IY25.** `include` bu kez `ParseError` fırlattı ve sınıf **durdurur**dur. Dosyanın bulunması, kanalı uyarıdan durdurura çevirdi. Bu, Sözdizim ve Etiketler dersindeki özetin ölçülmüş biçimidir: dahil edilen dosyadaki sözdizim hatası ana dosya çalıştıktan sonra yakalanabilir bir `ParseError` olarak fırlar. Sonuç olarak `include` için "hata yürütmeyi kesmez" cümlesi doğru değildir; doğru cümle "eksik dosya yürütmeyi kesmez"dir.

#### Özet

- Bulunamayan bir dosya `include` ile `E_WARNING` bildirir ve `false` döndürür; yürütme sürer. Bu dersin ölçü sınıfı **uyarı**dır (`eksik dahil dosyasi`).
- `require` aynı eksiklikte önce uyarı bildirir, sonra yürütmeyi keser; sınıfı **durdurur**dur.
- `include_once` ikinci çağrıda dosyayı çalıştırmaz ve `true` döndürür; ilk çağrıdaki dönüş değeri kaybolur.
- `./` ile başlayan yol çalışma dizinine bağlıdır; `__DIR__` ile kurulan yol betiğin konumuna bağlıdır.
- Dosya bulunur ama sözdizimi bozuksa `include` da `ParseError` fırlatır ve durdurur.

#### Kurs Kapanışı

Kurs boyunca tek soru soruldu: bu sorun hangi kanaldan duyuluyor? On dört ders, her biri bir düzeneği ölçtü ve her biri kendi sınıfını bu dört addan biriyle adlandırdı. Tablo, her satırda dersin ölçtüğü düzeneği, kanalını ve o kanalın bittiği yeri gösterir.

| Ders | Ölçülen düzenek | Kanal | Sınırlayıcı ölçüm |
|---|---|---|---|
| Yürütme Modeli | on dört gözlemli çekirdek | durdurur | sunucuda her istek baştan başlar |
| Sözdizim ve Etiketler | ayrıştırma ile yürütme hatası | durdurur | sözdizim hatası yürütmeyi başlamadan keser |
| Değişkenler ve Veri Tipleri | tip değere bağlı | sessiz | `strict_types` çağıranın dosyasını bağlar |
| İşleçler | sayı önekli dizgi | uyarı | `??` uyarıyı susturur |
| Denetim Akışı | karşılıksız `match` | durdurur | `default` kolu durdurmayı sessize çevirir |
| Fonksiyonlar | fazla argüman | sessiz | yerleşik fonksiyon `ArgumentCountError` verir |
| İndeksli ve İlişkisel Diziler | anahtar normalleşmesi | sessiz | ondalık anahtar `E_DEPRECATED` verir |
| Çok Boyutlu Diziler | eksik iç anahtar | uyarı | `??` yolun tamamını sessize çevirir |
| Dizi Fonksiyonları | `array_search` bulamaz | dönüş | `0` gevşek karşılaştırmada `false` olur |
| Dizgi İşleme | bozuk JSON | dönüş | `JSON_THROW_ON_ERROR` durdurur |
| Süper Küresel Değişkenler | eksik sorgu anahtarı | uyarı | dizi gelince `TypeError`, durum kodu değişir |
| Form İşleme | geçersiz form sayısı | dönüş | geçerli `0` başarısızlıkla aynı görünür |
| Dosya Yükleme | yüklenmemiş dosya | dönüş | `post_max_size` aşımı iki diziyi de boşaltır |
| Dosya Dahil Etme | eksik dahil dosya | uyarı | bozuk sözdizimi `ParseError` verir |

On dört dersin dağılımı 3 sessiz · 4 dönüş · 4 uyarı · 3 durdurur'dur; çekirdek her ders için bir gözlem taşıdığından çekirdeğin dağılımı da aynıdır. Dört sınıf aynı aksaklığın dört farklı bedelidir. Sessiz sınıfta bedeli yanlış sonuç öder, dönüşte çağıranın dikkati, uyarıda bildirimi okuyan göz, durdurur sınıfında ise çalışan yürütme.

Tablonun ikinci bir okuması vardır: her satırın son sütunu, o kanalın bir koşula bağlı olduğunu gösterir. `??` uyarıyı sessize çevirir, `JSON_THROW_ON_ERROR` dönüşü durdurur sınıfına taşır, `default` kolu durdurmayı sessize çevirir. Kanal, düzeneğin sabit bir özelliği değil, çağıranın yazdığı koda göre kayan bir özelliğidir. Bu nedenle kanalı varsaymak yerine her yeni düzenekte aynı soru sorulur.

Kurs bu soruyla PHP'nin sorunlarını duyma biçimlerini ayırdı; sınıflandırmaların güvenlik sonuçları M24 ve M25'in konusudur. Sıradaki kurs PHP'de sınıfları, arayüzleri ve ad alanlarını ele alır.

