İçeriğe geç
Educora
Orta17 dk8 / 10

İstisnalar ve hata yönetimi

try/catch/finally ile hataları yakala, Exception ve Error hiyerarşisini kavra, kendi istisna sınıfını oluştur ve hata raporlamayı geliştirme ile canlı ortam için doğru ayarla.

Kendini test et
Bu derste öğreneceklerin
  • throw, try, catch ve finally ile istisna fırlatmak ve yakalamak
  • Throwable, Exception ve Error hiyerarşisini açıklamak ve özel bir istisna yazmak
  • Hata raporlamayı geliştirme ve canlı ortam için doğru yapılandırmak

Ödeme sistemi yanıt vermiyor, kullanıcı bozuk bir JSON göndermiş, veritabanına ulaşılamıyor... Gerçek bir sitede bu tür durumlar her gün yaşanır. İyi bir program “beyaz ekran” göstermez: sorunu yakalar, kullanıcıya anlaşılır bir mesaj verir, ayrıntıları ise günlüğe (log) yazar. PHP'de bunun temel aracı istisnalardır (exceptions).

try, catch ve finally

Kod bir sorun fark ettiğinde throw ile bir istisna fırlatır: fonksiyon hemen durur ve istisna, onu yakalayan bir catch bloğu bulana kadar çağrı zinciri boyunca yukarı çıkar. Riskli kod try bloğuna yazılır, catch belirli türdeki bir istisnayı yakalar, finally ise her durumda, istisna olsa da olmasa da çalışır.

PHP
<?php

function divide(int $a, int $b): float
{
    if ($b === 0) {
        throw new InvalidArgumentException("Cannot divide by zero");
    }
    return $a / $b;
}

foreach ([4, 0] as $b) {
    try {
        $result = divide(12, $b);
        echo "12 / $b = $result\n";
    } catch (InvalidArgumentException $e) {
        echo "Error: ", $e->getMessage(), "\n";
    } finally {
        echo "-- done with $b\n";
    }
}
Beklenen çıktı
12 / 4 = 3
-- done with 4
Error: Cannot divide by zero
-- done with 0

$b = 0 olduğunda echo satırı hiç çalışmadı: throw'dan sonra kontrol doğrudan catch'e geçti. finally ise döngünün iki turunda da çalıştı. İstisna nesnesinin kullanışlı metotları: getMessage() mesajı, getCode() kodu, getFile() ve getLine() hatanın yerini, getPrevious() ise önceki istisnayı verir.

Exception ve Error

PHP 7'den beri fırlatılabilen her şeyin ortak atası Throwable arayüzüdür. Ondan iki kol ayrılır: **Exception, programın kendi mantığındaki sorunlar (hatalı argüman, bulunamayan dosya, başarısız veritabanı bağlantısı); Error** ise PHP'nin iç hataları (yanlış tür, sıfıra bölme, var olmayan bir metodun çağrılması).

KolSık görülen sınıflarNe zaman
ExceptionInvalidArgumentException, RuntimeException, JsonException, PDOExceptionbeklenen sorunlar: yakalanıp ele alınabilir
ErrorTypeError, ValueError, DivisionByZeroError, ArgumentCountError, UnhandledMatchErrorgenellikle koddaki bir hata: düzeltilmesi gerekir
PHP
<?php

$tests = [
    fn() => intdiv(10, 0),
    fn() => json_decode("{bad json", flags: JSON_THROW_ON_ERROR),
    fn() => throw new RuntimeException("Disk is full"),
];

foreach ($tests as $test) {
    try {
        $test();
    } catch (Throwable $e) {
        echo get_class($e), ": ", $e->getMessage(), "\n";
    }
}
Beklenen çıktı
DivisionByZeroError: Division by zero
JsonException: Syntax error
RuntimeException: Disk is full

Kendi istisnaların

Kendi istisna sınıfını oluşturmak için mevcut bir sınıftan, genellikle RuntimeException ya da DomainException'dan türet. Adı sorunu açıkça anlatsın; gerekirse ek bilgiyi bir özellikte sakla. Böylece catch bloğu tam olarak bu duruma kesin bir tepki verebilir.

PHP
<?php

class InsufficientFundsException extends RuntimeException
{
    public function __construct(public readonly float $missing)
    {
        parent::__construct("Not enough money: missing $missing AZN");
    }
}

class Wallet
{
    public function __construct(private float $balance) {}

    public function pay(float $amount): void
    {
        if ($amount > $this->balance) {
            throw new InsufficientFundsException($amount - $this->balance);
        }
        $this->balance -= $amount;
        echo "Paid $amount, left {$this->balance}\n";
    }
}

$wallet = new Wallet(30);
try {
    $wallet->pay(12);
    $wallet->pay(25.5);
    $wallet->pay(1);
} catch (InsufficientFundsException $e) {
    echo $e->getMessage(), "\n";
    echo "Top up at least ", ceil($e->missing), " AZN\n";
}
Beklenen çıktı
Paid 12, left 18
Not enough money: missing 7.5 AZN
Top up at least 8 AZN
Eski: dönüş değerini denetlemek
$data = json_decode($input);
if ($data === null) {
    // broken JSON or the value null?
    echo json_last_error_msg();
}
Modern: istisna
try {
    $data = json_decode($input, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    echo "Invalid JSON: ", $e->getMessage();
}
null hem bir hata hem de geçerli bir JSON değeri (null) anlamına gelebilir. İstisna ise hatayı kesin biçimde bildirir.

Uyarılar ve hata raporlama

Bazı sorunlar istisna fırlatmaz, yalnızca uyarı verir: örneğin var olmayan bir değişken Warning: Undefined variable $x, kullanımdan kalkmış bir fonksiyon ise Deprecated mesajı üretir. Hangi mesajların görüneceğini php.ini ayarları belirler. Altın kural: geliştirme sırasında her şeyi gör; canlı ortamda (production) ise kullanıcıya hiçbir şey gösterme ve her şeyi günlüğe yaz.

Ayar (php.ini)GeliştirmeCanlı ortam
error_reportingE_ALLE_ALL
display_errorsOnOff
log_errorsOnOn
Kodda da açabilirsin: error_reporting(E_ALL); ini_set('display_errors', '1');

Önemli noktalar

  • throw bir istisna fırlatır; try riskli kodu içerir, catch belirli bir türü yakalar, finally ise her zaman çalışır.
  • Throwable'ın iki kolu vardır: Exception (beklenen sorunlar) ve Error (PHP'nin iç hataları).
  • Kendi istisnanı mevcut bir sınıftan türet ve anlamlı bir ad ver; ek bilgiyi bir özellikte sakla.
  • PHP 8'de throw bir ifadedir: $x = $arr[$k] ?? throw new ...;
  • Geliştirmede display_errors = On, canlı ortamda ise Off ve log_errors = On olmalıdır.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
finally bloğu ne zaman çalışır?