Stručně: PHP hlásí problémy dvěma způsoby: jako klasické chyby, například varování a upozornění, které se řídí pomocí error_reporting, a jako výjimky, které lze zachytit pomocí try a catch. Od PHP 7 vyhazuje mnoho interních chyb také objekt třídy Error; spolu s Exception implementuje rozhraní Throwable. V produkci platí: chyby protokolovat, nikdy je nezobrazovat v prohlížeči. V PHP 8.5 patří kvůli PHP2Shell k přechodným opatřením navíc fatal_error_backtraces=0.
Dva světy: klasické chyby a výjimky
PHP pochází z doby, kdy se chyby vypisovaly hlavně jako hlášení: upozornění (E_NOTICE), varování (E_WARNING), zastaralé funkce (E_DEPRECATED) a fatální chyby (E_ERROR). Mnohá z těchto hlášení nechají skript běžet dál, často s chybnými nebo prázdnými daty.
Výjimky fungují jinak. Přeruší normální průběh a skočí na nejbližší vhodný blok catch. Pokud žádný není, skript skončí. Pro rozhodovatele je rozdíl důležitý: tiché varování může způsobit, že se objednávka uloží neúplně, aniž si toho někdo všimne. Výjimka problém zviditelní a umožní ho ošetřit.
Podceňovaným přínosem klasických hlášení jsou upozornění typu E_DEPRECATED. Oznamují, že jazykový prvek v budoucí verzi PHP zanikne. Kdo je sbírá v logu a pravidelně vyhodnocuje, ví před upgradem, která místa je nutné upravit, a při naléhavé bezpečnostní zranitelnosti nemusí dlouho testovat.
Hierarchie Throwable
Vše, co lze vyhodit pomocí throw a zachytit pomocí catch, implementuje Throwable. Pod ním jsou dvě větve:
| Třída | Účel | Příklady podtříd |
|---|---|---|
Exception |
očekávatelné chyby aplikace a rozšíření | ErrorException, PDOException, vlastní třídy |
Error |
interní chyby PHP | TypeError, ValueError, ArgumentCountError, ArithmeticError, DivisionByZeroError, UnhandledMatchError |
Vlastní třídy chyb dědí z Exception, ne z Error. Od PHP 8.0 vyhazuje většina interních funkcí při neplatných parametrech TypeError nebo ValueError, místo aby jen vypsala varování. Příkladem je strlen([]).
try, catch a finally v praxi
<?php
declare(strict_types=1);
final class ZahlungFehlgeschlagen extends RuntimeException {}
function bezahlen(Bestellung $b, Zahlungsdienst $dienst, LoggerInterface $log): bool
{
try {
$dienst->belasten($b->betrag(), $b->referenz());
return true;
} catch (ZahlungFehlgeschlagen $e) {
$log->warning('Zahlung abgelehnt', ['bestellung' => $b->id(), 'exception' => $e]);
return false;
} catch (TimeoutException | VerbindungsFehler) {
// od PHP 8.0 je proměnná v catch volitelná
$log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
return false;
} finally {
$dienst->verbindungSchliessen();
}
}
Několik tříd lze v jednom catch spojit pomocí |. Blok finally proběhne vždy, ať k chybě došlo, nebo ne. Od PHP 8.0 je navíc throw výraz a může stát například za ??: $id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');.
Globální handlery: set_error_handler a set_exception_handler
Dvě funkce zachytí to, co by jinak proklouzlo. set_exception_handler() zaregistruje funkci pro nezachycené objekty Throwable. Podle příručky skript po jejím zavolání skončí; hodí se tedy k protokolování a neutrální chybové stránce, ne k pokračování v práci.
Osvědčil se handler, který zapíše úplnou chybu s časem a náhodným identifikátorem do logu a návštěvníkovi ukáže jen neutrální stránku s tímto identifikátorem. Podpora pak může případ v logu dohledat, aniž by interní podrobnosti pronikly ven.
set_error_handler() zachytává klasické chyby. Příručka ukazuje, jak je převést na ErrorException, aby se varování ošetřovala jako výjimky:
<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
if (!(error_reporting() & $nr)) {
return false; // vyloučeno pomocí error_reporting
}
if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
return false; // upozornění na zastaralé neeskalovat
}
throw new ErrorException($text, 0, $nr, $datei, $zeile);
});
Fatální chyby, například přetečení paměti, se tímto způsobem zachytit nedají. Co se při přetečení uvnitř děje, vysvětluje stránka memory_limit a Out-of-Memory.
Konfigurace výpisu chyb a protokolování
Nejdůležitější nastavení s výchozími hodnotami podle příručky:
| Nastavení | Výchozí | Doporučení pro produkci |
|---|---|---|
error_reporting |
E_ALL od PHP 8.0.0 |
E_ALL |
display_errors |
1 |
Off |
display_startup_errors |
1 od PHP 8.0.0 |
Off |
log_errors |
0 |
On |
error_log |
prázdné (log webového serveru, resp. SAPI) | vlastní soubor mimo webroot |
fatal_error_backtraces |
1, nové v PHP 8.5.0 |
dočasně 0 (PHP2Shell) |
Od PHP 8.2.0 určuje error_log_mode oprávnění k souboru logu, standardně 0644. Pokud logy obsahují osobní údaje nebo cesty, je vhodná přísnější hodnota, aby je ostatní uživatelé na serveru nemohli číst. Pomocí ignore_repeated_errors lze shrnout shodná hlášení ze stejného souboru a řádku, aby smyčka chyb log nezahltila.
Dodávaný php.ini-production už nastavuje display_errors na Off. Podle příručky ini_set('display_errors', ...) za běhu na fatální chyby nepůsobí a syntaktické chyby nastanou dříve, než se vůbec spustí nějaké ini_set(). Tyto hodnoty proto patří do konfigurace serveru. Souvislosti najdete v článku Vypnutí výpisu chyb v produkci; jak vést logy bez úniku dat, popisuje článek Bezpečné logování.
Zpracování chyb a PHP2Shell
Pro PHP 8.5 uvádí php.net jako jedno z přechodných opatření proti PHP2Shell nastavit fatal_error_backtraces=0 a poté PHP restartovat. Ostatní opatření se týkají memory_limit, post_max_size a session.upload_progress.enabled. Žádné z nich nenahrazuje aktualizaci na PHP 8.5.12, 8.4.27, 8.3.36 nebo 8.2.35.
; jen PHP 8.5, v php.ini nebo poolu FPM, poté restartovat všechny workery
fatal_error_backtraces = 0
Chybové logy jsou po PHP2Shell navíc důležitým důkazním prostředkem. Pokud se od 20. 10. 2026 množí chyby paměti nebo ukončení workerů, může to ukazovat na pokusy o útok, ale může jít i o neškodné příčiny. Logy uchovejte a vyhodnoťte je spolu s přístupovými logy, jak popisuje průvodce Vyhodnocení logů webového serveru po útoku.
Jak konkrétně pomáhá PHP SOS?
- Zkontrolujeme účinná nastavení výpisu chyb a protokolování pro každý web a nastavíme je pro produkci.
- Nastavíme přechodná opatření PHP2Shell podle verze PHP a restartujeme všechny workery.
- Zajistíme chybové a přístupové logy od 20. 10. 2026 a vyhodnotíme je z hlediska nápadností.
- V rámci bezpečnostního monitoringu hlásíme neobvyklé nahromadění chyb, místo aby zapadly v logu.
Časté dotazy
Jaký je rozdíl mezi Error a Exception?
Obě implementují rozhraní Throwable. Exception je určena pro chyby, které aplikace očekává a ošetřuje, například neúspěšné připojení k databázi. Error představuje interní chyby PHP, jako je TypeError nebo dělení nulou.
Má být display_errors v produkci zapnuto?
Ne. Chybová hlášení v prohlížeči prozrazují cesty, dotazy a někdy i přístupové údaje. V produkci patří display_errors na Off a log_errors na On, aby chyby skončily v protokolu, a ne u návštěvníka.
Lze přetečení paměti zachytit pomocí try/catch?
Ne. Pokud skript překročí memory_limit, PHP jej ukončí fatální chybou, kterou nelze zachytit jako výjimku. Objeví se jen v chybovém logu, pokud je aktivní log_errors.
Co je fatal_error_backtraces?
Nastavení, které existuje od PHP 8.5.0 a přidává k fatálním chybám backtrace. Je standardně aktivní. Jako přechodné opatření proti PHP2Shell php.net pro PHP 8.5 uvádí nastavit je na 0 a poté PHP restartovat.