Я могу использовать set_error_handler() для обнаружения большинства ошибок PHP, но он не работает для фатальных (E_ERROR) ошибок, таких как вызов несуществующей функции. Есть ли другой способ отловить эти ошибки?
Я пытаюсь вызвать в mail() по поводу всех ошибок и использую PHP 5.2.3.
Я написал Q&A в стиле вики с полным решением для отлова всех ошибок в PHP; которые можно просмотреть / почерпнуть / украсть / раскритиковать здесь, в Stack Overflow. Решение включает в себя пять методов, которые охватывают все ошибки, которые PHP может генерировать, которые в конечном итоге передают указанные ошибки типизированному объекту ErrorHandler.






Не совсем. Фатальные ошибки называются так, потому что они фатальны. От них не оправиться.
ловля и восстановление - две очень разные вещи.
PHP не предоставляет обычных средств для обнаружения фатальных ошибок и восстановления после них. Это связано с тем, что обработка обычно не должна восстанавливаться после фатальной ошибки. Строка, соответствующая выходному буферу (как было предложено в исходной публикации, методика, описанная на PHP.net), определенно не рекомендуется. Это просто ненадежно.
Вызов функции mail () из метода обработчика ошибок тоже оказался проблематичным. Если бы у вас было много ошибок, ваш почтовый сервер был бы загружен работой, и вы могли бы оказаться с корявым почтовым ящиком. Чтобы избежать этого, вы можете рассмотреть возможность запуска cron для периодического сканирования журналов ошибок и соответствующей отправки уведомлений. Вы также можете изучить программное обеспечение для мониторинга системы, такое как Nagios.
Чтобы поговорить с битом о регистрации функции выключения:
Это правда, что вы можете зарегистрировать функцию выключения, и это хороший ответ.
Дело в том, что обычно мы не должны пытаться восстанавливаться после фатальных ошибок, особенно не используя регулярное выражение для выходного буфера. Я отвечал на принятый ответ, который связан с предложением на php.net, которое с тех пор было изменено или удалено.
Это предложение заключалось в том, чтобы использовать регулярное выражение для выходного буфера во время обработки исключений, а в случае фатальной ошибки (обнаруженной сопоставлением с любым настроенным текстом ошибки, которого вы могли ожидать), попробуйте выполнить какое-то восстановление или продолжить обработку. Это не было бы рекомендуемой практикой (я считаю, что поэтому я тоже не могу найти исходное предложение. Я либо не обращаю на него внимания, либо сообщество php его отклонило).
Возможно, стоит отметить, что более поздние версии PHP (около 5.1), похоже, вызывают функцию выключения раньше, до того, как вызывается обратный вызов буферизации вывода. В версии 5 и ранее этот порядок был обратным (за обратным вызовом буферизации вывода следовала функция выключения). Кроме того, начиная с версии 5.0.5 (что намного раньше, чем версия 5.2.3 опрашивающего), объекты выгружаются задолго до того, как вызывается зарегистрированная функция выключения, поэтому вы не сможете полагаться на свои объекты в памяти для выполнения много чего.
Таким образом, регистрация функции выключения - это нормально, но задачи, которые должна выполнять функция выключения, вероятно, ограничиваются несколькими процедурами мягкого выключения.
Ключевой вывод здесь - это всего лишь несколько мудрых слов для тех, кто наткнется на этот вопрос и увидит совет в первоначально принятом ответе. Не используйте регулярное выражение в выходном буфере.
Пффф, я помню те 650 000+ писем, которые получил на следующее утро. С тех пор мой ErrorHandler ограничен 100 электронными письмами на каждый веб-сервер.
Это не правда. Вы можете зафиксировать фатальные ошибки с помощью register_shutdown_function.
Существуют варианты использования для обнаружения фатальных ошибок. Например, наборы тестов не должны просто останавливаться при сбое одного из них, они должны сообщать о фатальной ошибке и переходить к следующему тесту. PHP просто делает слишком много «фатальных» ошибок.
Да, говорить, что их «не следует ловить», очень недальновидно. В производственной системе вы необходимость, чтобы знать, когда что-то выходит из строя (настраивайте электронную почту или регистрируйте события в базе данных - обработка ошибок php по умолчанию не очень сложна).
Да, их «не надо ловить», но, к сожалению, ошибки часто используются там, где должны быть исключения (некачественные библиотеки, некачественные проекты). Это меня огорчает.
Я хочу вкратце прокомментировать то, что вы все говорите о том, что «ошибки нужно вылавливать, чтобы мы могли их исправить» ... Директивы ini ini log_errors и error_log.
В дополнение к этому, я очень РЕДКО получал фатальную ошибку. Я имею в виду, что вы можете вызвать функцию или переменную, которых не существует, и они все еще продолжают работать. Вот почему так важно протестировать свой код до того, как он попадет в рабочую среду.
Вы также можете добавить произвольное число в заголовок, например, 400 (например, это число не лучший выбор). Тогда в вашем журнале есть ошибка, и apache выдаст правильную страницу с ошибкой в зависимости от номера.
Вы также можете проверить это code.google.com/p/lagger, вы даже можете получить расширения, скажем, для Chrome, чтобы вы могли просматривать свой журнал ctrl + i и видеть ошибки и т. Таким образом, они не отображаются на странице, но вы все равно можете получить к ним доступ, не копаясь в файлах журналов.
Это не правильно. Выявление фатальных ошибок совершенно нормально и правильно, поэтому вы можете делать такие вещи, как регистрировать их и выполнять очистку. Обработка ошибок PHP просто плохо спроектирована.
Запуская php 5.3.3-7 + squeeze13, я не согласен с тем, что «объекты выгружаются задолго до вызова зарегистрированной функции выключения». Я получил доступ к своим объектам в зарегистрированной функции выключения, и она работала нормально, все объекты имеют те же данные, что и до ошибки.
Любой другой язык JavaScript, C# и т. д. Позволяет ловить фатальные ошибки с помощью try / catch. PHP здесь просто неправильный.
Некоторые фатальные ошибки не следует обнаруживать, но подавляющее большинство фатальных ошибок php должно быть исключением! Вызов метода для нулевой переменной должен быть исключением! Неподдерживаемые типы операндов должны быть исключением! PHP в основном вынуждает вас выполнять LBYL-программирование в тех случаях, когда это несовместимо.
Лично я считаю, что ВСЕ ошибки должны быть отловлены. Почему? Система №1 будет работать перед пользователем, поэтому вы можете занести все ошибки в базу данных и / или отправить электронное письмо разработчику. Ошибки, которые регистрируются в простом текстовом файле, не содержат почти бесполезной информации, никакой информации о том, какой пользователь их вызвал, или о том, как был вызван php-скрипт. # 2 В зависимости от того, как он был вызван (например, ajax или прямой html), вы можете захотеть вывести другой формат (json / xml), чем всегда HTML.
Верно. Дело в том, что ошибки следует отлавливать, но не следует отлавливать фатальные ошибки, чтобы как-то исправить, используя нечетные и ненадежные приемы (например, применение регулярного выражения к выходному буферу).
Регистрируйте фатальные ошибки с помощью register_shutdown_function, для которого требуется PHP 5.2+:
register_shutdown_function( "fatal_handler" );
function fatal_handler() {
$errfile = "unknown file";
$errstr = "shutdown";
$errno = E_CORE_ERROR;
$errline = 0;
$error = error_get_last();
if ($error !== NULL) {
$errno = $error["type"];
$errfile = $error["file"];
$errline = $error["line"];
$errstr = $error["message"];
error_mail(format_error( $errno, $errstr, $errfile, $errline));
}
}
Вам нужно будет определить функции error_mail и format_error. Например:
function format_error( $errno, $errstr, $errfile, $errline ) {
$trace = print_r( debug_backtrace( false ), true );
$content = "
<table>
<thead><th>Item</th><th>Description</th></thead>
<tbody>
<tr>
<th>Error</th>
<td><pre>$errstr</pre></td>
</tr>
<tr>
<th>Errno</th>
<td><pre>$errno</pre></td>
</tr>
<tr>
<th>File</th>
<td>$errfile</td>
</tr>
<tr>
<th>Line</th>
<td>$errline</td>
</tr>
<tr>
<th>Trace</th>
<td><pre>$trace</pre></td>
</tr>
</tbody>
</table>";
return $content;
}
Используйте Swift Mailer для записи функции error_mail.
Смотрите также:
+1 Это правильный ответ действительный. Я не знаю, почему люди зацикливаются на том, что «вы не можете восстановиться после фатальных ошибок» - в вопросе ничего не говорилось о восстановлении.
Спасибо, хорошо. Восстановление после фатальных ошибок (например, ограничения памяти) - это не то, что я бы пытался сделать, но возможность обнаружения этих ошибок (без отправки клиентом заявки в службу поддержки) имеет решающее значение.
он также перехватывает ошибки синтаксического анализа, если они не находятся в том же файле объявления функции регистра. (при условии, что это объявление в основном файле)
Обратите внимание, что функция обратного вызова должна выполняться до вызова register_shutdown_function ().
@SlavaN переменные устанавливаются перед if, поэтому они просто отправят электронное письмо о том, что ошибка неизвестна, если ошибка не может быть установлена.
Используя обычную почту: mail("[email protected]", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
@ScottNicol Slava V верен, потому что функция выключения вызывается каждый раз, когда скрипт завершает работу. Теперь, когда код написан, электронное письмо будет отправляться при КАЖДОЙ загрузке страницы.
@periklis (ниже) имеет лучший ответ
Обратите внимание, что register_shutdown_function вызывается перед деструкторами, поэтому, если деструктор генерирует исключение, он не будет обработан вашим обратным вызовом.
Незначительная придирка, но для краткости, поскольку вы ожидаете массив, вы можете просто сделать if ($error) вместо if ( $error !== NULL).
Обратите внимание, что (1) register_shutdown_function добавляет заданную функцию в очередь обратных вызовов завершения работы, поэтому она должна хорошо работать с неизвестным кодом, выполняемым до и после, во время одного и того же выключения; (2) он не проверяет, находится ли данная функция уже в очереди или нет; (3) отменить регистрацию зарегистрированной функции невозможно. Следовательно, зарегистрированная функция выключения не только должна хорошо взаимодействовать с другим неизвестным кодом, но и с возможными дополнительными исполнениями своего собственного кода. Совет: используйте что-нибудь вроде static $count; ... $count++; ... if ($count > 1) return; ... (fatal handling) .
РЖУ НЕ МОГУ ! 480 голосов за? для чего? приведенный вами пример: `$ trace = print_r (debug_backtrace (false), true);` может и заблокирует серверный скрипт! это случилось со мной. Да да, не обязательно. но в конце концов так и будет. Поскольку этот цикл не ограничен, и вы не знаете, что делаете. Избегайте этого любой ценой, если вам не нравится перезапускать сервер Apache.
Поскольку error_mail находится внутри оператора if, значения переменных по умолчанию никогда не используются. Можно смело снимать ...
Примечание: это не 100% правильный ответ. Любое место, где для игнорирования ошибок используется символ @, по-прежнему будет УСТАНОВИТЬ последнюю ошибку (чтобы вы могли обрабатывать ошибки). Таким образом, ваш сценарий завершается без проблем, но функция register_shutdown_function по-прежнему считает, что произошла ошибка. Только с PHP 7 появилась функция error_clear_last ().
debug_backtrace показывает только стек «fatal_handler», но не стек, когда произошла ошибка. Интересно, есть ли способ увидеть стек, когда произошла фатальная ошибка.
Спасибо, это помогло мне :)
Я только что придумал это решение (PHP 5.2.0+):
function shutDownFunction() {
$error = error_get_last();
// Fatal error, E_ERROR === 1
if ($error['type'] === E_ERROR) {
// Do your stuff
}
}
register_shutdown_function('shutDownFunction');
Различные типы ошибок определены в Предопределенные константы.
Это решение делает для меня гораздо больше, чем самый популярный ответ. Ответ с наивысшим рейтингом будет отправлять вам электронное письмо при каждом запуске скрипта, даже если ошибки нет. Этот строго работает с фатальной ошибкой.
@periklis, если последняя ошибка уже была обработана, error_get_last все равно вернет ее, не так ли?
@Pacerier Я не уверен, что вы имеете в виду под словом «обработано», поскольку ошибки не являются исключением, но я полагаю, что ответ - «да».
@periklis, я имею в виду, что вы уже "обработали" предыдущую ошибку с помощью обработчика ошибок в set_error_handler. В этом случае вы снова будете «обрабатывать» эту ошибку, когда скрипт завершит работу.
@Pacerier Понятно, это интересный вопрос. Взгляните на php.net/error_get_last, в одном из комментариев упоминается, что "If an error handler (see set_error_handler ) successfully handles an error then that error will not be reported by this function."
@periklis, он не был точным. В set_error_handler иногда мы заставляем наш обработчик возвращать false, чтобы продолжить поведение по умолчанию (нормальная обработка ошибок). В этом случае error_get_last снова облажается, сообщив о той же ошибке. Какое решение лучше?
Если вы ссылаетесь на FATAL ошибки как на исходный вопрос OP, то ваш set_error_handler() не будет выполнен. set_error_handler() может обрабатывать определенные типы ошибок. Я бы посоветовал вам использовать register_shutdown_function() для всего остального (полный список см. В руководстве).
Возможно, это очевидно, вызов register_shutdown_function() должен быть раньше, чем возникнет фатальная ошибка. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction'); не будет работать должным образом.
В дополнение к E_ERROR вы, вероятно, захотите проверить E_COMPILE_ERROR, E_CORE_ERROR и, возможно, даже E_PARSE. Как говорили другие, любая функция, зарегистрированная с помощью register_shutdown_function (), будет запускаться при каждом вызове скрипта, поэтому вы захотите свести к минимуму любой код, который вы вставляете в такую функцию, и не делать такие вещи, как попытки отправить заголовки без тестирования с headers_sent () или что-нибудь еще, что может вызвать дополнительные ошибки.
если ошибок не было, код: if ($ error ['type'] === E_ERROR) вызовет предупреждение, потому что вы пытаетесь получить доступ к несуществующему ключу в NULL
Что делать, если внутри функции выключения возникает еще одна фатальная ошибка? Будет ли код входить в бесконечный цикл?
Вы не можете создать исключение внутри зарегистрированной функции выключения, например:
<?php
function shutdown() {
if (($error = error_get_last())) {
ob_clean();
throw new Exception("fatal error");
}
}
try {
$x = null;
$x->method()
} catch(Exception $e) {
# This won't work
}
?>
Но вы можете захватить и перенаправить запрос на другую страницу.
<?php
function shutdown() {
if (($error = error_get_last())) {
ob_clean();
# Report the event, send email, etc.
header("Location: http://localhost/error-capture");
# From /error-capture. You can use another
# redirect, to e.g. the home page
}
}
register_shutdown_function('shutdown');
$x = null;
$x->method()
?>
Что ж, фатальные ошибки вроде бы можно отловить и другим способом :)
ob_start('fatal_error_handler');
function fatal_error_handler($buffer){
$error = error_get_last();
if ($error['type'] == 1){
// Type, message, file, line
$newBuffer='<html><header><title>Fatal Error </title></header>
<style>
.error_content{
background: ghostwhite;
vertical-align: middle;
margin:0 auto;
padding: 10px;
width: 50%;
}
.error_content label{color: red;font-family: Georgia;font-size: 16pt;font-style: italic;}
.error_content ul li{ background: none repeat scroll 0 0 FloralWhite;
border: 1px solid AliceBlue;
display: block;
font-family: monospace;
padding: 2%;
text-align: left;
}
</style>
<body style = "text-align: center;">
<div class = "error_content">
<label >Fatal Error </label>
<ul>
<li><b>Line</b> ' . $error['line'] . '</li>
<li><b>Message</b> ' . $error['message'] . '</li>
<li><b>File</b> ' . $error['file'] . '</li>
</ul>
<a href = "javascript:history.back()"> Back </a>
</div>
</body></html>';
return $newBuffer;
}
return $buffer;
}
Я бы дал 10 голосов, если бы мог. Он отлично работает для меня с теми странными ошибками, которые иногда возникают, когда страница бомбится и ничего не регистрируется. Я бы не стал использовать в реальном производственном коде, но здорово добавить на страницу, когда требуется быстрый ответ на то, что не удается. Спасибо!
Одно из лучших решений, которые я нашел в Интернете. Работает как шарм.
Каким образом? Объяснение было бы в порядке, особенно если это одно из лучших решений в Интернете (может стать еще лучше).
Например, весь необходимый CSS-контент? Нельзя ли сократить до самого необходимого? Ответьте, отредактировав свой ответ, а не здесь, в комментариях (при необходимости).
@PeterMortensen Я не претендую на лучшее. Также это мое личное решение проблемы, есть и другие варианты получше, более профессиональные. Как кто-то предположил, он не подходит для производства. Css есть bcz Я только что вырезал свой личный код
@PeterMortensen также нет единого решения этой проблемы, возможно, он получил голоса за bcz, он решает проблему, с которой они сталкиваются, а не bcz в лучшем виде
Мне нужно обрабатывать фатальные ошибки для производства, чтобы вместо этого отображать вывод HTML в статическом стиле 503 Служба Недоступна. Это, безусловно, разумный подход к «отлову фатальных ошибок». Вот что я сделал:
У меня есть специальная функция обработки ошибок error_handler, которая будет отображать мою HTML-страницу «503 service unavailable» на любой E_ERROR, E_USER_ERROR и т. д. Теперь она будет вызываться в функции выключения, обнаруживая мою фатальную ошибку,
function fatal_error_handler() {
if (@is_array($e = @error_get_last())) {
$code = isset($e['type']) ? $e['type'] : 0;
$msg = isset($e['message']) ? $e['message'] : '';
$file = isset($e['file']) ? $e['file'] : '';
$line = isset($e['line']) ? $e['line'] : '';
if ($code>0)
error_handler($code, $msg, $file, $line);
}
}
set_error_handler("error_handler");
register_shutdown_function('fatal_error_handler');
в моей пользовательской функции error_handler, если это ошибка E_ERROR, E_USER_ERROR и т.д. Я также вызываю @ob_end_clean();, чтобы очистить буфер, тем самым удаляя сообщение PHP о «фатальной ошибке».
Обратите внимание на строгую проверку isset () и функции отключения звука @, поскольку мы не хотим, чтобы наши сценарии error_handler генерировали какие-либо ошибки.
По-прежнему соглашаясь с keparo, обнаружение фатальных ошибок лишает вас цели «FATAL error», поэтому на самом деле это не предназначено для дальнейшей обработки. Не запускайте никаких функций mail () в этом процессе завершения работы, так как вы обязательно создадите резервную копию почтового сервера или своего почтового ящика. Лучше записать эти вхождения в файл и запланировать задание cron, чтобы найти эти файлы журнал ошибок и отправить их администраторам.
Я разработал эту функцию, чтобы сделать возможным "песочницу" кода, который может вызвать фатальную ошибку. Поскольку исключения, вызванные закрытием register_shutdown_function, не генерируются из стека вызовов до фатальной ошибки, я вынужден выйти после этой функции, чтобы обеспечить единообразный способ ее использования.
function superTryCatchFinallyAndExit( Closure $try, Closure $catch = NULL, Closure $finally )
{
$finished = FALSE;
register_shutdown_function( function() use ( &$finished, $catch, $finally ) {
if ( ! $finished ) {
$finished = TRUE;
print "EXPLODE!".PHP_EOL;
if ( $catch ) {
superTryCatchFinallyAndExit( function() use ( $catch ) {
$catch( new Exception( "Fatal Error!!!" ) );
}, NULL, $finally );
} else {
$finally();
}
}
} );
try {
$try();
} catch( Exception $e ) {
if ( $catch ) {
try {
$catch( $e );
} catch( Exception $e ) {}
}
}
$finished = TRUE;
$finally();
exit();
}
PHP имеет обнаруживаемые фатальные ошибки. Они определены как E_RECOVERABLE_ERROR. Руководство по PHP описывает E_RECOVERABLE_ERROR как:
Catchable fatal error. It indicates that a probably dangerous error occured, but did not leave the Engine in an unstable state. If the error is not caught by a user defined handle (see also set_error_handler()), the application aborts as it was an E_ERROR.
Вы можете «поймать» эти «фатальные» ошибки, используя set_error_handler () и проверяя E_RECOVERABLE_ERROR. Я считаю полезным генерировать исключение при обнаружении этой ошибки, тогда вы можете использовать try / catch.
Этот вопрос и ответ представляют собой полезный пример: Как я могу отловить «обнаруживаемую фатальную ошибку» при хинтинге типа PHP?
Однако ошибки E_ERROR можно обработать, но нельзя исправить, поскольку механизм находится в нестабильном состоянии.
Я разработал способ перехвата всех типов ошибок в 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');
// Custom error handling vars
define('DISPLAY_ERRORS', TRUE);
define('ERROR_REPORTING', E_ALL | E_STRICT);
define('LOG_ERRORS', TRUE);
register_shutdown_function('shut');
set_error_handler('handler');
// Function to catch no user error handler function errors...
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: // 1 //
$typestr = 'E_ERROR'; break;
case E_WARNING: // 2 //
$typestr = 'E_WARNING'; break;
case E_PARSE: // 4 //
$typestr = 'E_PARSE'; break;
case E_NOTICE: // 8 //
$typestr = 'E_NOTICE'; break;
case E_CORE_ERROR: // 16 //
$typestr = 'E_CORE_ERROR'; break;
case E_CORE_WARNING: // 32 //
$typestr = 'E_CORE_WARNING'; break;
case E_COMPILE_ERROR: // 64 //
$typestr = 'E_COMPILE_ERROR'; break;
case E_CORE_WARNING: // 128 //
$typestr = 'E_COMPILE_WARNING'; break;
case E_USER_ERROR: // 256 //
$typestr = 'E_USER_ERROR'; break;
case E_USER_WARNING: // 512 //
$typestr = 'E_USER_WARNING'; break;
case E_USER_NOTICE: // 1024 //
$typestr = 'E_USER_NOTICE'; break;
case E_STRICT: // 2048 //
$typestr = 'E_STRICT'; break;
case E_RECOVERABLE_ERROR: // 4096 //
$typestr = 'E_RECOVERABLE_ERROR'; break;
case E_DEPRECATED: // 8192 //
$typestr = 'E_DEPRECATED'; break;
case E_USER_DEPRECATED: // 16384 //
$typestr = 'E_USER_DEPRECATED'; break;
}
$message =
'<b>' . $typestr .
': </b>' . $errstr .
' in <b>' . $errfile .
'</b> on line <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);
//Logging error on php file error log...
if (LOG_ERRORS)
error_log(strip_tags($message), 0);
}
ob_start();
@include 'content.php';
ob_end_flush();
?>
Что делает строка @include 'content.php'?
Если вы используете PHP> = 5.1.0 Просто сделайте что-нибудь подобное с классом ErrorException:
<?php
// Define an error handler
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}
// Set your error handler
set_error_handler("exception_error_handler");
/* Trigger exception */
try
{
// Try to do something like finding the end of the internet
}
catch(ErrorException $e)
{
// Anything you want to do with $e
}
?>
Существуют определенные обстоятельства, при которых должны быть обнаружены даже фатальные ошибки (вам может потребоваться выполнить некоторую очистку перед выходом изящно и не просто умереть ...)
Я реализовал ловушку pre_system в своих приложениях CodeIgniter, чтобы я мог получать свои фатальные ошибки по электронной почте, и это помогло мне найти ошибки, о которых не сообщалось (или о которых сообщалось после того, как они были исправлены, поскольку я уже знал о них :)).
Sendemail проверяет, не сообщалось ли уже об ошибке, чтобы не отправлять вам спам с известными ошибками несколько раз.
class PHPFatalError {
public function setHandler() {
register_shutdown_function('handleShutdown');
}
}
function handleShutdown() {
if (($error = error_get_last())) {
ob_start();
echo "<pre>";
var_dump($error);
echo "</pre>";
$message = ob_get_clean();
sendEmail($message);
ob_start();
echo '{"status":"error","message":"Internal application error!"}';
ob_flush();
exit();
}
}
Что такое "Sendemail"? Вы имеете в виду Отправить письмо (ответ от редактирование вашего ответа, а не здесь в комментариях)?
Вот просто хороший трюк, чтобы получить текущий метод error_handler =)
<?php
register_shutdown_function('__fatalHandler');
function __fatalHandler()
{
$error = error_get_last();
// Check if it's a core/fatal error. Otherwise, it's a normal shutdown
if ($error !== NULL && $error['type'] === E_ERROR) {
// It is a bit hackish, but the set_exception_handler
// will return the old handler
function fakeHandler() { }
$handler = set_exception_handler('fakeHandler');
restore_exception_handler();
if ($handler !== null) {
call_user_func(
$handler,
new ErrorException(
$error['message'],
$error['type'],
0,
$error['file'],
$error['line']));
}
exit;
}
}
?>
Также хочу отметить, что если вы позвоните
<?php
ini_set('display_errors', false);
?>
PHP перестает отображать ошибку. В противном случае текст ошибки будет отправлен клиенту до вашего обработчика ошибок.
Проголосовал за это из-за строки ini_set ('display_errors', false);
Если по какой-то причине этот бит включен, он все равно будет отображать ошибки php, даже если вы обрабатываете его по-другому
/**
* ErrorHandler that can be used to catch internal PHP errors
* and convert to an ErrorException instance.
*/
abstract class ErrorHandler
{
/**
* Active stack
*
* @var array
*/
protected static $stack = array();
/**
* Check if this error handler is active
*
* @return bool
*/
public static function started()
{
return (bool) static::getNestedLevel();
}
/**
* Get the current nested level
*
* @return int
*/
public static function getNestedLevel()
{
return count(static::$stack);
}
/**
* Starting the error handler
*
* @param int $errorLevel
*/
public static function start($errorLevel = \E_WARNING)
{
if (!static::$stack) {
set_error_handler(array(get_called_class(), 'addError'), $errorLevel);
}
static::$stack[] = null;
}
/**
* Stopping the error handler
*
* @param bool $throw Throw the ErrorException if any
* @return null|ErrorException
* @throws ErrorException If an error has been catched and $throw is true
*/
public static function stop($throw = false)
{
$errorException = null;
if (static::$stack) {
$errorException = array_pop(static::$stack);
if (!static::$stack) {
restore_error_handler();
}
if ($errorException && $throw) {
throw $errorException;
}
}
return $errorException;
}
/**
* Stop all active handler
*
* @return void
*/
public static function clean()
{
if (static::$stack) {
restore_error_handler();
}
static::$stack = array();
}
/**
* Add an error to the stack
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @return void
*/
public static function addError($errno, $errstr = '', $errfile = '', $errline = 0)
{
$stack = & static::$stack[count(static::$stack) - 1];
$stack = new ErrorException($errstr, 0, $errno, $errfile, $errline, $stack);
}
}
Этот класс позволяет вам иногда запускать конкретный ErrorHandler, если он вам нужен. И тогда вы также можете остановить обработчик.
Используйте этот класс, например. нравится:
ErrorHandler::start(E_WARNING);
$return = call_function_raises_E_WARNING();
if ($innerException = ErrorHandler::stop()) {
throw new Exception('Special Exception Text', 0, $innerException);
}
// or
ErrorHandler::stop(true); // directly throws an Exception;
Ссылка на полный код класса:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php
Он также может обрабатывать FATAL_ERRORS с помощью функции register_shutdown_function. Согласно этому классу FATAL_ERROR является одним из следующих array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR).
class ErrorHandler
{
// [...]
public function registerExceptionHandler($level = null, $callPrevious = true)
{
$prev = set_exception_handler(array($this, 'handleException'));
$this->uncaughtExceptionLevel = $level;
if ($callPrevious && $prev) {
$this->previousExceptionHandler = $prev;
}
}
public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1)
{
$prev = set_error_handler(array($this, 'handleError'), $errorTypes);
$this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
if ($callPrevious) {
$this->previousErrorHandler = $prev ?: true;
}
}
public function registerFatalHandler($level = null, $reservedMemorySize = 20)
{
register_shutdown_function(array($this, 'handleFatalError'));
$this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
$this->fatalLevel = $level;
}
// [...]
}
Поскольку большинство ответов здесь излишне многословны, вот моя не уродливая версия ответа, получившего наибольшее количество голосов:
function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array()) {
//Do stuff: mail, log, etc
}
function fatalHandler() {
$error = error_get_last();
if ($error) errorHandler($error["type"], $error["message"], $error["file"], $error["line"]);
}
set_error_handler("errorHandler")
register_shutdown_function("fatalHandler");
Вы не можете поймать / обработать фатальные ошибки, но вы можете зарегистрировать / сообщить о них. Для быстрой отладки я изменил один ответ на этот простой код
function __fatalHandler()
{
$error = error_get_last();
// Check if it's a core/fatal error, otherwise it's a normal shutdown
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>fatal error:\n";
print_r($error);
echo "</pre>";
die;
}
}
register_shutdown_function('__fatalHandler');
@TKoL первая строка. В основном входной файл вашего скрипта / программы, поэтому он выполняется первым, если это невозможно, поместите его в общий файл
Фатальные ошибки или исправимые фатальные ошибки теперь вызывают экземпляры Error в PHP 7 или более поздние версии. Как и любые другие исключения, объекты Error могут быть перехвачены с помощью блока try/catch.
Пример:
<?php
$variable = 'not an object';
try {
$variable->method(); // Throws an Error object in PHP 7 or higger.
} catch (Error $e) {
// Handle error
echo $e->getMessage(); // Call to a member function method() on string
}
Или вы можете использовать интерфейс Throwable для перехвата всех исключений.
Пример:
<?php
try {
undefinedFunctionCall();
} catch (Throwable $e) {
// Handle error
echo $e->getMessage(); // Call to undefined function undefinedFunctionCall()
}
Для получения дополнительной информации: http://php.net/manual/en/language.errors.php7.php
Есть идеи о том, как использовать это, чтобы поймать ошибку, такую как Fatal error: Trait 'FailedTrait' not found in, при использовании ReflectionClass?
@ TCB13 пытается обернуть внутреннее содержимое try в файл и include "filename.php" вместо этого в блок try, тогда блок захвата Throwable, по крайней мере, работает для ParseError.