Как поймать неопределенные функции с помощью 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 умирает в одной строке.
Кто-нибудь знает, почему и как обойти это?