# Chyby a výjimky v PHP

> Chyby a výjimky v PHP: Throwable, Error a Exception, try/catch/finally, globální handlery a bezpečná nastavení výpisu chyb a protokolů v produkci.

Zdroj: https://phpsos.de/cs/php/fehler-und-exceptions/  
Stav: 25.10.2026  
Poskytovatel: PHP SOS, Nouzová pomoc pro PHP a bezpečnost serverů. Okamžitá pomoc přes WhatsApp: https://wa.me/491736494914?text=Dobr%C3%BD%20den%2C%20PHP%20SOS%2C%20pot%C5%99ebujeme%20pomoc%20s%20na%C5%A1%C3%ADm%20PHP%20serverem.%20Firma%3A%20%E2%80%A6%20Web%3A%20%E2%80%A6

**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
<?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
<?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](https://phpsos.de/cs/php/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](https://phpsos.de/cs/blog/display-errors-produktion/); jak vést logy bez úniku dat, popisuje článek [Bezpečné logování](https://phpsos.de/cs/blog/sicher-loggen-php/).

## 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.

```ini
; 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](https://phpsos.de/cs/ratgeber/logfiles-auswerten/).

## 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](https://phpsos.de/cs/leistungen/security-monitoring/) 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.

## Zdroje

- [Příručka PHP: Konfigurace zpracování chyb](https://www.php.net/manual/de/errorfunc.configuration.php)
- [Příručka PHP: Třída Error](https://www.php.net/manual/de/class.error.php)
- [Příručka PHP: ErrorException](https://www.php.net/manual/en/class.errorexception.php)
- [Příručka PHP: set_exception_handler](https://www.php.net/manual/en/function.set-exception-handler.php)
- [php.net: Bezpečnostní oznámení](https://www.php.net/)
