6

Как поймать фатальную ошибку (`E_ERROR`) в PHP?

11

Я могу использовать set_error_handler() для перехвата большинства ошибок PHP, но это не работает для фатальных ошибок (E_ERROR), таких как вызов несуществующей функции. Существует ли другой способ перехвата этих ошибок?

Я пытаюсь отправить все ошибки с помощью mail() и использую PHP версии 5.2.3.

5 ответ(ов)

0

В PHP 7 и выше фатальные ошибки или восстанавливаемые фатальные ошибки теперь выбрасывают экземпляры класса Error. Как и любые другие исключения, объекты Error можно перехватить с помощью блока try/catch.

Пример:

<?php
$variable = 'not an object';

try {
    $variable->method(); // Выбросит объект Error в PHP 7 и выше.
} catch (Error $e) {
    // Обработка ошибки
    echo $e->getMessage(); // Вызов метода method() на строке
}

Ссылка на пример

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

Пример:

<?php
try {
    undefinedFunctionCall();
} catch (Throwable $e) {
    // Обработка ошибки
    echo $e->getMessage(); // Вызов несуществующей функции undefinedFunctionCall()
}

Ссылка на пример

Для получения дополнительной информации: PHP.net - ошибки в PHP 7

0

Вы не можете поймать или обработать фатальные ошибки, но вы можете их записать или сообщить об них. Для быстрого отладки я модифицировал один из ответов, добавив следующий простой код:

function __fatalHandler()
{
    $error = error_get_last();

    // Проверяем, является ли это фатальной ошибкой, иначе это нормальное завершение
    if ($error !== NULL && in_array($error['type'],
        array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING,
              E_COMPILE_ERROR, E_COMPILE_WARNING, E_RECOVERABLE_ERROR))) {

        echo "<pre>фатальная ошибка:\n";
        print_r($error);
        echo "</pre>";
        die;
    }
}

register_shutdown_function('__fatalHandler');

Этот код регистрирует функцию, которая будет вызвана при завершении скрипта. Внутри этой функции мы используем error_get_last(), чтобы получить последнюю ошибку. Если это фатальная ошибка, мы выводим её на экран для отладки.

0

Вы разработали способ перехватывать все типы ошибок в PHP (почти все)! Я не уверен по поводу E_CORE_ERROR (думаю, что он не будет работать только для этой ошибки)! Но для других фатальных ошибок (E_ERROR, E_PARSE, E_COMPILE и т.д.) это работает отлично, используя всего одну функцию обработчика ошибок! Вот моё решение:

Добавьте следующий код в ваш главный файл (index.php):

<?php
    define('E_FATAL',  E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR |
            E_COMPILE_ERROR | E_RECOVERABLE_ERROR);

    define('ENV', 'dev');

    // Переменные для пользовательской обработки ошибок
    define('DISPLAY_ERRORS', TRUE);
    define('ERROR_REPORTING', E_ALL | E_STRICT);
    define('LOG_ERRORS', TRUE);

    register_shutdown_function('shut');

    set_error_handler('handler');

    // Функция для перехвата ошибок, для которых нет пользовательской обработчика...
    function shut(){
        $error = error_get_last();

        if($error && ($error['type'] & E_FATAL)){
            handler($error['type'], $error['message'], $error['file'], $error['line']);
        }
    }

    function handler($errno, $errstr, $errfile, $errline) {
        switch ($errno){
            case E_ERROR:
                $typestr = 'E_ERROR'; break;
            case E_WARNING:
                $typestr = 'E_WARNING'; break;
            case E_PARSE:
                $typestr = 'E_PARSE'; break;
            case E_NOTICE:
                $typestr = 'E_NOTICE'; break;
            case E_CORE_ERROR:
                $typestr = 'E_CORE_ERROR'; break;
            case E_CORE_WARNING:
                $typestr = 'E_CORE_WARNING'; break;
            case E_COMPILE_ERROR:
                $typestr = 'E_COMPILE_ERROR'; break;
            case E_COMPILE_WARNING:
                $typestr = 'E_COMPILE_WARNING'; break;
            case E_USER_ERROR:
                $typestr = 'E_USER_ERROR'; break;
            case E_USER_WARNING:
                $typestr = 'E_USER_WARNING'; break;
            case E_USER_NOTICE:
                $typestr = 'E_USER_NOTICE'; break;
            case E_STRICT:
                $typestr = 'E_STRICT'; break;
            case E_RECOVERABLE_ERROR:
                $typestr = 'E_RECOVERABLE_ERROR'; break;
            case E_DEPRECATED:
                $typestr = 'E_DEPRECATED'; break;
            case E_USER_DEPRECATED:
                $typestr = 'E_USER_DEPRECATED'; break;
        }

        $message =
            '<b>' . $typestr .
            ': </b>' . $errstr .
            ' в <b>' . $errfile .
            '</b> на строке <b>' . $errline .
            '</b><br/>';

        if(($errno & E_FATAL) && ENV === 'production'){
            header('Location: 500.html');
            header('Status: 500 Internal Server Error');
        }

        if(!($errno & ERROR_REPORTING))
            return;

        if(DISPLAY_ERRORS)
            printf('%s', $message);

        // Логируем ошибку в файл журнала ошибок PHP...
        if(LOG_ERRORS)
            error_log(strip_tags($message), 0);
    }

    ob_start();

    @include 'content.php';

    ob_end_flush();
?>

Данный код позволяет вам перехватывать различные ошибки в PHP и обрабатывать их в одном месте. Если вы используете этот подход, он должен помочь вам избежать потери информации о фатальных ошибках и сделать ваш код более устойчивым. Не забудьте протестировать его в своем окружении разработки перед использованием на продакшн-сайте!

0

Вам не получится выбросить исключение в зарегистрированной функции завершения (shutdown function) таким образом:

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           throw new Exception("фатальная ошибка");
        }
    }

    try {
        $x = null;
        $x->method();
    } catch(Exception $e) {
        # Это не сработает
    }
?>

Однако вы можете перехватить ошибку и перенаправить запрос на другую страницу.

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           # Логируем событие, отправляем email и т.д.
           header("Location: http://localhost/error-capture");
           # С /error-capture. Вы также можете использовать другое
           # перенаправление, например, на главную страницу
        }
    }
    register_shutdown_function('shutdown');

    $x = null;
    $x->method();
?>

В этом примере, если в вашем коде произойдет ошибка, функция shutdown выполнится в конце обработки скрипта и позволит вам обработать ошибку надлежащим образом, например, перенаправив пользователя на страницу с сообщением об ошибке.

0

Если вы используете PHP версии 5.1.0 или выше, вы можете реализовать обработку ошибок с помощью класса ErrorException. Вот пример, как это сделать:

<?php
    // Определяем обработчик ошибок
    function exception_error_handler($errno, $errstr, $errfile, $errline) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }

    // Устанавливаем наш обработчик ошибок
    set_error_handler("exception_error_handler");

    /* Генерируем исключение */
    try
    {
        // Попробуйте сделать что-то, например, найти конец интернета
    }
    catch(ErrorException $e)
    {
        // Здесь можно обработать исключение $e любым удобным способом
    }
?>

В этом коде сначала определяется функция-обработчик, которая преобразует стандартные ошибки в исключения. Затем используется set_error_handler для установки этой функции как обработчика ошибок. В блоке try можно выполнять код, который может вызвать ошибку, а в блоке catch можно взять на себя обработку ошибки, обернутой в исключение.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь