error_reporting()
Функция error_reporting() в PHP: управление уровнями ошибок, настройка отображения и логирования для разработки и продакшена.
Введение
В этой главе рассматривается, как PHP сообщает об ошибках: функция error_reporting(), управляющая какие ошибки PHP генерирует, директивы display_errors и log_errors, управляющие куда эти ошибки отправляются, а также вспомогательные функции для обработки и логирования ошибок. По окончании вы будете знать, как настраивать отчёты об ошибках по-разному для разработки и продакшена, и почему неправильная настройка является одной из самых распространённых ошибок в области безопасности и отладки в PHP.
Почему важна система отчётов об ошибках
PHP работает как гибкий, динамически типизированный язык: многие ошибки, которые остановили бы компиляцию в другом языке, проявляются во время выполнения в виде предупреждений, уведомлений или сообщений об устаревании. Если вы их подавляете, некорректное поведение может незаметно попасть в продакшен. Если же вы показываете их конечным пользователям, вы рискуете раскрыть пути к файлам, SQL-запросы и трассировки стека злоумышленникам.
Правильная система отчётов об ошибках — это верный баланс для каждого окружения:
- При разработке — показывайте всё, наглядно, чтобы вы выявляли ошибки по мере их написания.
- В продакшене — ничего не показывайте пользователю, но записывайте всё в файл для последующего анализа.
Уровни ошибок и константы
Ошибки PHP классифицируются по уровням. Каждый уровень — это предопределённая константа, и вы комбинируете их с помощью побитовых операторов. Наиболее распространённые уровни:
| Константа | Значение |
|---|---|
E_ERROR | Фатальная ошибка времени выполнения; выполнение скрипта прекращается. |
E_WARNING | Предупреждение времени выполнения; скрипт продолжает работу. |
E_NOTICE | Уведомление (например, использование неопределённой переменной). |
E_DEPRECATED | Использование функциональности, которая будет удалена в будущей версии PHP. |
E_USER_ERROR / E_USER_WARNING / E_USER_NOTICE | Уровни, которые вы определяете самостоятельно с помощью trigger_error(). |
E_ALL | Все ошибки, предупреждения и уведомления. |
Поскольку это битовые флаги, вы комбинируете их с помощью | (включить), & (маска) и ~ (отрицание):
// All errors EXCEPT notices and deprecation messages
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
// Only fatal errors and warnings
error_reporting(E_ERROR | E_WARNING);Примечание о версии: Начиная с PHP 8.0 уровень
error_reportingпо умолчанию равенE_ALL.E_STRICTбыл устаревшим в 8.0 и удалён в 8.4, а его уведомления теперь включены вE_ALL, поэтому добавлять его отдельно больше не нужно.
Отчёт против отображения: два разных параметра
Частый источник путаницы состоит в том, что два независимых переключателя определяют, действительно ли вы видите ошибку:
error_reporting()— решает, какие уровни PHP генерирует.display_errors— решает, выводятся ли сгенерированные ошибки в вывод.
Необходимо включить оба, чтобы увидеть ошибку на экране. В примере ниже генерируется уведомление уровня E_ALL и выводится на экран, потому что оба переключателя включены:
<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');
echo $undefined; // Warning: Undefined variable $undefinedЕсли бы display_errors был выключен, то же уведомление всё равно генерировалось бы (и могло бы быть залогировано), но в выводе страницы ничего не появилось бы.
Основные функции отчётов об ошибках
error_reporting()
Устанавливает, о каких уровнях ошибок сообщается во время выполнения, и возвращает предыдущий уровень. Вызовите функцию без аргументов, чтобы прочитать текущую настройку.
<?php
$old = error_reporting(E_ALL & ~E_NOTICE);
echo "Now reporting all errors except notices.\n";
echo "Previous level was: " . $old . "\n";ini_set()
Переопределяет директиву php.ini на время выполнения текущего скрипта. Это эквивалент редактирования php.ini во время выполнения, и именно так переключают display_errors, display_startup_errors и log_errors из кода.
<?php
// Development setup: show everything on screen
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);Примечание: display_errors не может перехватить фатальные ошибки синтаксического анализа в том же файле, потому что весь файл не компилируется до того, как ini_set() запустится. Для этих случаев задайте директиву непосредственно в php.ini.
set_error_handler()
Регистрирует обратный вызов, который выполняется при каждой (нефатальной) ошибке, позволяя вам заменить поведение PHP по умолчанию — например, преобразовывать предупреждения в исключения или форматировать их как JSON для API. Обратный вызов получает уровень ошибки, сообщение, файл и строку.
<?php
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool {
echo "[$errno] $errstr in " . basename($errfile) . " on line $errline\n";
return true; // true = we handled it; PHP's internal handler is skipped
});
echo $undefined; // routed to our handler instead of the default messageСмотрите set_error_handler() для полной сигнатуры и способов восстановления предыдущего обработчика.
error_log() и trigger_error()
error_log()отправляет сообщение в настроенный лог PHP, указанный файл или на адрес электронной почты — но не в вывод страницы. Это безопасный для продакшена способ записи проблем.trigger_error()генерирует ошибку определённую вами (уровниE_USER_*), которая затем проходит через тот же конвейерerror_reporting/обработчиков, что и встроенные ошибки.
<?php
// Append a message to a specific log file (message type 3)
error_log("Payment gateway timed out", 3, "/var/log/php_errors.log");
// Raise a user-level warning that your handler / log can pick up
trigger_error("Cache miss for product 42", E_USER_WARNING);Подробнее в error_log() и trigger_error().
Рекомендуемая конфигурация для каждого окружения
Вместо того чтобы разбрасывать настройки повсюду, разместите их в начале вашего загрузочного файла.
<?php
// --- Development ---
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');<?php
// --- Production ---
error_reporting(E_ALL); // still GENERATE everything...
ini_set('display_errors', '0'); // ...but never show it to users...
ini_set('log_errors', '1'); // ...log it instead.
ini_set('error_log', '/var/log/php_errors.log');Блок для продакшена обеспечивает полную видимость через логи, не раскрывая ничего посетителям — это конфигурация, которая почти всегда нужна на рабочем сервере.
Лучшие практики
- Никогда не запускайте продакшен с включённым
display_errors. Утечка путей и трассировок стека — это риск раскрытия информации. - Везде используйте уровень отчётов
E_ALL. Отчётность и отображение — разные вещи; подавление уровней лишь скрывает ошибки. - Избегайте оператора подавления ошибок
@. Он скрывает ошибки в месте вызова и значительно усложняет отладку; вместо этого явно обрабатывайте условие. - По возможности перехватывайте ошибки через исключения. Для восстанавливаемых сбоев предпочитайте
try/catchобработчикам ошибок. Смотрите PHP Exceptions. - Используйте библиотеку логирования в крупных приложениях. Инструменты вроде Monolog предоставляют уровни логов, ротацию и несколько направлений поверх
error_log().
Заключение
Система отчётов об ошибках в PHP состоит из трёх слоёв: выбор уровней с помощью error_reporting(), выбор назначения с помощью display_errors / log_errors и, при необходимости, настройка обработки с помощью set_error_handler() и trigger_error(). Настраивайте их осознанно — подробный вывод на экран во время разработки, тихое логирование в продакшене — и вы получите быструю отладку, не раскрывая приложение пользователям или злоумышленникам.
Дополнительное чтение: PHP Error handling · error_get_last() · try…catch.