Aby pokazać błędy PHP, wstaw na początku skryptu te linie: ini_set('display_errors', '1'); i error_reporting(E_ALL);. Pierwsza sprawia, że PHP wypisuje komunikaty błędów, druga, że zgłasza każdy rodzaj błędu, łącznie z ostrzeżeniami i informacjami o przestarzałym kodzie.
<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
Z tymi ustawieniami błąd taki jak odczyt zmiennej, której nigdy nie ustawiono, wypisuje komunikat z plikiem i numerem linii, zamiast cicho zawieść:
Warning: Undefined variable $total in /home/index.php on line 7
error_reporting() wywołane bez argumentu zwraca bieżące ustawienie jako liczbę. Kliknij Run, aby ustawić poziom i odczytać go z powrotem jako nazwy:
Każdy typ błędu to jeden bit, więc E_ALL & ~E_DEPRECATED oznacza „wszystko oprócz informacji o przestarzałym kodzie”. Zmień pierwszą linię na error_reporting(E_ERROR | E_WARNING); i uruchom ponownie.
Wyświetlanie błędów w php.ini
Ustawienie w skrypcie działa dopiero, gdy skrypt zacznie się wykonywać. Aby pokazywać błędy dla każdego skryptu, łącznie z błędami parsowania, zmień php.ini (znajdziesz go przez php --ini):
; Development
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
Po edycji zrestartuj Apache albo PHP-FPM. Wbudowany serwer php -S i wiersz poleceń czytają plik przy każdym starcie. Na hostingu współdzielonym bez dostępu do php.ini Apache z mod_php przyjmuje też linie w .htaccess:
php_flag display_errors on
php_value error_reporting -1
-1 ustawia każdy bit, czyli to samo co E_ALL.
Poziomy błędów
| Stała | Co ją wywołuje | Zatrzymuje skrypt |
|---|---|---|
E_ERROR | Krytyczny błąd w czasie działania, na przykład brak pamięci | tak |
E_PARSE | Błąd składni w pliku | tak |
E_WARNING | Niezdefiniowana zmienna lub klucz tablicy, brak pliku w include | nie |
E_NOTICE | Drobne problemy (w PHP 8 zostało ich niewiele) | nie |
E_DEPRECATED | Kod, który przyszła wersja PHP odrzuci | nie |
E_USER_* | Błędy zgłaszane przez Ciebie przez trigger_error() | tylko E_USER_ERROR |
E_ALL | Każdy poziom, łącznie z błędami rdzenia i kompilacji, których tu nie wymieniono |
PHP 8.0 zamienił wiele starych ostrzeżeń w wyjątki: dzielenie przez zero operatorem / i przekazanie złego typu do wbudowanej funkcji rzucają teraz podklasę Error, tak samo jak wywołanie niezdefiniowanej funkcji od PHP 7. Wszystkie trzy możesz przechwycić:
Wyjątki są opisane w try i catch. Ostrzeżenia, notice i informacje o przestarzałym kodzie nadal przechodzą przez opisany tu system raportowania błędów.
Logowanie błędów zamiast wyświetlania
Na działającej stronie odwiedzający nigdy nie powinni widzieć komunikatów błędów: ujawniają ścieżki, SQL i kod. Zapisuj je zamiast tego do pliku logu:
; Production
display_errors = Off
log_errors = On
error_log = /var/log/php/app-errors.log
error_reporting = E_ALL
Zostaw error_reporting = E_ALL na produkcji. Obniżenie poziomu nie usuwa błędów, tylko sprawia, że się o nich nie dowiadujesz. Własne komunikaty możesz zapisać do tego samego logu przez error_log():
<?php
error_log("Payment failed for order 1042");
Potem obserwuj plik podczas testów: tail -f /var/log/php/app-errors.log.
Zamiana ostrzeżeń w wyjątki przez set_error_handler
set_error_handler zastępuje domyślną obsługę ostrzeżeń, notice i informacji o przestarzałym kodzie Twoją własną funkcją. Popularny wzorzec zamienia każde z nich w ErrorException, więc zapomniany klucz tablicy zatrzymuje kod jak każdy inny wyjątek i można go przechwycić:
Funkcja obsługi dostaje poziom błędu jako pierwszy argument, więc może traktować poziomy różnie. Ta tylko loguje informacje o przestarzałym kodzie, a całą resztę zamienia w wyjątek:
Błędy krytyczne i błędy parsowania nigdy nie trafiają do funkcji obsługi, bo skrypt nie może po nich kontynuować. Aby je zapisać, zarejestruj funkcję zamykającą, która czyta error_get_last().
Odczyt ostatniego błędu przez error_get_last
Niektóre funkcje zwracają false i podają przyczynę tylko jako ostrzeżenie, na przykład file_get_contents dla nieistniejącego pliku. error_get_last() zwraca to ostrzeżenie jako tablicę, co się przydaje, gdy chcesz pokazać własny komunikat:
Operator @ przed wywołaniem wycisza komunikat dla tego jednego wyrażenia. Używaj go oszczędnie i tylko wtedy, gdy zaraz potem sprawdzasz wynik, jak tutaj; samo @ jedynie ukrywa błędy.
Częsty błąd: błędy są włączone, a strona nadal jest pusta
Jeśli dodałeś ini_set('display_errors', '1') i nadal widzisz białą stronę, to prawdopodobnie błąd parsowania. PHP parsuje cały plik przed wykonaniem pierwszej linii, więc brakujący ; albo } zatrzymuje wszystko, zanim wykona się ini_set. Sprawdź plik z terminala, który zawsze wypisuje komunikat:
php -l index.php
Errors parsing index.php
PHP Parse error: syntax error, unexpected token "," in index.php on line 2
Numer linii wskazuje miejsce, w którym PHP się poddał, a to często linia po faktycznym błędzie. Popraw go, uruchamiaj php -l, aż wypisze No syntax errors detected, i odśwież stronę.
Najczęściej zadawane pytania
Jak wyświetlić wszystkie błędy w PHP?
Dodaj ini_set('display_errors', '1'); ini_set('display_startup_errors', '1'); error_reporting(E_ALL); na samym początku skryptu albo ustaw display_errors = On i error_reporting = E_ALL w php.ini i zrestartuj serwer WWW lub PHP-FPM.
Dlaczego moja strona PHP pokazuje pusty biały ekran?
Błąd krytyczny zatrzymał skrypt przy wyłączonym display_errors, więc nic się nie wypisało. Włącz wyświetlanie błędów (albo przeczytaj log błędów, na przykład przez tail -f na pliku, na który wskazuje error_log), aby zobaczyć komunikat i numer linii.
Dlaczego ini_set('display_errors', 1) nie pokazuje mojego błędu parsowania?
Błąd parsowania zatrzymuje PHP, zanim wykona się jakakolwiek linia pliku, więc wywołanie ini_set nigdy nie następuje. Ustaw display_errors w php.ini albo umieść ini_set w małym pliku, który potem dołącza przez include ten zepsuty.
Czy display_errors powinno być włączone na produkcji?
Nie. Komunikaty błędów ujawniają odwiedzającym ścieżki plików i szczegóły kodu. Na produkcji użyj display_errors = Off, log_errors = On i ścieżki w error_log, a error_reporting = E_ALL zostaw, żeby problemy nadal były zapisywane.
Co robi error_reporting(0)?
Wyłącza raportowanie wszystkich typów błędów do końca skryptu, więc ostrzeżenia, a nawet błędy krytyczne nie dają żadnego komunikatu. To ukrywa problemy, zamiast je naprawiać; lepiej logować błędy niż je wyciszać.