Стандартная ошибка PHP: настройка обработки и примеры
Стандартная ошибка PHP и её обработка
В PHP ошибки по умолчанию выводятся прямо в браузер в виде сообщения с указанием файла, строки и типа ошибки. Такое поведение удобно при разработке, но на боевом сервере может раскрыть конфиденциальную информацию. Ниже рассмотрены способы настройки обработки ошибок: от базовых изменений в php.ini до создания собственных обработчиков.
Основное решение: отключение вывода ошибок и включение логирования
Как сделать так, чтобы ошибки не отображались пользователям, но записывались в файл?
Самый надёжный способ - изменить директивы php.ini или задать их через ini_set() в самом скрипте. Для production сервера рекомендуется:
ini_set('display_errors', '0');
ini_set('display_startup_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php_errors.log');
error_reporting(E_ALL);Default php error (стандартная ошибка php)
- display_errors - отключает вывод ошибок в вывод скрипта.
- display_startup_errors - отключает вывод ошибок, возникающих при запуске PHP.
- log_errors - включает запись в лог-файл.
- error_log - путь к файлу лога (должен быть доступен для записи веб-серверу).
- error_reporting(E_ALL) - логировать все типы ошибок.
Типичная проблема: лог-файл не создаётся или не записывается из-за неверных прав доступа. Решение - проверять владельца и права на директорию, а также использовать абсолютный путь.
Пример ошибки: Warning: Unknown: failed to open stream: Permission denied - нужно дать права веб-пользователю (например, www-data) на запись.
Этот подход подходит для любого проекта, где требуется скрыть технические детали от пользователей и сохранить их для анализа.
Вариант 1: Выборочное подавление через error_reporting
Как скрыть только определённые типы ошибок, например уведомления (Notice)?
Директива error_reporting принимает битовую маску. Например, чтобы выводить только фатальные ошибки и предупреждения, но не уведомления:
error_reporting(E_ERROR | E_WARNING | E_PARSE);
Или, наоборот, показывать всё, кроме строгих стандартов:
error_reporting(E_ALL & ~E_STRICT);
Типичная ошибка: использование error_reporting(0) полностью отключает вывод, но если display_errors включён, сообщения всё равно могут появиться. Нужно отключать обе директивы.
Цель: гибкая настройка уровня детализации логов во время разработки или для разных окружений.
Вариант 2: Пользовательский обработчик с set_error_handler()
Как перехватить стандартную ошибку и выполнить собственные действия, например отправить уведомление?
Функция set_error_handler() позволяет задать callback, который будет вызываться при возникновении ошибки (кроме фатальных E_ERROR, E_PARSE и т.п.):
function myErrorHandler($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return false;
}
throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler('myErrorHandler');
В этом примере ошибка преобразуется в исключение ErrorException, которое можно поймать в try-catch.
Проблема: обработчик не срабатывает для фатальных ошибок. Для их перехвата требуется register_shutdown_function() или использование error_get_last().
Пример типичной ошибки: забыть вернуть true или false - если вернуть false, стандартный обработчик PHP выполнится дополнительно.
Цель: централизованная обработка ошибок, логирование в БД, отправка email или отображение дружественного сообщения.
Вариант 3: Использование try-catch с ErrorException
Как обрабатывать обычные ошибки как исключения?
После установки обработчика, который бросает ErrorException, можно оборачивать потенциально опасный код:
set_error_handler(function($severity, $message, $file, $line) {
throw new ErrorException($message, 0, $severity, $file, $line);
});
try {
$result = 10 / 0; // Warning: Division by zero
} catch (ErrorException $e) {
echo 'Поймана ошибка: ' . $e->getMessage();
}
Типичная ошибка: обработчик не восстановит предыдущий после блока try-catch, если не использовать restore_error_handler(). Рекомендуется сохранять старый обработчик и возвращать его.
Цель: единообразная работа с ошибками и исключениями в приложении.
Вариант 4: Настройка через .htaccess (Apache)
Как настроить ошибки без доступа к php.ini, используя .htaccess?
Если на сервере разрешена перезапись директив через .htaccess, можно добавить:
php_flag display_errors off
php_flag log_errors on
php_value error_log /home/user/logs/php_errors.log
Директива php_flag для булевых, php_value для строковых.
Проблема: на некоторых хостингах php_flag может быть запрещён (в php.ini директива AllowOverride установлена в None). Тогда способ не сработает.
Типичная ошибка: неправильный путь к логу (относительный - лучше указывать абсолютный).
Цель: быстрая настройка на shared-хостинге без редактирования основного конфигурационного файла.
Расширенные примеры обработки ошибок PHP
Ниже приведены продвинутые сценарии, которые помогут глубже понять механизмы работы с ошибками.
Логирование ошибок в базу данных
Пример пользовательского обработчика, который сохраняет ошибку в таблицу MySQL:
function dbErrorHandler($severity, $message, $file, $line) {
$pdo = new PDO('mysql:host=localhost;dbname=errors', 'user', 'pass');
$stmt = $pdo->prepare('INSERT INTO error_log (severity, message, file, line, time) VALUES (?, ?, ?, ?, NOW())');
$stmt->execute([$severity, $message, $file, $line]);
// Продолжить стандартную обработку
return false;
}
set_error_handler('dbErrorHandler');
// Пример вызова
$undefined = $someVar; // Notice: Undefined variable
Результат (в БД появится запись):
+----------+----------------------------+---------------+------+---------------------+ | severity | message | file | line | time | +----------+----------------------------+---------------+------+---------------------+ | 8 | Undefined variable: someVar| /var/www/... | 10 | 2025-03-25 12:00:00 | +----------+----------------------------+---------------+------+---------------------+
Перехват фатальных ошибок через register_shutdown_function
Обработчик set_error_handler() не ловит E_ERROR. Для этого используется завершающая функция:
register_shutdown_function(function() {
$error = error_get_last();
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
// Логируем фатальную ошибку
error_log(sprintf(
'FATAL: %s in %s on line %d',
$error['message'],
$error['file'],
$error['line']
));
// Можно вывести дружественное сообщение
echo 'Произошла критическая ошибка. Мы уже работаем над ней.';
}
});
// Пример вызова фатальной ошибки
undefinedFunction(); // Call to undefined function
Результат в лог-файле:
[25-Mar-2025 12:00:00 UTC] FATAL: Call to undefined function undefinedFunction() in /var/www/index.php on line 10
Отправка ошибок на email
Пользовательский обработчик с отправкой письма администратору:
function emailErrorHandler($severity, $message, $file, $line) {
$to = 'admin@example.com';
$subject = 'PHP Error on ' . $_SERVER['HTTP_HOST'];
$body = "Severity: $severity\nMessage: $message\nFile: $file\nLine: $line\nURL: " . ($_SERVER['REQUEST_URI'] ?? 'CLI');
mail($to, $subject, $body, 'From: errors@example.com');
return true;
}
set_error_handler('emailErrorHandler');
trigger_error('Тестовое предупреждение', E_USER_WARNING);
На почту придёт письмо с текстом ошибки. Рекомендуется использовать очередь или ограничение частоты, чтобы не перегрузить почтовую систему.
Комплексный обработчик с разными уровнями ошибок
Пример, который обрабатывает ошибки по-разному в зависимости от severity:
function smartErrorHandler($severity, $message, $file, $line) {
// Пропускаем ошибки, отключённые через error_reporting
if (!(error_reporting() & $severity)) return false;
switch ($severity) {
case E_NOTICE:
case E_USER_NOTICE:
// Просто логируем
error_log("Notice: $message in $file:$line");
break;
case E_WARNING:
case E_USER_WARNING:
// Логируем и отправляем email
error_log("Warning: $message in $file:$line");
mail('dev@example.com', 'PHP Warning', "$message in $file:$line");
break;
case E_ERROR:
case E_USER_ERROR:
// Для фатальных - выводим страницу ошибки и прекращаем работу
ob_clean();
http_response_code(500);
include 'error500.html';
exit(1);
default:
error_log("Unknown error: $message in $file:$line");
}
return true;
}
set_error_handler('smartErrorHandler');
Восстановление предыдущего обработчика
Если требуется временно изменить обработчик и вернуть старый:
$oldHandler = set_error_handler(function($severity, $message, $file, $line) {
echo 'Временный обработчик: ' . $message;
return true;
});
// Код, где нужна особая обработка
trigger_error('Временная ошибка', E_USER_NOTICE);
// Восстановление
restore_error_handler();
// Проверка, что старый обработчик работает
trigger_error('Обычная ошибка', E_USER_NOTICE);
Результат выполнения:
Временный обработчик: Временная ошибка Обычная ошибка (если старый обработчик был установлен, будет его результат)
Использование error_get_last() для диагностики после failed операций
Функция error_get_last() возвращает последнюю произошедшую ошибку. Полезна, если нельзя изменить код обработчика:
@file_get_contents('http://несуществующий-сайт.ру');
$lastError = error_get_last();
if ($lastError !== null) {
echo 'Последняя ошибка: ' . $lastError['message'];
} else {
echo 'Ошибок нет';
}
Результат:
Последняя ошибка: file_get_contents(http://несуществующий-сайт.ру): failed to open stream: php_network_getaddresses: getaddrinfo for несуществующий-сайт.ру failed: Name or service not known