Как поймать неопределенные функции с помощью set_error_handler в PHP

Я делаю скачок: мои PHP-скрипты ВСЕ будут терпеть неудачу изящно!

По крайней мере, это то, на что я надеюсь...

Я не хочу оборачивать (практически) каждую строку в операторах try...catch, поэтому я думаю, что лучше всего сделать собственный обработчик ошибок для начала моих файлов.

Я проверяю это на странице практики:

function customError($level,$message,$file,$line,$context) {
    echo "Sorry, an error has occured on line $line.<br />";
    echo "The function that caused the error says $message.<br />";
    die();
}

set_error_handler("customError");

echo($imAFakeVariable);

Это отлично работает, возвращая:

Извините, в строке 17 произошла ошибка. Функция, которая вызвала ошибку, сообщает неопределенную переменную: imAFakeVariable.

Однако эта настройка не работает для неопределенных функций.

function customError($level,$message,$file,$line,$context) {
    echo "Sorry, an error has occured on line $line.<br />";
    echo "The function that caused the error says $message.<br />";
    die();
}

set_error_handler("customError");

imAFakeFunction();

Это возвращает:

Неустранимая ошибка: вызов неопределенной функции: imafakefunction() в /Library/WebServer/Documents/experimental/errorhandle.php в строке 17

Почему мой пользовательский обработчик ошибок не перехватывает неопределенные функции? Есть ли другие проблемы, которые это вызовет?

Ответ 1

set_error_handler предназначен для обработки ошибок с кодами: E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE. Это связано с тем, что set_error_handler предназначен для сообщения об ошибках, создаваемых функцией пользовательских ошибок trigger_error.

Однако я нашел этот комментарий в руководстве, который может вам помочь:

"Следующие типы ошибок не могут быть обработаны с помощью определенной пользователем функции: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, и большая часть E_STRICT, поднятая в файл, где вызывается set_error_handler().

Это не совсем так. set_error_handler() не может обрабатывать их, но ob_start() может обрабатывать как минимум E_ERROR.

<?php

function error_handler($output)
{
    $error = error_get_last();
    $output = "";
    foreach ($error as $info => $string)
        $output .= "{$info}: {$string}\n";
    return $output;
}

ob_start('error_handler');

will_this_undefined_function_raise_an_error();

?>

Действительно, хотя эти ошибки следует сообщать молча в файле, например. Надеюсь, у вас не будет много ошибок E_PARSE в вашем проекте!: -)

Что касается общей отчетности об ошибках, придерживайтесь исключений (я считаю полезным сделать их связанными с моей системой MVC). Вы можете создать довольно универсальное Exception для предоставления опций с помощью кнопок и добавить много описания, чтобы пользователь знал, что неправильно.

Ответ 2

Думаю, вам нужно использовать register_shutdown_function также

Например:

 register_shutdown_function( array( $this, 'customError' ));.

   function customError() 
   {

     $arrStrErrorInfo = error_get_last();

     print_r( $arrStrErrorInfo );

   }

Ответ 3

Из документация (выделено мной):

Следующие типы ошибок не могут обрабатываться с помощью определенной пользователем функции: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING и большая часть E_STRICT, поднятых в файле, где вызывается set_error_handler().

Вызов undefined функции запускает E_ERROR, поэтому он не может обрабатываться обратным вызовом ошибки (или обработчиками исключений, если на то пошло). Все, что вы можете сделать, это установить error_reporting в 0.

PS, если вы загружаете свой собственный обработчик ошибок, вы должны позаботиться о том, чтобы правильно обработать оператор @. Из документации (выделено мной):

Важно помнить, что стандартный обработчик ошибок PHP полностью обойден. Параметры error_reporting() не будут иметь никакого эффекта, и ваш обработчик ошибок будет вызываться независимо - однако вы все еще можете прочитать текущее значение error_reporting и действовать соответствующим образом. Особо следует отметить, что это значение будет равно 0, если оператор, вызвавший ошибку, был добавлен оператором @error-control.

Ответ 4

Почему мой пользовательский обработчик ошибок не выполняет функции undefinedd? Существуют ли другие проблемы, которые это вызовет?

Угадаю, я бы сказал, что ошибки функции undefined проходят через другой путь выполнения, чем другие типы ошибок. Возможно, разработчики PHP могли бы рассказать вам больше, кроме того, что я сомневаюсь, что PHP каким-либо образом разработан.

Если вы хотите, чтобы ваши скрипты терпели неудачу, все еще записывая их в стиле PHP, попробуйте поместить всю страницу в функцию и затем вызвать ее в блоке try..catch.

Ответ 5

Очень интересная вещь, которую я обнаружил сегодня, когда сталкивался с подобной проблемой. Если вы используете следующее - он поймает ошибку с помощью вашей специальной функции/метода обработки ошибок:

ini_set('display_errors', 'Off');
error_reporting(-1);

set_error_handler(array("Cmd\Exception\Handler", "getError"), -1 & ~E_NOTICE & ~E_USER_NOTICE);

Установив "display_errors" в "Выкл.", вы можете поймать их на захвате с помощью обработчика.

Ответ 6

Я некоторое время играл с обработкой ошибок, и похоже, что он работает по большей части.

function fatalHandler() {
    global $fatalHandlerError, $fatalHandlerTitle;

    $fatalHandlerError = error_get_last();

    if( $fatalHandlerError !== null ) {

        print($fatalHandlerTitle="{$fatalHandlerTitle} | ".join(" | ", $fatalHandlerError).
                (preg_match("/memory/i", $fatalHandlerError["message"]) ? " | Mem: limit ".ini_get('memory_limit')." / peak ".round(memory_get_peak_usage(true)/(1024*1024))."M" : "")."\n".
                        "GET: ".var_export($_GET,1)."\n".
                        "POST: ".var_export($_POST,1)."\n".
                        "SESSION: ".var_export($_SESSION,1)."\n".
                        "HEADERS: ".var_export(getallheaders(),1));
    }

    return $fatalHandlerTitle;
}

function fatalHandlerInit($title="phpError") {
    global $fatalHandlerError, $fatalHandlerTitle;

    $fatalHandlerTitle = $title;
    $fatalHandlerError = error_get_last();

    set_error_handler( "fatalHandler" );
}

Теперь у меня проблема: если память исчерпана, она не сообщает об этом каждый раз. Похоже, это зависит от того, сколько памяти используется. Я сделал script для загрузки большого файла (занимает ~ 6.6M памяти) в бесконечном цикле. Setup1:

ini_set('memory_limit', '296M');

fatalHandlerInit("testing");

$file[] = file("large file"); // copy paste a bunch of times

В этом случае я получаю сообщение об ошибке, и оно умирает при загрузке файла на 45.

Setup2 - то же самое, но изменение: ini_set ('memory_limit', '299M');

На этот раз я не получаю сообщение об ошибке и даже не вызывает функцию пользовательских ошибок. script умирает в одной строке.

Кто-нибудь знает, почему и как обойти это?