Стандартная ошибка PHP: настройка обработки и примеры

Раздел: Ошибки 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

Стандартная ошибка PHP - comments

En
Default php error (php)