Чтобы показать ошибки PHP, поставьте в начало скрипта строки ini_set('display_errors', '1'); и error_reporting(E_ALL);. Первая заставляет PHP печатать сообщения об ошибках, вторая включает сообщения обо всех видах ошибок, включая предупреждения и устаревший код.
<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
После этого ошибка вроде чтения переменной, которой никогда не присваивали значение, выводит сообщение с файлом и номером строки, а не проходит молча:
Warning: Undefined variable $total in /home/index.php on line 7
error_reporting() без аргумента возвращает текущую настройку в виде числа. Нажмите Run, чтобы задать уровень и расшифровать его обратно в имена:
Каждый тип ошибки занимает один бит, поэтому E_ALL & ~E_DEPRECATED означает "всё, кроме устаревшего кода". Замените первую строку на error_reporting(E_ERROR | E_WARNING); и запустите снова.
Вывод ошибок в php.ini
Настройка в скрипте начинает действовать только после того, как скрипт запустился. Чтобы показывать ошибки во всех скриптах, включая ошибки разбора, измените php.ini (найти его можно командой php --ini):
; Development
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
После правки перезапустите Apache или PHP-FPM. Встроенный сервер php -S и командная строка читают файл при каждом запуске. На виртуальном хостинге без доступа к php.ini Apache с mod_php принимает и строки в .htaccess:
php_flag display_errors on
php_value error_reporting -1
-1 устанавливает все биты, это то же самое, что E_ALL.
Уровни ошибок
| Константа | Что её вызывает | Останавливает скрипт |
|---|---|---|
E_ERROR | Фатальная ошибка выполнения, например нехватка памяти | да |
E_PARSE | Синтаксическая ошибка в файле | да |
E_WARNING | Неопределённая переменная или ключ массива, отсутствующий файл в include | нет |
E_NOTICE | Мелкие проблемы (в PHP 8 их почти не осталось) | нет |
E_DEPRECATED | Код, который будущая версия PHP отвергнет | нет |
E_USER_* | Ошибки, которые вы вызываете через trigger_error() | только E_USER_ERROR |
E_ALL | Все уровни, включая ошибки ядра и компиляции, не перечисленные здесь |
PHP 8.0 превратил многие старые предупреждения в исключения: деление на ноль через / и передача неверного типа во встроенную функцию теперь выбрасывают подкласс Error, как и вызов неопределённой функции начиная с PHP 7. Все три можно перехватить:
Исключения описаны в try и catch. Предупреждения, notice и сообщения об устаревшем коде по-прежнему проходят через систему вывода ошибок, описанную здесь.
Запись ошибок в лог вместо вывода
На рабочем сайте посетители никогда не должны видеть сообщения об ошибках: они раскрывают пути, SQL и код. Записывайте их в лог-файл:
; Production
display_errors = Off
log_errors = On
error_log = /var/log/php/app-errors.log
error_reporting = E_ALL
Оставляйте error_reporting = E_ALL в продакшене. Если понизить уровень, ошибки никуда не денутся, вы просто перестанете о них узнавать. Свои сообщения можно писать в тот же лог через error_log():
<?php
error_log("Payment failed for order 1042");
Затем следите за файлом во время тестирования: tail -f /var/log/php/app-errors.log.
Предупреждения как исключения через set_error_handler
set_error_handler заменяет стандартную обработку предупреждений, notice и сообщений об устаревшем коде вашей функцией. Частый приём превращает каждое из них в ErrorException, и тогда забытый ключ массива останавливает код, как любое другое исключение, и его можно перехватить:
Обработчик получает уровень ошибки первым аргументом, поэтому может обрабатывать уровни по-разному. Этот только записывает в лог устаревший код, а всё остальное превращает в исключение:
Фатальные ошибки и ошибки разбора никогда не доходят до обработчика, потому что после них скрипт не может продолжить работу. Чтобы записывать их, зарегистрируйте функцию завершения, которая читает error_get_last().
Последняя ошибка через error_get_last
Некоторые функции возвращают false и сообщают причину только предупреждением, например file_get_contents для отсутствующего файла. error_get_last() возвращает это предупреждение в виде массива, что удобно, когда хочется показать своё сообщение:
Оператор @ перед вызовом подавляет сообщение для этого одного выражения. Пользуйтесь им редко и только тогда, когда сразу проверяете результат, как здесь; @ сам по себе только прячет ошибки.
Частая ошибка: вывод ошибок включён, а страница всё равно пустая
Если вы добавили ini_set('display_errors', '1') и всё равно видите белую страницу, скорее всего, это ошибка разбора. PHP разбирает весь файл до выполнения первой строки, поэтому пропущенная ; или } останавливает всё ещё до того, как сработает ini_set. Проверьте файл из терминала, он всегда печатает сообщение:
php -l index.php
Errors parsing index.php
PHP Parse error: syntax error, unexpected token "," in index.php on line 2
Номер строки указывает, где PHP сдался, а это часто строка после настоящей ошибки. Исправьте её, запускайте php -l снова, пока не увидите No syntax errors detected, и перезагрузите страницу.
Часто задаваемые вопросы
Как включить вывод всех ошибок в PHP?
Добавьте ini_set('display_errors', '1'); ini_set('display_startup_errors', '1'); error_reporting(E_ALL); в самое начало скрипта или задайте display_errors = On и error_reporting = E_ALL в php.ini и перезапустите веб-сервер или PHP-FPM.
Почему PHP-страница показывает пустой белый экран?
Фатальная ошибка остановила скрипт, а display_errors выключен, поэтому ничего не напечаталось. Включите вывод ошибок (или прочитайте лог ошибок, например через tail -f по файлу, на который указывает error_log), чтобы увидеть сообщение и номер строки.
Почему ini_set('display_errors', 1) не показывает ошибку разбора?
Ошибка разбора останавливает PHP до выполнения первой строки файла, поэтому вызов ini_set так и не происходит. Задайте display_errors в php.ini или поместите ini_set в небольшой файл, который потом делает include сломанного.
Нужно ли включать display_errors в продакшене?
Нет. Сообщения об ошибках раскрывают посетителям пути к файлам и детали кода. В продакшене используйте display_errors = Off, log_errors = On и путь в error_log, а error_reporting = E_ALL оставьте, чтобы проблемы всё равно записывались.
Что делает error_reporting(0)?
Отключает сообщения обо всех типах ошибок до конца скрипта, так что предупреждения и даже фатальные ошибки проходят без сообщений. Это скрывает проблемы, а не исправляет их; лучше писать ошибки в лог, чем заглушать.