Настройка вывода сообщений об ошибках: display_errors и error_reporting
Основные методы настройки отображения ошибок в PHP
Настройка через php.ini (основной и постоянный способ)
Файл php.ini предоставляет централизованное управление директивами display_errors и error_reporting. Изменения действуют для всех скриптов на сервере (или в директории, если используется пользовательский php.ini).
Пример конфигурации для окружения разработки (все ошибки выводятся):
; php.ini
display_errors = On
error_reporting = E_ALL
Пример для боевого сервера (ошибки логируются, но не показываются пользователю):
; php.ini
display_errors = Off
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
Типичная проблема: После изменения php.ini требуется перезапуск веб-сервера или PHP-FPM, иначе новые настройки не вступят в силу. Проверить действующие значения можно через phpinfo().
Цель: Единая конфигурация для всего сервера или виртуального хоста. Используется администраторами при развёртывании проектов.
Как настроить отображение ошибок через .htaccess (для Apache)?
Если нет доступа к основному php.ini, можно переопределить директивы в файле .htaccess (при включённом AllowOverride Options).
# .htaccess
php_value display_errors On
php_value error_reporting -1
Значение -1 соответствует E_ALL (все уровни, включая будущие).
Возможная ошибка: Директива php_value не работает в .htaccess, если PHP работает как модуль Apache и AllowOverride не включает Options. Вместо этого может потребоваться SetEnv или конфигурация через vhost.
Цель: Локальная настройка для конкретного сайта или подпапки, когда нет прав на изменение php.ini.
Как временно включить вывод ошибок в PHP-скрипте с помощью ini_set?
Функция ini_set() позволяет изменить директивы во время выполнения скрипта. Подходит для отладки конкретного участка кода.
<?php
ini_set('display_errors', '1');
ini_set('error_reporting', E_ALL);
Важно: ini_set('display_errors', '1') должен быть вызван до любого вывода HTML или отправки заголовков, иначе возникнет ошибка 'headers already sent'.
Проблема: Если скрипт уже отправил данные, ini_set('display_errors', '1') не даст эффекта; ошибки по-прежнему не будут отображаться, так как буфер вывода уже отправлен.
Цель: Быстрая отладка в одном файле без изменения глобальных настроек.
Как настроить уровень отчёта об ошибках через функцию error_reporting?
Функция error_reporting() устанавливает уровень ошибок для текущего скрипта, при этом display_errors остаётся прежним.
<?php
error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
Она не влияет на отображение ошибок, только на то, какие типы будут перехватываться.
Распространённая ошибка: Разработчики путают error_reporting() и ini_set('display_errors'). Первое управляет фильтрацией, второе - выводом. Для полного контроля нужны обе настройки.
Цель: Игнорировать незначительные предупреждения (например, E_NOTICE) при разработке или на продакшене.
Как использовать константу -1 для включения всех ошибок?
Значение -1 в error_reporting или E_ALL включает все известные и будущие уровни ошибок. Это удобно для окружения разработки.
<?php
error_reporting(-1);
ini_set('display_errors', '1');
Недостаток: На боевом сервере -1 может выводить слишком много информации, включая deprecation notices, что нежелательно.
Цель: Максимально детальная отладка на локальной машине.
Как отключить display_errors только для продакшена, оставив логирование?
На продакшене рекомендуется отключать показ ошибок, но сохранять их запись в журнал. Настройка в .htaccess или php.ini:
php_value display_errors Off
php_value log_errors On
php_value error_log /path/to/error.log
Проблема: Если путь к логу неверен или нет прав на запись, ошибки могут не регистрироваться. Необходимо проверить, что файл создаётся и доступен для записи веб-сервером.
Цель: Соблюдение безопасности и возможность анализа ошибок в логах без раскрытия информации пользователю.
Как настроить отображение ошибок для командной строки (CLI)?
При запуске PHP из командной строки можно передать директивы через флаг -d:
php -d display_errors=1 -d error_reporting=-1 script.php
Также можно использовать отдельный php.ini для CLI, расположенный в /etc/php/X.Y/cli/php.ini.
Нюанс: Для CLI значения по умолчанию могут отличаться от веб-версии. Например, display_errors в CLI часто включён, но для веба выключен.
Цель: Отладка скриптов, запускаемых через cron или консоль.
Что делать, если ошибка появляется, но не выводится (скрытый белый экран)?
В таких случаях следует проверить три настройки:
display_errors– должен быть включён;error_reporting– должен включать уровень ошибки;display_startup_errors– для ошибок, возникающих при запуске PHP (например, при загрузке расширений).
; php.ini
display_startup_errors = On
display_errors = On
error_reporting = E_ALL
Типичная ошибка: Разработчик включает display_errors и error_reporting, но забывает про display_startup_errors. В результате фатальные ошибки на старте (например, синтаксическая ошибка в подключаемом файле) не выводятся, хотя в log попадают.
Цель: Выявление ошибок, возникающих до загрузки основного кода (например, в autoloader или при загрузке расширений).
Расширенные примеры настройки и отладки ошибок
Пример 1. Использование битовых масок для тонкой настройки error_reporting
Можно комбинировать уровни ошибок с помощью побитовых операторов. Например, показать только предупреждения и уведомления, но не Deprecated.
<?php
// Показать только E_WARNING и E_NOTICE, игнорируя всё остальное
error_reporting(E_WARNING | E_NOTICE);
// Вызов предупреждения
trigger_error('Это предупреждение', E_WARNING);
// Вызов уведомления
echo $undefined_variable;
?>
Результат выполнения скрипта (при включённом display_errors):
Warning: Это предупреждение in /path/script.php on line 6 Notice: Undefined variable: undefined_variable in /path/script.php on line 9
Пояснение: Побитовое ИЛИ (|) объединяет константы. Такой подход удобен, когда нужно отфильтровать только определённые типы ошибок, исключив всё лишнее.
Пример 2. Использование ini_set для временного включения отображения ошибок в середине скрипта (с проверкой буфера вывода)
Чтобы избежать ошибки 'headers already sent', можно проверить, не был ли уже отправлен вывод, и использовать буферизацию.
<?php
// Включение буферизации вывода
ob_start();
// Какой-то код, который может генерировать ошибки
echo 'Начало вывода';
// Проверка, можно ли ещё менять заголовки
if (!headers_sent()) {
ini_set('display_errors', '1');
ini_set('error_reporting', E_ALL);
}
// Ошибка
trigger_error('Тестовая ошибка', E_USER_WARNING);
// Отправка буфера
ob_end_flush();
?>
Результат:
WARNING: ... Начало вывода
Пояснение: Буферизация позволяет накапливать вывод, изменять настройки до отправки заголовков, а затем вывести содержимое. Без буферизации после первого echo изменение display_errors уже не сработает.
Пример 3. Раздельная настройка для разных уровней ошибок (E_USER_* и системных)
Иногда требуется показывать только пользовательские ошибки (созданные через trigger_error()), но скрывать стандартные предупреждения. Для этого можно написать собственный обработчик.
<?php
set_error_handler(function($severity, $message, $file, $line) {
// Пропустить все, кроме E_USER_WARNING и E_USER_NOTICE
if (!($severity & (E_USER_WARNING | E_USER_NOTICE))) {
return false; // стандартная обработка PHP
}
echo "Пользовательская ошибка: [$severity] $message в $file:$line\n";
}, E_ALL);
trigger_error('Это пользовательское предупреждение', E_USER_WARNING);
echo $undefined; // это E_NOTICE не будет выведено нашим обработчиком, а пойдёт в стандартный
?>
Результат (при display_errors = On и error_reporting = E_ALL):
Пользовательская ошибка: [512] Это пользовательское предупреждение в /path/script.php:15 Notice: Undefined variable: undefined in /path/script.php on line 17
Пояснение: Собственный обработчик позволяет гибко управлять отображением, а для необработанных типов вызывается стандартный механизм.
Пример 4. Настройка display_errors через файл .user.ini (для PHP-FPM и CGI)
Когда используется PHP-FPM или CGI, можно разместить файл .user.ini в корневой директории проекта. Он читается как php.ini для данной папки (аналогично .htaccess, но не требует Apache).
; .user.ini
display_errors = On
error_reporting = E_ALL
Не нужно перезапускать FPM, так как файл проверяется при каждом запросе.
Пояснение: Это удобно для хостингов, где нет доступа к глобальному php.ini. Файл помещается в корень сайта и действует рекурсивно.
Пример 5. Использование ini_get для диагностики текущих настроек
Перед настройкой полезно проверить, какие значения установлены в данный момент. Это помогает отладить, почему ошибки не выводятся.
<?php
echo 'display_errors: ' . ini_get('display_errors') . "\n";
echo 'error_reporting: ' . ini_get('error_reporting') . "\n";
echo 'log_errors: ' . ini_get('log_errors') . "\n";
?>
Результат (пример):
display_errors: 1 error_reporting: 32767 log_errors: 0
Пояснение: Значение 32767 соответствует E_ALL в 32-битной системе. Вывод помогает убедиться, что настройки действительно были изменены.
Пример 6. Комбинация error_reporting и display_errors для отладки конкретного модуля
Допустим, нужно временно включить полный вывод ошибок только для одного включаемого файла, оставив остальной проект в режиме production.
<?php
// index.php
ini_set('display_errors', '0');
error_reporting(E_ALL & ~E_NOTICE);
// ... код ...
// Подключаем отладочный модуль
$old = ini_get('display_errors');
ini_set('display_errors', '1');
error_reporting(E_ALL);
include 'debug_module.php';
// Восстанавливаем настройки
ini_set('display_errors', $old);
error_reporting(E_ALL & ~E_NOTICE);
// ... остальной код ...
?>
Пояснение: Сохранение и восстановление предыдущих настроек через ini_get позволяет изолировать отладку без побочных эффектов для остальной части приложения.
Пример 7. Настройка display_errors через php.ini для PHP-FPM pool
В конфигурации пула FPM (обычно /etc/php/*/fpm/pool.d/*.conf) можно задать директивы через php_admin_value или php_value. Этот способ не допускает переопределения через ini_set в коде.
; pool.conf
php_admin_value[display_errors] = On
php_admin_value[error_reporting] = E_ALL
После изменения нужно перезагрузить FPM: systemctl reload php*-fpm.
Пояснение: php_admin_value делает настройку неизменяемой из скрипта, что полезно для продакшен-окружения, чтобы запретить разработчикам случайно включить вывод ошибок.
Пример 8. Ошибка при использовании display_errors с кэшированием через OPCache
Если используется OPCache, изменение настроек через ini_set работает, но может не отразиться на кэшированных скриптах, если они уже скомпилированы с отключённым отображением. Решение – на время отладки отключить OPCache или очистить его.
// Временно отключить кэширование OPCache
ini_set('opcache.enable', '0');
// или очистить кэш
opcache_reset();
Пояснение: OPCache хранит скомпилированный код, и настройки display_errors применяются на этапе выполнения, поэтому очистка кэша не всегда обязательна, но если ошибка скрыта из-за того, что код не перекомпилирован, помогает.