Управление ошибками при разработке на PHP: примеры и советы

Раздел: PHP -> Отладка и ошибки

Основные подходы к обработке ошибок в PHP

Как создать универсальный обработчик ошибок, перехватывающий все типы ошибок PHP?

Наиболее эффективное решение - использование пользовательских обработчиков через set_error_handler(), set_exception_handler() и register_shutdown_function(). Это позволяет контролировать отображение ошибок, логировать их и выполнять кастомные действия.


// Установка пользовательского обработчика ошибок
function customErrorHandler($severity, $message, $file, $line) {
    // Не обрабатываем ошибки, если они подавлены оператором @
    if (error_reporting() === 0) {
        return false;
    }
    // Логируем ошибку
    $logMessage = date('Y-m-d H:i:s') . " [$severity] $message in $file:$line\n";
    error_log($logMessage, 3, '/var/log/php_errors.log');
    // В режиме разработки выводим ошибку
    if (ini_get('display_errors')) {
        echo '
Ошибка: ' . $message . ' в файле ' . $file . ' на строке ' . $line . '
'; } return true; } set_error_handler('customErrorHandler');

Error php file src (обработка ошибок php файла)

Пояснение: Функция customErrorHandler получает уровень ошибки, сообщение, файл и строку. Она игнорирует подавленные ошибки (@), записывает данные в лог и, если включен display_errors, выводит сообщение в удобном формате. После вызова set_error_handler все стандартные ошибки (E_WARNING, E_NOTICE и др.) будут направляться в эту функцию.

Проблема: Фатальные ошибки (E_ERROR, E_PARSE) не перехватываются set_error_handler. Они завершают скрипт до вызова обработчика.

Решение: Использовать register_shutdown_function для обработки фатальных ошибок. Внутри функции можно вызвать error_get_last() и выполнить логирование.


function shutdownHandler() {
    $error = error_get_last();
    if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        // Логирование фатальной ошибки
        $logMessage = date('Y-m-d H:i:s') . " [FATAL] {$error['message']} in {$error['file']}:{$error['line']}\n";
        error_log($logMessage, 3, '/var/log/php_errors.log');
        // Вывод сообщения пользователю (если разрешено)
        if (ini_get('display_errors')) {
            echo "Произошла критическая ошибка. Пожалуйста, попробуйте позже.";
        }
    }
}
register_shutdown_function('shutdownHandler');
    

Php fatal error function home (фатальная ошибка функции в php)

Также рекомендуется установить обработчик исключений через set_exception_handler, чтобы перехватывать непойманные исключения.


function exceptionHandler($exception) {
    $logMessage = date('Y-m-d H:i:s') . " [EXCEPTION] {$exception->getMessage()} in {$exception->getFile()}:{$exception->getLine()}\n";
    error_log($logMessage, 3, '/var/log/php_errors.log');
    if (ini_get('display_errors')) {
        echo "Необработанное исключение: " . $exception->getMessage();
    }
}
set_exception_handler('exceptionHandler');
  

Php notice undefined index (ошибка undefined index в php)

Типичная ошибка: Забывают установить error_reporting и display_errors в зависимости от окружения. Если display_errors включён на продакшене, пользователь увидит технические сообщения. Рекомендуется в файле конфигурации приложения устанавливать эти параметры динамически.

Вариант 1: Настройка через php.ini или .htaccess

Как настроить отображение и логирование ошибок без написания кода? Этот вариант подходит для простых проектов или начальной настройки сервера.


; В php.ini или в .htaccess (если разрешено)
error_reporting = E_ALL
display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
  

Index php не работает (проблемы с index.php)

Пояснение: error_reporting задаёт уровни ошибок, которые нужно отслеживать. display_errors отключает вывод на экран (важно для продакшена). log_errors включает запись в файл. Недостаток - отсутствие гибкости: нельзя изменить формат сообщений или выполнить дополнительные действия.

Проблема: В общем хостинге доступ к php.ini может быть ограничен. Иногда необходимо изменять настройки из кода с помощью ini_set().

Вариант 2: Использование блоков try-catch для исключений

Как обрабатывать только исключения, оставляя стандартные ошибки без внимания? Применяется в объектно-ориентированном коде, где ошибки преобразуются в исключения.


try {
    // Потенциально опасный код
    $result = someFunctionThatThrows();
} catch (\Exception $e) {
    // Логирование и уведомление
    error_log($e->getMessage());
    echo "Произошла ошибка: " . $e->getMessage();
}
  

Php mysql connect error (ошибка подключения к mysql)

Пояснение: Исключения не перехватывают стандартные предупреждения и уведомления. Для полного контроля нужно комбинировать с пользовательским обработчиком или преобразовывать ошибки в исключения через set_error_handler, который выбрасывает ErrorException.


set_error_handler(function($severity, $message, $file, $line) {
    throw new \ErrorException($message, 0, $severity, $file, $line);
});
  

почему не работает php (почему не работает php)

Проблема: Если исключение не поймано, оно станет фатальным. Необходим также set_exception_handler для последней инстанции.

Вариант 3: Логирование ошибок без пользовательского интерфейса

Как скрыть ошибки от пользователя, но сохранить их для разработчика? Простое решение - включить log_errors и отключить display_errors.


ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/logs/error.log');
error_reporting(E_ALL);
  

В лог-файле записываются все ошибки, но на экране ничего не показывается. Пользователь видит только пустой экран или HTTP 500. Недостаток - отсутствие информации о контексте ошибки. Для продакшена можно дополнительно отправлять уведомления на email.

Проблема: Если лог-файл недоступен для записи, ошибки могут теряться. Следует проверять права на директорию и использовать системный лог (syslog) как альтернативу.

Расширенный пример 1: Полноценный класс обработки ошибок

Создадим класс ErrorHandler, который объединяет все механизмы: обработка ошибок, исключений, фатальных ошибок. Класс позволяет гибко настраивать вывод и логирование.

Пример

class ErrorHandler {
    protected $logFile;
    protected $displayErrors;
    public function __construct($logFile, $displayErrors = false) {
        $this->logFile = $logFile;
        $this->displayErrors = $displayErrors;
        $this->registerHandlers();
    }
    protected function registerHandlers() {
        set_error_handler([$this, 'handleError']);
        set_exception_handler([$this, 'handleException']);
        register_shutdown_function([$this, 'handleShutdown']);
    }
    public function handleError($severity, $message, $file, $line) {
        if (error_reporting() === 0) return false;
        $this->log('Error', $severity, $message, $file, $line);
        if ($this->displayErrors) {
            echo 'PHP Error [' . $severity . ']: ' . $message . ' in ' . $file . ':' . $line . "\n";
        }
        return true;
    }
    public function handleException($exception) {
        $this->log('Exception', $exception->getCode(), $exception->getMessage(), $exception->getFile(), $exception->getLine());
        if ($this->displayErrors) {
            echo 'Uncaught Exception: ' . $exception->getMessage() . "\n";
        }
    }
    public function handleShutdown() {
        $error = error_get_last();
        if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
            $this->log('Fatal', $error['type'], $error['message'], $error['file'], $error['line']);
            if ($this->displayErrors) {
                echo 'Fatal Error: ' . $error['message'] . "\n";
            }
        }
    }
    protected function log($type, $code, $message, $file, $line) {
        $entry = date('Y-m-d H:i:s') . " [$type] Code $code: $message in $file:$line" . PHP_EOL;
        file_put_contents($this->logFile, $entry, FILE_APPEND | LOCK_EX);
    }
}
// Использование
$handler = new ErrorHandler(__DIR__ . '/logs/app.log', true);
// Пример ошибки
echo $undefinedVariable;

Результат выполнения: В лог-файл будет записана запись об уведомлении, а на экране появится сообщение (если displayErrors = true). Кроме того, класс защищён от повторного вызова лога при подавлении ошибки.

PHP Error [8]: Undefined variable: undefinedVariable in /path/to/test.php:40

Расширенный пример 2: Красивая страница ошибки для продакшена

Вместо стандартного сообщения можно показывать пользователю дружественную HTML-страницу, а разработчику отправлять email с деталями. Используем обработчик исключений.

Пример

function productionExceptionHandler($exception) {
    // Логируем в файл
    error_log(date('Y-m-d H:i:s') . " [CRITICAL] {$exception->getMessage()} in {$exception->getFile()}:{$exception->getLine()}\n", 3, '/var/log/prod_errors.log');
    // Отправляем email администратору
    $subject = 'Критическая ошибка на сайте';
    $body = "Ошибка: {$exception->getMessage()}\nФайл: {$exception->getFile()}:{$exception->getLine()}\nТрассировка:\n{$exception->getTraceAsString()}";
    mail('admin@example.com', $subject, $body);
    // Показываем красивую страницу
    http_response_code(500);
    echo file_get_contents('error500.html');
    exit;
}
set_exception_handler('productionExceptionHandler');

Результат: Пользователь видит статичную страницу error500.html, администратор получает письмо, а в лог записываются детали. При этом скрипт завершается корректно.


Расширенный пример 3: Преобразование всех ошибок в исключения с помощью ErrorException

Этот подход удобен при работе с современными фреймворками и PSR-3 логгерами. Все стандартные ошибки становятся исключениями, которые можно ловить в try-catch.

Пример

set_error_handler(function($severity, $message, $file, $line) {
    // Исключение для всех уровней, кроме тех, что подавлены
    if (error_reporting() & $severity) {
        throw new \ErrorException($message, 0, $severity, $file, $line);
    }
});
try {
    // Код с предупреждением
    $result = 1 / 0; // Division by zero (E_WARNING)
} catch (\ErrorException $e) {
    echo 'Поймано ErrorException: ' . $e->getMessage() . ' (код: ' . $e->getSeverity() . ')';
}

Результат: Выведется сообщение: "Поймано ErrorException: Division by zero (код: 2)". Такой подход упрощает унификацию обработки ошибок и исключений.

Поймано ErrorException: Division by zero (код: 2)

Расширенный пример 4: Использование syslog для централизованного логирования

Если требуется запись ошибок в системный журнал (syslog), можно использовать функцию openlog() и syslog().

Пример

openlog('MyApp', LOG_PID | LOG_PERROR, LOG_LOCAL0);
set_error_handler(function($severity, $message, $file, $line) {
    $priority = LOG_ERR; // по умолчанию
    if ($severity === E_WARNING || $severity === E_USER_WARNING) $priority = LOG_WARNING;
    elseif ($severity === E_NOTICE || $severity === E_USER_NOTICE) $priority = LOG_NOTICE;
    syslog($priority, "Error in $file:$line: $message");
    return true;
});
// Пример
trigger_error('User notice', E_USER_NOTICE);
closelog();

Результат: В системном логе (например, /var/log/syslog) появится запись вида: "May 20 12:34:56 hostname MyApp[12345]: Error in /path/to/file.php:10: User notice". Преимущество - централизованный сбор логов, удобно на серверах.

Обработка ошибок PHP файла - comments

En
Error php file src (php)