Вывод ошибок PHP: конфигурация и обработка
Основные способы настройки вывода ошибок PHP
Как включить вывод всех ошибок PHP в коде?
Наиболее эффективный способ для локальной разработки - установка директив display_errors и error_reporting в начале скрипта. Это гарантирует, что все ошибки, включая синтаксические, будут отображены.
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
Первая строка включает отображение ошибок, вторая - ошибки, возникающие при запуске PHP (например, синтаксические в подключаемых файлах). Третья - задаёт уровень отчёта о всех возможных ошибках.
Если ошибка происходит до выполнения ini_set (например, в синтаксисе самого скрипта), она не будет показана. Для решения необходимо настроить php.ini или .htaccess. Также не следует оставлять эти директивы включёнными на боевом сервере - это может раскрыть конфиденциальные данные.
Как настроить вывод ошибок через php.ini?
Глобальная настройка в файле php.ini применяется ко всем скриптам на сервере. Для локальной среды можно установить:
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
Для боевого сервера:
display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
log_errors = On
Первые два параметра выключают показ ошибок, третий исключает устаревшие предупреждения, а четвёртый включает запись в лог.
Необходимо перезагрузить веб-сервер после изменения php.ini. Если файл недоступен (например, на shared хостинге), используйте .htaccess или ini_set.
Как настроить вывод ошибок через .htaccess (Apache)?
Если нет доступа к php.ini, можно задать директивы через .htaccess в корне сайта:
php_value display_errors 1
php_value display_startup_errors 1
php_value error_reporting -1
-1 соответствует E_ALL. Для отключения замените 1 на 0 и установите нужный уровень.
Директивы работают только на серверах Apache с модулем mod_php. На Nginx или при использовании CGI/FPM этот способ не сработает.
Как задать определённый уровень отчёта об ошибках?
Функция error_reporting() принимает битовую маску. Примеры:
error_reporting(E_ALL & ~E_NOTICE); // все, кроме уведомлений
error_reporting(E_ERROR | E_WARNING); // только фатальные ошибки и предупреждения
error_reporting(0); // отключить отчёт полностью
Уровни можно комбинировать. На этапе разработки рекомендуется E_ALL, на продакшене - E_ALL & ~E_DEPRECATED & ~E_STRICT с логированием.
Некоторые устаревшие конструкции могут генерировать E_DEPRECATED, что засоряет лог. Поэтому их часто исключают.
Как выводить исключения с помощью try-catch?
Исключения (Throwable) не обрабатываются стандартным выводом ошибок, их нужно ловить вручную. Пример:
try {
throw new Exception('Произошла ошибка');
} catch (Exception $e) {
echo 'Поймано исключение: ' . $e->getMessage();
}
Также можно ловить все типы ошибок, используя базовый тип \Throwable.
Если исключение не перехвачено, PHP выдаёт фатальную ошибку, которую стандартные настройки не показывают, если display_errors выключен. Для этого используйте set_exception_handler.
Как создать собственный обработчик ошибок?
Функция set_error_handler() перехватывает нефатальные ошибки. Пример вывода в красивом формате:
function customError($severity, $message, $file, $line) {
echo "<b>Ошибка:</b> [$severity] $message в файле $file на строке $line";
}
set_error_handler("customError");
echo $undefinedVar; // вызовет обработчик
Обработчик не ловит фатальные ошибки (E_ERROR, E_PARSE). Для них нужен register_shutdown_function.
Если в пользовательском обработчике допущена ошибка, она может быть не видна. Лучше возвращать true для продолжения работы.
Как записывать ошибки в файл лога?
Включение log_errors и указание error_log:
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php_errors.log');
error_reporting(E_ALL);
Все ошибки будут писаться в файл, минуя вывод на экран. Путь должен быть доступен для записи веб-серверу.
Если файл лога не создаётся, проверьте права на папку. Можно использовать системный лог (syslog).
Как отловить фатальные ошибки (E_ERROR, E_PARSE)?
Фатальные ошибки не перехватываются set_error_handler. Используйте register_shutdown_function вместе с error_get_last:
register_shutdown_function(function() {
$error = error_get_last();
if ($error && ($error['type'] === E_ERROR || $error['type'] === E_PARSE)) {
echo "Фатальная ошибка: {$error['message']} в {$error['file']}:{$error['line']}";
}
});
// Вызов несуществующей функции
testFunc();
Эта конструкция показывает фатальные ошибки даже при выключенном display_errors.
Обработчик срабатывает после завершения выполнения, поэтому любые попытки вывести результат могут быть не видны, если произошёл сбой до вызова функции. Для продакшена лучше писать ошибку в лог.
Расширенные примеры настройки вывода ошибок PHP
Ниже приведены практические сценарии с кодом и результатами их выполнения.
Пример 1. Комбинированная настройка для разработки
Включение всех ошибок, установка пользовательского обработчика и отлов фатальных ошибок.
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Пользовательский обработчик нефатальных ошибок
set_error_handler(function($severity, $message, $file, $line) {
$output = "<b>Ошибка уровня $severity:</b> $message в $file:$line";
echo "<div style='color:red;'>$output</div>";
});
// Обработка фатальных ошибок
register_shutdown_function(function() {
$error = error_get_last();
if ($error && ($error['type'] === E_ERROR || $error['type'] === E_PARSE)) {
echo "<div style='color:darkred; font-weight:bold;'>Фатальная ошибка: {$error['message']} в {$error['file']}:{$error['line']}</div>";
}
});
// Демонстрация
echo $undefinedVar; // Notice
trigger_error('Пользовательское предупреждение', E_USER_WARNING);
Результат выполнения (вывод в браузере):
Ошибка уровня 8: Undefined variable: undefinedVar в /var/www/example.php:17 Ошибка уровня 512: Пользовательское предупреждение в /var/www/example.php:18
При вызове несуществующей функции (например, badFunc()) на экране появится сообщение о фатальной ошибке.
Пример 2. Логирование с разными стилями вывода
В данном примере ошибки записываются в лог-файл, а для пользователя выводится единое сообщение.
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__.'/errors.log');
error_reporting(E_ALL);
set_error_handler(function($severity, $message, $file, $line) {
$logEntry = date('Y-m-d H:i:s')." [$severity] $message in $file:$line".PHP_EOL;
file_put_contents(ini_get('error_log'), $logEntry, FILE_APPEND);
echo "<p>Извините, произошла ошибка. Администратор уведомлен.</p>";
});
file_get_contents('/несуществующий_файл');
Содержимое errors.log после запуска:
2025-03-25 14:30:12 [2] file_get_contents(/несуществующий_файл): Failed to open stream: No such file or directory in /var/www/example.php:13
Пользователь видит только вежливое сообщение, а техническая информация сохраняется в логе.
Пример 3. Отлов исключений через set_exception_handler
Глобальный обработчик неперехваченных исключений.
set_exception_handler(function(\Throwable $exception) {
echo "<b>Необработанное исключение:</b> " . $exception->getMessage();
echo "<br>Файл: {$exception->getFile()}, строка {$exception->getLine()}";
});
throw new \InvalidArgumentException('Неверный аргумент');
Результат:
Необработанное исключение: Неверный аргумент Файл: /var/www/example.php, строка 6
Без этого обработчика PHP выдал бы фатальную ошибку, которая может не отображаться при выключенном display_errors.
Пример 4. Использование error_reporting с битовыми масками
Демонстрация разных уровней.
error_reporting(E_ALL & ~E_NOTICE);
echo $notSet; // Notice подавлен, ничего не выведется
trigger_error('Это предупреждение', E_USER_WARNING); // будет выведено
// Включить обратно уведомления
error_reporting(E_ALL);
echo $notSet; // Notice теперь отображается
Вывод:
Notice: Undefined variable: notSet in /var/www/example.php на строке 9
Первое обращение к $notSet не вызвало сообщения, второе - вызвало, так как маска была изменена.
Пример 5. Проверка error_get_last после выполнения скрипта
Получение последней ошибки для кастомной обработки.
$result = @file_get_contents('http://example.com/notfound');
if ($result === false) {
$lastError = error_get_last();
if ($lastError) {
echo "Не удалось загрузить: {$lastError['message']}";
}
}
Результат:
Не удалось загрузить: file_get_contents(http://example.com/notfound): Failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found
Здесь оператор @ подавляет вывод ошибки, но error_get_last позволяет её извлечь.