Aktualna sytuacja: PHP2Shell. Dwie krytyczne luki w PHP (CVSS 9,2) umożliwiają wykonanie złośliwego kodu bez logowania. Poprawki od 21.10.2026.Co należy teraz zrobić →

Błędy i wyjątki w PHP

PHP zna klasyczne komunikaty o błędach i wyjątki. Ta strona wyjaśnia hierarchię Throwable, czystą obsługę błędów, bezpieczne ustawienia produkcyjne i środek PHP2Shell dotyczący fatal_error_backtraces.

Stan 5 min czytaniaWiedza o PHP

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
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
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.

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; jak prowadzić logi bez wycieku danych, wyjaśnia artykuł Bezpieczne logowanie.

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.

; 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.

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 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

Kontakt natychmiast

Liczy się każda godzina. Proszę napisać do nas teraz.

Proszę krótko opisać system, wersję PHP i obserwacje. Skontaktujemy się z Państwem, ustalimy sytuację i będziemy pracować zdalnie, aż incydent zostanie zamknięty i udokumentowany.

Natychmiastowa pomoc