# Błędy i wyjątki w PHP

> Błędy i wyjątki w PHP: Throwable, Error i Exception, try/catch/finally, globalne handlery oraz bezpieczne ustawienia wyświetlania błędów i logów.

Źródło: https://phpsos.de/pl/php/fehler-und-exceptions/  
Stan: 25.10.2026  
Dostawca: PHP SOS, Pomoc awaryjna PHP i bezpieczeństwo serwerów. Natychmiastowa pomoc przez WhatsApp: https://wa.me/491736494914?text=Dzie%C5%84%20dobry%2C%20PHP%20SOS%2C%20potrzebujemy%20pomocy%20z%20naszym%20serwerem%20PHP.%20Firma%3A%20%E2%80%A6%20Strona%20internetowa%3A%20%E2%80%A6

**Krótko:** PHP zgłasza problemy na dwa sposoby: jako klasyczne błędy, takie jak ostrzeżenia i uwagi, sterowane przez `error_reporting`, oraz jako wyjątki, które można przechwycić za pomocą `try` i `catch`. Od PHP 7 wiele błędów wewnętrznych zgłasza też obiekt klasy `Error`; razem z `Exception` implementuje on interfejs `Throwable`. W produkcji obowiązuje zasada: błędy zapisywać w logu, nigdy nie wyświetlać w przeglądarce. W PHP 8.5 do środków przejściowych z powodu PHP2Shell należy ponadto `fatal_error_backtraces=0`.

## Dwa światy: klasyczne błędy i wyjątki

PHP pochodzi z czasów, gdy błędy były wyświetlane przede wszystkim jako komunikaty: uwagi (`E_NOTICE`), ostrzeżenia (`E_WARNING`), przestarzałe funkcje (`E_DEPRECATED`) i błędy krytyczne (`E_ERROR`). Wiele z tych komunikatów pozwala skryptowi działać dalej, często z błędnymi lub pustymi danymi.

Wyjątki działają inaczej. Przerywają normalny przebieg i przeskakują do najbliższego pasującego bloku `catch`. Jeśli żadnego nie znajdą, skrypt się kończy. Dla osób podejmujących decyzje różnica jest ważna: ciche ostrzeżenie może sprawić, że zamówienie zostanie zapisane niekompletnie, a nikt tego nie zauważy. Wyjątek czyni problem widocznym i możliwym do obsłużenia.

Niedocenianą zaletą klasycznych komunikatów są uwagi typu `E_DEPRECATED`. Zapowiadają one, że jakiś środek językowy zniknie w przyszłej wersji PHP. Kto zbiera je w logu i regularnie analizuje, wie przed aktualizacją, które miejsca trzeba poprawić, i nie musi długo testować w razie pilnej luki bezpieczeństwa.

## Hierarchia Throwable

Wszystko, co można rzucić za pomocą `throw` i złapać za pomocą `catch`, implementuje `Throwable`. Poniżej są dwie gałęzie:

| Klasa | Przeznaczenie | Przykłady podklas |
|---|---|---|
| `Exception` | przewidywalne błędy aplikacji i rozszerzeń | `ErrorException`, `PDOException`, własne klasy |
| `Error` | wewnętrzne błędy PHP | `TypeError`, `ValueError`, `ArgumentCountError`, `ArithmeticError`, `DivisionByZeroError`, `UnhandledMatchError` |

Własne klasy błędów dziedziczą po `Exception`, a nie po `Error`. Od PHP 8.0 większość funkcji wewnętrznych zgłasza przy nieprawidłowych parametrach `TypeError` lub `ValueError`, zamiast tylko wyświetlać ostrzeżenie. Przykładem jest `strlen([])`.

## try, catch i finally w praktyce

```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 zmienna w catch jest opcjonalna
        $log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
        return false;
    } finally {
        $dienst->verbindungSchliessen();
    }
}
```

Kilka klas można połączyć w jednym `catch` za pomocą `|`. Blok `finally` wykonuje się zawsze, niezależnie od tego, czy wystąpił błąd. Od PHP 8.0 `throw` jest ponadto wyrażeniem i może stać na przykład za `??`: `$id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');`.

## Globalne handlery: set_error_handler i set_exception_handler

Dwie funkcje przechwytują to, co inaczej by się prześliznęło. `set_exception_handler()` rejestruje funkcję dla nieprzechwyconych obiektów Throwable. Według podręcznika skrypt kończy się po jej wywołaniu; nadaje się więc do zapisu w logu i neutralnej strony błędu, a nie do dalszej pracy.

Sprawdził się handler, który zapisuje do logu pełny błąd z czasem i losowym identyfikatorem, a odwiedzającemu pokazuje tylko neutralną stronę z tym identyfikatorem. Wsparcie techniczne może wtedy odnaleźć przypadek w logu, a wewnętrzne szczegóły nie wydostają się na zewnątrz.

`set_error_handler()` przechwytuje klasyczne błędy. Podręcznik pokazuje, jak zamienić je w `ErrorException`, aby ostrzeżenia były traktowane jak wyjątki:

```php
<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
    if (!(error_reporting() & $nr)) {
        return false; // wykluczone przez error_reporting
    }
    if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
        return false; // nie eskalować uwag o przestarzałych elementach
    }
    throw new ErrorException($text, 0, $nr, $datei, $zeile);
});
```

Błędów krytycznych, takich jak przepełnienie pamięci, nie można tą drogą przechwycić. Co dzieje się wewnątrz przy przepełnieniu, wyjaśnia strona [memory_limit i Out-of-Memory](https://phpsos.de/pl/php/out-of-memory/).

## Konfiguracja wyświetlania błędów i zapisu w logu

Najważniejsze ustawienia z wartościami domyślnymi według podręcznika:

| Ustawienie | Domyślnie | Zalecenie dla produkcji |
|---|---|---|
| `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` | puste (log serwera WWW lub SAPI) | własny plik poza webrootem |
| `fatal_error_backtraces` | `1`, nowość w PHP 8.5.0 | tymczasowo `0` (PHP2Shell) |

Od PHP 8.2.0 `error_log_mode` określa uprawnienia pliku logu, domyślnie `0644`. Jeśli logi zawierają dane osobowe lub ścieżki, rozsądna jest ostrzejsza wartość, aby inni użytkownicy na serwerze nie mogli ich czytać. Za pomocą `ignore_repeated_errors` można łączyć identyczne komunikaty z tego samego pliku i wiersza, aby pętla błędów nie zalała logu.

Dołączony `php.ini-production` ustawia `display_errors` już na `Off`. Według podręcznika `ini_set('display_errors', ...)` w czasie działania nie wpływa na błędy krytyczne, a błędy składni występują, zanim jakiekolwiek `ini_set()` w ogóle się wykona. Dlatego te wartości należą do konfiguracji serwera. Tło opisuje artykuł [Wyłączanie wyświetlania błędów w produkcji](https://phpsos.de/pl/blog/display-errors-produktion/); jak prowadzić logi bez wycieku danych, wyjaśnia artykuł [Bezpieczne logowanie](https://phpsos.de/pl/blog/sicher-loggen-php/).

## Obsługa błędów a PHP2Shell

Dla PHP 8.5 php.net podaje jako jeden ze środków przejściowych przeciw PHP2Shell ustawienie `fatal_error_backtraces=0` i późniejszy restart PHP. Pozostałe środki dotyczą `memory_limit`, `post_max_size` i `session.upload_progress.enabled`. Żaden z nich nie zastępuje aktualizacji do PHP 8.5.12, 8.4.27, 8.3.36 lub 8.2.35.

```ini
; tylko PHP 8.5, w php.ini lub puli FPM, potem zrestartować wszystkie workery
fatal_error_backtraces = 0
```

Logi błędów są po PHP2Shell ponadto ważnym materiałem dowodowym. Jeśli od 20.10.2026 przybywa błędów pamięci lub przerwanych workerów, może to wskazywać na próby ataku, ale może mieć też niegroźne przyczyny. Proszę zachować logi i analizować je razem z logami dostępu, jak opisano w poradniku [Analiza logów serwera WWW po ataku](https://phpsos.de/pl/ratgeber/logfiles-auswerten/).

## Jak konkretnie pomaga PHP SOS?

- Sprawdzamy obowiązujące ustawienia wyświetlania błędów i zapisu w logach dla każdej strony i ustawiamy je odpowiednio do produkcji.
- Wprowadzamy środki przejściowe PHP2Shell odpowiednie do wersji PHP i restartujemy wszystkie workery.
- Zabezpieczamy logi błędów i dostępu od 20.10.2026 i analizujemy je pod kątem nieprawidłowości.
- W ramach [monitoringu bezpieczeństwa](https://phpsos.de/pl/leistungen/security-monitoring/) zgłaszamy nietypowe nagromadzenia błędów, zamiast pozwalać im zniknąć w logu.


## Częste pytania

**Jaka jest różnica między Error a Exception?**  
Obie implementują interfejs Throwable. Exception przeznaczony jest dla błędów, które aplikacja przewiduje i obsługuje, na przykład nieudanego połączenia z bazą danych. Error oznacza wewnętrzne błędy PHP, takie jak TypeError lub dzielenie przez zero.

**Czy display_errors powinno być w produkcji włączone?**  
Nie. Komunikaty o błędach w przeglądarce zdradzają ścieżki, zapytania, a czasem dane dostępowe. W produkcji display_errors powinno mieć wartość Off, a log_errors wartość On, aby błędy trafiały do logu, a nie do odwiedzającego.

**Czy przepełnienie pamięci można przechwycić za pomocą try/catch?**  
Nie. Jeśli skrypt przekroczy memory_limit, PHP kończy go błędem krytycznym, którego nie można przechwycić jako wyjątku. Pojawia się on tylko w logu błędów, o ile log_errors jest aktywne.

**Czym jest fatal_error_backtraces?**  
To ustawienie, które istnieje od PHP 8.5.0 i dołącza do błędów krytycznych backtrace. Jest domyślnie aktywne. Jako środek przejściowy przeciw PHP2Shell php.net podaje dla PHP 8.5 ustawienie go na 0 i późniejszy restart PHP.

## Źródła

- [Podręcznik PHP: konfiguracja obsługi błędów](https://www.php.net/manual/de/errorfunc.configuration.php)
- [Podręcznik PHP: klasa Error](https://www.php.net/manual/de/class.error.php)
- [Podręcznik PHP: ErrorException](https://www.php.net/manual/en/class.errorexception.php)
- [Podręcznik PHP: set_exception_handler](https://www.php.net/manual/en/function.set-exception-handler.php)
- [php.net: komunikaty o bezpieczeństwie](https://www.php.net/)
