PHP'de istisnaların yanında ikinci bir hata mekanizması daha vardır: tanılama hataları (diagnostic errors) ya da eski adıyla geleneksel hatalar. Warning: Undefined variable, Deprecated: ... veya Notice: ... gibi mesajlar bu mekanizmadan gelir. Her birinin bir seviyesi vardır (E_WARNING, E_NOTICE, E_DEPRECATED ve diğerleri); hangi seviyelerin raporlanacağını error_reporting() belirler, bu hataları kendi kodunuzla ele almak için set_error_handler() kullanılır ve çoğu zaman en temiz yol onları ErrorException nesnesine çevirip istisna olarak yakalamaktır.
Bu yazı konunun dil tarafına odaklanıyor: sabitler ve değerleri, bit maskesiyle seviye seçmek, özel hata işleyicisi yazmak, @ operatörünün gerçekte ne yaptığı, trigger_error() ile kendi uyarılarınızı üretmek ve PHP 8.4 ile 8.5'teki ilgili değişiklikler. Sunucu tarafındaki display_errors ve log_errors gibi php.ini ayarları için PHP hosting rehberine, throw ve try/catch için PHP try/catch ve exception yazısına bakabilirsiniz.
Tanılama hataları istisnalardan nasıl ayrılır?
Bir istisna fırlatıldığında normal akış hemen durur ve yığın çözülür. Tanılama hatalarının çoğu ise akışı durdurmaz: PHP mesajı üretir, raporlama ayarlarına göre gösterir ya da günlüğe yazar ve bir sonraki satırdan devam eder. Yalnızca E_ERROR gibi ölümcül seviyeler betiği sonlandırır. Kılavuza göre PHP 8.0'dan beri varsayılan olarak tüm tanılama hataları raporlanıyor; daha önce E_NOTICE ve E_DEPRECATED varsayılan olarak raporlanmıyordu.
<?php
echo "Önce\n";
echo $tanimsiz;
echo "\nSonra\n";
$dizi = ['a' => 1];
echo $dizi['b'];
echo "\nYine devam\n";Önce
Warning: Undefined variable $tanimsiz in /yol/ornek.php on line 3
Sonra
Warning: Undefined array key "b" in /yol/ornek.php on line 6
Yine devamÖrnekte iki uyarı üretildi ama program son satıra kadar çalıştı. Bu davranış iki yönlüdür: küçük bir hata tüm sayfayı çökertmez, ama fark edilmeyen bir uyarının ardından kod yanlış bir değerle (burada null) çalışmaya devam eder. Tanımsız değişken ve dizi anahtarı uyarılarının çözümleri için Undefined variable ve array key uyarısı yazısına bakın.
Kılavuz şunu da hatırlatır: sürümler arasında tanılama mesajları eklenebilir, kaldırılabilir, seviyesi değişebilir ya da bir Throwable hatasına yükseltilebilir. Örneğin eskiden uyarı veren birçok geçersiz argüman durumu artık TypeError veya ValueError fırlatıyor. Bu yüzden kodunuzu yeni sürüme taşırken hem uyarılara hem istisnalara bakmanız gerekir.
Hata seviyesi sabitleri ve değerleri

Her seviye bir tamsayı sabitidir ve değerler ikinin kuvvetleridir; böylece bit işlemleriyle birleştirilip maskelenebilirler. Aşağıdaki tablo kılavuzdaki sabitleri ve değerlerini özetliyor:
| Sabit | Değer | Anlamı |
|---|---|---|
| E_ERROR | 1 | Ölümcül çalışma zamanı hatası, betik durur |
| E_WARNING | 2 | Ölümcül olmayan uyarı, betik devam eder |
| E_PARSE | 4 | Derleme anında sözdizimi hatası |
| E_NOTICE | 8 | Hata olabilecek ama normal akışta da görülebilecek durum |
| E_CORE_ERROR / E_CORE_WARNING | 16 / 32 | PHP başlarken oluşan hata ve uyarılar |
| E_COMPILE_ERROR / E_COMPILE_WARNING | 64 / 128 | Zend motorunun derleme hatası ve uyarısı |
| E_USER_ERROR | 256 | trigger_error() ile üretilen hata (8.4 ile kullanımı deprecated) |
| E_USER_WARNING | 512 | trigger_error() ile üretilen uyarı |
| E_USER_NOTICE | 1024 | trigger_error() ile üretilen bildirim (varsayılan) |
| E_STRICT | 2048 | Kullanılmıyor, 8.4 ile sabit deprecated |
| E_RECOVERABLE_ERROR | 4096 | Eski yakalanabilir ölümcül hata, pratikte neredeyse kullanılmıyor |
| E_DEPRECATED | 8192 | Gelecek sürümlerde çalışmayacak kod |
| E_USER_DEPRECATED | 16384 | trigger_error() veya #[\Deprecated] ile üretilen deprecation |
| E_ALL | 30719 | Tüm seviyeler (8.4 öncesinde 32767) |
Son satır önemli bir sürüm notu içerir: E_STRICT seviyesi PHP 8.4'te motordan kaldırıldığı için E_ALL değeri 32767'den 30719'a düştü. Kodunuzda E_ALL yerine sabit sayı yazdıysanız bu fark sorun çıkarabilir; kılavuz bu yüzden sayılar yerine adlandırılmış sabitleri kullanmayı önerir.
error_reporting() ile seviye seçmek
error_reporting() argümansız çağrıldığında geçerli seviyeyi döndürür. Bir değer verildiğinde yeni seviyeyi ayarlar ve önceki değeri döndürür; bu sayede değişikliği daha sonra geri alabilirsiniz. Seviyeleri birleştirmek için |, birini çıkarmak için & ~ kullanılır, belirli bir seviyenin açık olup olmadığını & ile sınarsınız.
<?php
echo E_ERROR, ' ', E_WARNING, ' ', E_NOTICE, ' ', E_DEPRECATED, "\n"; // çıktı: 1 2 8 8192
echo E_USER_WARNING, ' ', E_USER_DEPRECATED, "\n"; // çıktı: 512 16384
echo E_ALL, "\n"; // çıktı: 30719
$eski = error_reporting(E_ALL & ~E_DEPRECATED);
echo $eski, ' ', error_reporting(), "\n"; // çıktı: 30719 22527
var_dump((bool) (error_reporting() & E_DEPRECATED)); // çıktı: bool(false)
var_dump((bool) (error_reporting() & E_WARNING)); // çıktı: bool(true)E_ALL & ~E_DEPRECATED ifadesi "her şey, deprecation hariç" anlamına gelir ve sonucu 22527 olur. -1 değeri de tüm seviyeleri açar ve kılavuza göre gelecekte eklenecek seviyeleri de kapsar. Aşağıdaki örnek, seviyeyi çalışma anında daraltmanın etkisini gösteriyor:
<?php
error_reporting(E_ALL & ~E_WARNING);
echo $tanimsiz; // uyarı gösterilmez
echo "Sessiz geçti\n";
error_reporting(E_ALL);
echo $tanimsiz2; // bu kez görünür
echo "\n";Sessiz geçti
Warning: Undefined variable $tanimsiz2 in /yol/ornek.php on line 7Kılavuz iki önemli hatırlatma yapar. Birincisi, geliştirme ortamında seviyenin her zaman E_ALL olması gerekir ki sorunlar görülüp düzeltilsin. İkincisi, bazı hatalar betik çalışmaya başlamadan önce oluşabildiği için seviyenin yalnızca kodda değil yapılandırmada da ayarlanması önerilir. Seviyeyi düşürmek hatayı düzeltmez, yalnızca görünmez kılar; bir uyarıyı susturmak yerine nedenini gidermek neredeyse her zaman doğru tercihtir.
set_error_handler ile kendi işleyicinizi yazmak

set_error_handler() ile kaydettiğiniz fonksiyon, PHP'nin kendi işleyicisi yerine çağrılır. Fonksiyon dört argüman alır: seviye ($errno), mesaj ($errstr), dosya ve satır. Dönüş değeri belirleyicidir: false döndürürseniz PHP'nin standart işleyicisi de çalışır; başka bir değer döndürürseniz standart işleyici tamamen atlanır.
<?php
set_error_handler(function (int $no, string $mesaj, string $dosya, int $satir): bool {
echo "[$no] $mesaj (satır $satir)\n";
return true; // PHP'nin kendi işleyicisi çalışmasın
});
echo $yok;
$liste = [];
echo $liste[3];
trigger_error('Stok azaldı', E_USER_WARNING);
echo "Akış sürüyor\n";[2] Undefined variable $yok (satır 7)
[2] Undefined array key 3 (satır 9)
[512] Stok azaldı (satır 10)
Akış sürüyorDikkat edilmesi gereken birkaç kural var. İşleyici, error_reporting() ayarından bağımsız olarak çağrılır; seviyeye saygı göstermek istiyorsanız bunu işleyicinin içinde kendiniz kontrol etmelisiniz. İşleyici döndükten sonra betik hatanın oluştuğu satırdan sonraki deyimle devam eder; durdurmak gerekiyorsa bu da işleyicinin sorumluluğundadır. Son olarak E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR ve E_COMPILE_WARNING seviyeleri kullanıcı işleyicisiyle hiç ele alınamaz.
İkinci parametre $error_levels bir maskedir ve işleyicinizin hangi seviyelerde çağrılacağını sınırlar. Maske dışındaki seviyeler PHP'nin standart işleyicisine gider:
<?php
set_error_handler(function (int $no, string $m): bool {
echo "yalnızca uyarılar: $m\n";
return true;
}, E_WARNING | E_USER_WARNING);
echo $tanimsiz; // E_WARNING: işleyiciye gider
trigger_error('eski API', E_USER_DEPRECATED); // maske dışında: PHP gösteriryalnızca uyarılar: Undefined variable $tanimsiz
Deprecated: eski API in /yol/ornek.php on line 8PHP 8.0 ile işleyicinin beşinci parametresi $errcontext tamamen kaldırıldı. Eski bir kod tabanında bu parametreyi varsayılan değer vermeden tanımlayan işleyiciler, kılavuzun belirttiği gibi "çok az argüman" hatasıyla karşılaşır; parametreyi silmeniz yeterlidir.
Uyarıları ErrorException ile istisnaya çevirmek
Uyarılar try/catch ile yakalanamaz. Hem istisnaları hem uyarıları tek bir yolla ele almak istiyorsanız kılavuzun önerdiği kalıp, işleyici içinde bir ErrorException fırlatmaktır. Bu sınıf Exception sınıfını genişletir ve ek olarak hatanın seviyesini taşır; seviyeyi getSeverity() ile okursunuz.
<?php
set_error_handler(function (int $no, string $mesaj, string $dosya, int $satir): bool {
if (!(error_reporting() & $no)) {
return false; // @ ile susturulmuş ya da raporlanmayan seviye
}
throw new ErrorException($mesaj, 0, $no, $dosya, $satir);
});
try {
$bolen = 0;
$sayi = 10 / $bolen;
} catch (DivisionByZeroError $e) {
echo 'Error: ', $e->getMessage(), "\n";
}
try {
$veri = unserialize('bozuk veri');
} catch (ErrorException $e) {
echo 'ErrorException: ', $e->getMessage(), "\n";
echo 'Seviye: ', $e->getSeverity(), ' (E_WARNING = ', E_WARNING, ")\n";
}
$sonuc = @unserialize('bozuk veri');
var_dump($sonuc);Error: Division by zero
ErrorException: unserialize(): Error at offset 0 of 10 bytes
Seviye: 2 (E_WARNING = 2)
bool(false)Örnek üç şeyi gösteriyor. Sıfıra bölme zaten bir DivisionByZeroError istisnasıdır ve işleyiciye hiç uğramaz. unserialize() ise bozuk veride uyarı üretir; işleyici bunu ErrorException olarak fırlatır ve normal bir istisna gibi yakalarız. Son satırda @ ile susturulan çağrıda istisna fırlatılmadı, çünkü işleyici error_reporting() & $no kontrolü yapıyor. Kılavuz bu kontrolü özellikle vurgular: bu kontrol yapılmazsa @ operatörü bozulur ve susturulmuş hatalar için bile istisna fırlatılır.
@ operatörü gerçekte ne yapar?
@ işareti önüne konduğu ifadenin üreteceği tanılama mesajlarını bastırır. Değeri alınabilen her şeyin önüne yazılabilir (değişken, fonksiyon çağrısı, include gibi), ama fonksiyon veya sınıf tanımının ya da if ve foreach gibi yapıların önüne yazılamaz. Mesaj kaybolmaz: error_get_last() ile son hatanın tipini, mesajını, dosyasını ve satırını okuyabilirsiniz.
<?php
$icerik = @file_get_contents('olmayan-dosya.txt');
var_dump($icerik);
$son = error_get_last();
echo $son['type'], "\n";
echo $son['message'], "\n";
error_clear_last();
var_dump(error_get_last());bool(false)
2
file_get_contents(olmayan-dosya.txt): Failed to open stream: No such file or directory
NULLBurada dosya okunamadığında file_get_contents() false döndürdü ve uyarı ekrana basılmadı, ama error_get_last() mesajı sakladı. error_clear_last() çağrısından sonra fonksiyon null döndürür. Son hata her yeni hatada değiştiği için bu kontrolü hemen yapmanız gerekir. Dosya işlemlerinde hata kontrolünün daha ayrıntılı örnekleri PHP dosya okuma ve yazma yazısında var.
Daha az bilinen bir ayrıntı: @ özel işleyicinizi devre dışı bırakmaz. İşleyici yine çağrılır; yalnızca o sırada error_reporting() farklı bir değer döndürür. PHP 8.0'dan beri bu değer sıfır değil, ölümcül seviyelerin birleşimidir (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE):
<?php
set_error_handler(function (int $no, string $mesaj): bool {
echo "işleyici çağrıldı, error_reporting() = ", error_reporting(), "\n";
return true;
});
$x = @$tanimsiz;
echo "bitti\n";işleyici çağrıldı, error_reporting() = 4437
bitti4437 sayısı bu altı sabitin toplamıdır (1 + 16 + 64 + 256 + 4096 + 4). Eski kodlarda sık görülen if (error_reporting() === 0) kontrolü bu yüzden PHP 8'de artık çalışmaz; doğru yol, yukarıdaki gibi error_reporting() & $no ile seviyeyi bit düzeyinde sınamaktır. Kılavuz ayrıca PHP 8.0 öncesinde @ operatörünün betiği sonlandıran kritik hataları bile susturabildiğini, örneğin yanlış yazılmış bir fonksiyon adında betiğin hiçbir iz bırakmadan durabildiğini hatırlatır. Bu artık mümkün değil.
trigger_error ile kendi uyarılarınızı üretmek
trigger_error() kendi kodunuzdan bir tanılama mesajı üretir. Yalnızca E_USER_* ailesini kabul eder, varsayılanı E_USER_NOTICE seviyesidir ve başka bir seviye verildiğinde PHP 8.0'dan beri ValueError fırlatır. Mesaj 1024 bayttan uzunsa kesilir ve HTML olarak kaçırılmaz; tarayıcıda gösterecekseniz kendiniz kaçırmalısınız.
<?php
trigger_error('Eski fonksiyon kullanılıyor', E_USER_DEPRECATED);
trigger_error('Ciddi sorun', E_USER_ERROR);
echo "Bu satıra gelinmez\n";Deprecated: Eski fonksiyon kullanılıyor in /yol/ornek.php on line 2
Deprecated: Passing E_USER_ERROR to trigger_error() is deprecated since 8.4, throw an exception or call exit with a string message instead in /yol/ornek.php on line 3
Fatal error: Ciddi sorun in /yol/ornek.php on line 3
Stack trace:
#0 /yol/ornek.php(3): trigger_error('Ciddi sorun', 256)
#1 {main}Çıktıda PHP 8.4'te gelen değişikliği görüyorsunuz: E_USER_ERROR geçirmek artık deprecated ve kılavuz bunun yerine bir istisna fırlatmayı ya da exit() çağırmayı öneriyor. Ölümcül hata mesajının altındaki yığın izi de PHP 8.5 ile gelen bir özelliktir; 8.5'ten itibaren azami çalışma süresinin aşılması gibi ölümcül hatalar da mesajlarında geri izleme (backtrace) taşıyor. Kendi kütüphanenizde bir fonksiyonu kullanımdan kaldırmak için E_USER_DEPRECATED kullanabilir ya da PHP 8.4 ile gelen #[\Deprecated] özniteliğine bakabilirsiniz; ayrıntılar PHP attributes ve PHP 8.4 deprecated uyarıları yazılarında.
İşleyici yığını: restore_error_handler ve get_error_handler
Her set_error_handler() çağrısı önceki işleyiciyi bir iç yığına kaydeder; restore_error_handler() en üsttekini çıkarıp bir öncekini, kayıt edildiği maskeyle birlikte geri getirir. Fonksiyonu set_error_handler() çağrısından daha fazla kez çağırırsanız PHP'nin yerleşik işleyicisi devreye girer ve bir hata oluşmaz. PHP 8.5 ile gelen get_error_handler() o anda etkin olan kullanıcı işleyicisini döndürür, yerleşik işleyici etkinse null verir.
<?php
function ilk(int $no, string $m): bool { echo "ilk: $m\n"; return true; }
function ikinci(int $no, string $m): bool { echo "ikinci: $m\n"; return true; }
set_error_handler('ilk');
set_error_handler('ikinci');
echo $a;
restore_error_handler();
echo $b;
restore_error_handler();
var_dump(get_error_handler()); // PHP 8.5 ve sonrasıikinci: Undefined variable $a
ilk: Undefined variable $b
NULLHepsini birleştirmek: küçük bir hata altyapısı
Şimdiye kadar gördüğünüz parçalar, bir uygulamanın giriş dosyasına konabilecek küçük bir hata altyapısında birleşir. Aşağıdaki örnekte hata işleyicisi deprecation mesajlarını yalnızca günlüğe yazıyor ve diğer tüm tanılama hatalarını ErrorException olarak fırlatıyor. İstisna işleyicisi ise yakalanmayan her şeyi ayrıntısıyla günlüğe yazıp kullanıcıya yalnızca genel bir mesaj gösteriyor. Örnekte "günlük" olarak ekrana yazdırılıyor; gerçek bir uygulamada bu satırlar bir dosyaya ya da merkezi bir kayıt sistemine gider.
<?php
set_error_handler(function (int $no, string $mesaj, string $dosya, int $satir): bool {
if (!(error_reporting() & $no)) {
return false;
}
if ($no === E_DEPRECATED || $no === E_USER_DEPRECATED) {
fwrite(STDOUT, "[günlük] deprecation: $mesaj\n");
return true;
}
throw new ErrorException($mesaj, 0, $no, $dosya, $satir);
});
set_exception_handler(function (Throwable $t): void {
fwrite(STDOUT, '[günlük] ' . get_class($t) . ': ' . $t->getMessage() . ' (satır ' . $t->getLine() . ")\n");
echo "Üzgünüz, bir hata oluştu.\n";
});
trigger_error('eski_fonksiyon() yakında kaldırılacak', E_USER_DEPRECATED);
$ayarlar = ['tema' => 'koyu'];
$dil = $ayarlar['dil'];
echo "Bu satıra gelinmez\n";[günlük] deprecation: eski_fonksiyon() yakında kaldırılacak
[günlük] ErrorException: Undefined array key "dil" (satır 21)
Üzgünüz, bir hata oluştu.Akış şöyle ilerledi: deprecation mesajı istisnaya dönüşmeden günlüğe yazıldı ve program devam etti. Ardından tanımsız dizi anahtarı uyarısı işleyicide ErrorException oldu; bu istisnayı hiçbir try yakalamadığı için istisna işleyicisine ulaştı ve betik orada sonlandı. Bu düzende tanımsız bir anahtar artık sessizce null ile devam eden bir uyarı değil, akışı durduran ve kaydı tutulan bir hatadır. Bu katılık her proje için uygun olmayabilir, ama hataların erken ve görünür biçimde ortaya çıkmasını sağlar. Kılavuzun hatırlattığı gibi işleyiciler betik çalışmaya başladıktan sonra kaydedildiği için, betik başlamadan önce oluşan hatalar (örneğin dosya yüklemeleri sırasında) bu işleyicilere ulaşmaz.
Sık yapılan hatalar

- Seviyeyi düşürerek sorunu gizlemek.
error_reporting(0)veya her satıra@eklemek hatayı düzeltmez; fark edilmeyen uyarılar yanlış verilerle çalışan koda dönüşür. - İşleyicide error_reporting() kontrolünü unutmak.
ErrorExceptionfırlatan bir işleyici bu kontrolü yapmazsa@ile bilinçli olarak susturulan hatalar bile istisnaya dönüşür. - error_reporting() === 0 kontrolüne güvenmek. PHP 8'de
@kullanıldığında bu değer 0 değil 4437 olur. - İşleyiciden hiçbir şey döndürmemek. Dönüş değeri
falseolmadığı sürece PHP'nin kendi mesajı da basılmaz; hatayı günlüğe de yazmıyorsanız sessizce kaybolur. - Sabit sayılar kullanmak.
E_ALLdeğeri 8.4'te değişti;32767gibi sayılar yerine adlandırılmış sabitleri yazın. - E_USER_ERROR ile akışı durdurmak. 8.4'ten beri deprecated; bir istisna fırlatın.
Sık Sorulan Sorular
Warning ile Exception arasındaki fark nedir?
Warning bir tanılama mesajıdır: PHP onu raporlar ve çoğu durumda bir sonraki satırdan devam eder. Exception ise bir nesnedir; fırlatıldığında akış durur ve yakalanana kadar yığın çözülür. Uyarıları istisna gibi ele almak için set_error_handler() içinde ErrorException fırlatırsınız.
Fatal error'ları set_error_handler ile yakalayabilir miyim?
Hayır. E_ERROR, E_PARSE, E_CORE_* ve E_COMPILE_* seviyeleri kullanıcı işleyicisine hiç gelmez. Bununla birlikte PHP 7'den beri birçok eski ölümcül durum yakalanabilir Error istisnalarına dönüştü; bunlar için Uncaught Error çözümü yazısına bakın.
error_reporting() fonksiyonu mu, php.ini ayarı mı?
Fonksiyon, aynı adlı yapılandırma direktifini çalışma anında değiştirir ve yalnızca o betiğin süresi boyunca geçerlidir. Betik başlamadan önce oluşan hatalar fonksiyon çalışmadan gerçekleştiği için kılavuz direktifin yapılandırmada da ayarlanmasını önerir. Sunucu ayarları için PHP hosting rehberine bakabilirsiniz.
Deprecated mesajlarını kapatmalı mıyım?
Kılavuz üretim ortamında E_ALL & ~E_NOTICE & ~E_DEPRECATED düzeyine inmenin mümkün olduğunu, ama çoğu durumda E_ALL değerinin erken uyarı verdiği için hâlâ uygun olduğunu söyler. Deprecation mesajları bir sonraki sürüm yükseltmesinde neyin kırılacağını gösterir; onları görünür tutup günlüğe yazmak ve zamanla düzeltmek en sağlıklısıdır.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: PHP öğrenme rehberi · PHP try/catch ve exception · Undefined variable uyarısı · PHP 8.4 deprecated uyarıları · Uncaught Error çözümü