PHP: пользовательский обработчик ошибок — обработка разбора и фатальных ошибок

Как я могу обрабатывать ошибки parse и фатальные с помощью настраиваемого обработчика ошибок?


person A.N.M. Saiful Islam    schedule 14.12.2009    source источник
comment
Почему бы не перехватывать ошибки синтаксического анализа вверх по течению с помощью php -l?   -  person Milo LaMar    schedule 26.12.2018
comment
Как вы делаете это из браузера, который запускает программу PHP?   -  person David Spector    schedule 26.06.2021


Ответы (6)


Простой ответ: вы не можете. См. руководство:

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

Для любой другой ошибки вы можете использовать set_error_handler()

РЕДАКТИРОВАТЬ:

Поскольку кажется, что есть некоторые дискуссии по этой теме, в отношении использования register_shutdown_function, мы должны взглянуть на определение обработки: для меня обработка ошибки означает обнаружение ошибки и реагирование таким образом, чтобы это было «хорошо». для пользователя и базовые данные (базы данных, файлы, веб-службы и т. д.).

Используя register_shutdown_function, вы не можете обработать ошибку из кода, в котором она была вызвана, а это означает, что код все равно перестанет работать в точке, где возникает ошибка. Однако вы можете представить пользователю сообщение об ошибке вместо белой страницы, но вы не можете, например, откатить все, что ваш код делал до сбоя.

person Dan Soap    schedule 14.12.2009
comment
На самом деле вы можете обрабатывать эти ошибки с помощью пользовательской функции. Все, что вам нужно сделать, это определить функцию register_shutdown_function. См. мой ответ ниже для рабочего примера, который я реализовал на своем веб-сайте. - person jdias; 20.09.2011
comment
Да, ты можешь. Однако у него есть некоторые недостатки: см. Мое редактирование выше. - person Dan Soap; 01.10.2011
comment
Спасибо за обновление. На самом деле вы можете сделать больше, чем предоставить пользователю сообщение об ошибке. Вы можете зарегистрировать сведения об ошибке, включая переменные, которые были установлены во время возникновения ошибки. Например, многие люди, использующие виртуальный хостинг, не имеют доступа к журналам Apache. Используя эту функцию, они смогут регистрировать критические ошибки и устранять их. Кроме того, вы можете попытаться восстановить транзакции. Например, если ошибка произошла, когда пользователь пытался разместить заказ, вы можете сбросить все детали заказа в журнал или по электронной почте и попытаться восстановить его в автономном режиме. - person jdias; 01.10.2011
comment
На практике почти единственная разница между фатальной ошибкой и неожиданной нефатальной ошибкой заключается в том, что вы не можете получить трассировку стека для первой. - person Tgr; 02.08.2012
comment
Или вы можете обрабатывать их с помощью приложения, такого как NewRelic, которое позволяет вам захватывать их, а затем активно отслеживать и исправлять их. Если вы просто проигнорируете их, они никогда не будут обработаны. - person GL_Stephen; 31.03.2014
comment
Как отмечалось в других ответах, вы можете поймать все типы ошибок includes (не: require). Так что все, что вам нужно, это небольшой .php файл верхнего уровня, который выполняет всю перехват, который затем include является настоящим файлом. Это относительно легко реализовать с помощью NginX и php-fpm (подсказка: cgi.fix_pathinfo=0). - person Tino; 24.08.2017
comment
Все мои эксперименты проваливаются. Куда идут блоки try/catch? Я всегда вижу сообщение о фатальной ошибке из строки abc();, где нет функции abc. Я хочу поймать это исключение, чтобы сообщение об ошибке не отображалось, и я мог обработать ошибку молча. Я использую Апач 2.4. - person David Spector; 31.01.2020

На самом деле вы можете обрабатывать синтаксический анализ и фатальные ошибки. Это правда, что функция обработчика ошибок, которую вы определили с помощью set_error_handler(), не будет вызываться. Это можно сделать, определив функцию выключения с помощью register_shutdown_function(). Вот что у меня работает на моем сайте:

Файл prepend.php (этот файл будет автоматически добавляться ко всем php-скриптам). Ниже приведены советы по добавлению файлов в PHP.

set_error_handler("errorHandler");
register_shutdown_function("shutdownHandler");

function errorHandler($error_level, $error_message, $error_file, $error_line, $error_context)
{
$error = "lvl: " . $error_level . " | msg:" . $error_message . " | file:" . $error_file . " | ln:" . $error_line;
switch ($error_level) {
    case E_ERROR:
    case E_CORE_ERROR:
    case E_COMPILE_ERROR:
    case E_PARSE:
        mylog($error, "fatal");
        break;
    case E_USER_ERROR:
    case E_RECOVERABLE_ERROR:
        mylog($error, "error");
        break;
    case E_WARNING:
    case E_CORE_WARNING:
    case E_COMPILE_WARNING:
    case E_USER_WARNING:
        mylog($error, "warn");
        break;
    case E_NOTICE:
    case E_USER_NOTICE:
        mylog($error, "info");
        break;
    case E_STRICT:
        mylog($error, "debug");
        break;
    default:
        mylog($error, "warn");
}
}

function shutdownHandler() //will be called when php script ends.
{
$lasterror = error_get_last();
switch ($lasterror['type'])
{
    case E_ERROR:
    case E_CORE_ERROR:
    case E_COMPILE_ERROR:
    case E_USER_ERROR:
    case E_RECOVERABLE_ERROR:
    case E_CORE_WARNING:
    case E_COMPILE_WARNING:
    case E_PARSE:
        $error = "[SHUTDOWN] lvl:" . $lasterror['type'] . " | msg:" . $lasterror['message'] . " | file:" . $lasterror['file'] . " | ln:" . $lasterror['line'];
        mylog($error, "fatal");
}
}

function mylog($error, $errlvl)
{
...do whatever you want...
}

PHP вызовет функцию errorHandler(), если поймает ошибку в любом из скриптов. Если ошибка вызывает немедленное завершение работы скрипта, ошибка обрабатывается функцией shutdownHandler().

Это работает над сайтом, который я разрабатываю. Я еще не тестировал его в производстве. Но в настоящее время он улавливает все ошибки, которые я обнаруживаю при его разработке.

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

TODO:

1 - Мне нужно поработать над лучшей функцией log(), чтобы изящно обрабатывать ошибки. Поскольку я все еще в разработке, я в основном записываю ошибку в базу данных и вывожу ее на экран.

2 - Реализовать обработку ошибок для всех вызовов MySQL.

3 - Реализовать обработку ошибок для моего кода javascript.

ВАЖНЫЕ ПРИМЕЧАНИЯ:

1 - я использую следующую строку в моем php.ini, чтобы автоматически добавлять вышеуказанный скрипт ко всем php-скриптам:

auto_prepend_file = "/homepages/45/d301354504/htdocs/hmsee/cgi-bin/errorhandling.php"

это работает хорошо.

2 - Я регистрирую и исправляю все ошибки, включая ошибки E_STRICT. Я верю в разработку чистого кода. Во время разработки мой файл php.ini имеет следующие строки:

track_errors = 1
display_errors = 1
error_reporting = 2147483647
html_errors = 0

Когда я выйду в эфир, я изменю display_errors на 0, чтобы снизить риск того, что мои пользователи увидят уродливые сообщения об ошибках PHP.

Я надеюсь, что это помогает кому-то.

person jdias    schedule 06.09.2011
comment
Кажется, не обрабатывает ошибки синтаксического анализа... попробуйте следующее: echo Cat; эхо Собака эхо Лев; - person Phantom007; 10.05.2014
comment
Вы должны добавить файл перед обработкой ошибок синтаксического анализа! - person dargmuesli; 24.10.2016
comment
E_DEPRECATED и E_USER_DEPRECATED также должны быть включены в пользовательскую функцию обработчика ошибок. - person Wh1T3h4Ck5; 19.09.2017
comment
Если я вручную добавлю такой файл в начало, он не поймает ошибку abc();, где abc не определен. Отображается необработанная ошибка. Пожалуйста, предоставьте полный тестовый пример. - person David Spector; 31.01.2020

Вы можете отслеживать эти ошибки, используя такой код:

(Ошибки синтаксического анализа можно обнаружить только в том случае, если они возникают в других файлах сценариев через include() или require() или путем помещения этого кода в auto_prepend_file, как упоминалось в других ответах.)

function shutdown() {
    $isError = false;

    if ($error = error_get_last()){
    switch($error['type']){
        case E_ERROR:
        case E_CORE_ERROR:
        case E_COMPILE_ERROR:
        case E_USER_ERROR:
            $isError = true;
            break;
        }
    }

    if ($isError){
        var_dump ($error);//do whatever you need with it
    }
}

register_shutdown_function('shutdown');
person Deniss Kozlovs    schedule 14.12.2009
comment
Да, есть способ поймать E_PARSE! Поместите функцию обработчика ошибок в index.php и включите/требуйте другой файл, где должна быть логика сайта! - person Lucas Batistussi; 13.05.2012
comment
Единственный способ предотвратить отображение ошибок синтаксического анализа — включить сценарий, содержащий ошибку синтаксического анализа. - person Melsi; 27.04.2013
comment
@LucasBatistussi, вы все еще не можете отловить ошибки синтаксического анализа для index.php - person Pacerier; 19.07.2013
comment
Тестирование на php 5.3 - win 7. Функция выключения НЕ вызывается при ошибках синтаксического анализа. - person kwolfe; 18.09.2013
comment
@LucasBatistussi, можешь объяснить? Я поместил функцию обработчика ошибок в index.php, а затем включил ее в свой скрипт, используя include(index.php). Но он по-прежнему не может поймать ошибки синтаксического анализа (синтаксическая ошибка). Я не думаю, что это возможно без встраивания в php.ini. - person Khurshid Alam; 20.08.2014
comment
Когда я пробую тестовый пример abc();, где функция abc не определена, ВНУТРИ включаемого файла сообщение об ошибке PHP все еще появляется. Может ли кто-нибудь предоставить полный демонстрационный код, который работает? - person David Spector; 31.01.2020

Из комментариев PHP.net на странице http://www.php.net/manual/en/function.set-error-handler.php

Я понял, что несколько человек здесь упомянули, что вы не можете фиксировать ошибки синтаксического анализа (тип 4, E_PARSE). Это неправда. Вот как я это делаю. Я надеюсь, что это помогает кому-то.

1) Создайте файл "auto_prepend.php" в корневом каталоге веб-сайта и добавьте следующее:

<?php 
register_shutdown_function('error_alert'); 

function error_alert() 
{ 
        if(is_null($e = error_get_last()) === false) 
        { 
                mail('[email protected]', 'Error from auto_prepend', print_r($e, true)); 
        } 
} 
?> 

2) Затем добавьте этот «php_value auto_prepend_file /www/auto_prepend.php» в ваш файл .htaccess в корневом каталоге веб-сайта.

  • убедитесь, что вы изменили адрес электронной почты и путь к файлу.
person Creativehavoc    schedule 10.06.2011
comment
Это решение охватывает E_PARSE, E_COMPILE_ERROR и т. д. Таким образом, ответ Дэна Соупа и руководство по php.net неверны. Спасибо! - person mgutt; 16.02.2015
comment
Если вы используете FastCgi, вы не можете установить значение PHP в .htaccess. Вместо этого вы используете свой локальный файл php.ini и устанавливаете непосредственно auto_prepend_file = /www/auto_prepend.php - person ; 26.04.2016

По моему опыту, вы можете отловить все типы ошибок, скрыть сообщение об ошибке по умолчанию и отобразить собственное сообщение об ошибке (если хотите). Ниже перечислены вещи, которые вам нужны.

1) Сценарий начального/верхнего уровня, назовем его index.php, где вы храните свои пользовательские функции обработчика ошибок. Обработчики пользовательских функций ошибок должны оставаться наверху, чтобы они ловили ошибки ниже них, под «ниже» я имею в виду во включенных файлах.

2) Предположение, что этот топовый скрипт безошибочен, должно быть верным! это очень важно, вы не сможете отловить фатальные ошибки в index.php, когда ваша пользовательская функция обработчика ошибок будет найдена в index.php.

3) Директивы Php (также должны быть найдены в index.php) set_error_handler("myNonFatalErrorHandler"); #для отлова нефатальных ошибок register_shutdown_function('myShutdown'); #для отлова фатальных ошибок ini_set('display_errors', false); #для скрытия ошибок, отображаемых пользователю php ini_set('log_errors',FALSE); #при условии, что мы сами регистрируем ошибки ini_set('error_reporting', E_ALL); #Мы любим сообщать обо всех ошибках

в то время как в производстве (если я не ошибаюсь) мы можем оставить ini_set('error_reporting', E_ALL); как есть, чтобы иметь возможность регистрировать ошибки, в то же время ini_set('display_errors', false); будет следить за тем, чтобы пользователю не отображались ошибки.

Что касается фактического содержания двух функций, о которых я говорю, myNonFatalErrorHandler и myShutdown, я не помещаю здесь подробное содержание, чтобы все было просто. Кроме того, другие посетители привели множество примеров. Я просто показываю очень простую идею.

function myNonFatalErrorHandler($v, $m, $f, $l, $c){
 $some_logging_var_arr1[]="format $v, $m, $f, ".$err_lvl[$l].", $c the way you like";
 //You can display the content of $some_logging_var_arr1 at the end of execution too.
}

function myShutdown()
{
  if( ($e=error_get_last())!==null ){
      $some_logging_var_arr2= "Format the way you like:". $err_level[$e['type']].$e['message'].$e['file'].$e['line'];
  }
//display $some_logging_var_arr2 now or later, e.g. from a custom session close function
}

что касается $err_lvl, то это может быть:

$err_lvl = array(E_ERROR=>'E_ERROR', E_CORE_ERROR=>'E_CORE_ERROR', E_COMPILE_ERROR=>'E_COMPILE_ERROR', E_USER_ERROR=>'E_USER_ERROR', E_PARSE=>'E_PARSE', E_RECOVERABLE_ERROR=>'E_RECOVERABLE_ERROR', E_WARNING=>'E_WARNING', E_CORE_WARNING=>'E_CORE_WARNING', E_COMPILE_WARNING=>'E_COMPILE_WARNING',
E_USER_WARNING=>'E_USER_WARNING', E_NOTICE=>'E_NOTICE', E_USER_NOTICE=>'E_USER_NOTICE',E_STRICT=>'E_STRICT');
person Melsi    schedule 28.12.2013
comment
Спасибо, что добавили, что функции не могут находиться в том же файле, что и ошибка. - person Niclas; 18.12.2015
comment
Лучше использовать Closure в качестве обработчика ошибок, потому что именованные функции могут быть переопределены в настоящее время - person Tino; 24.08.2017
comment
Это единственный ответ, который работает для меня. Ключ был ini_set('display_errors', false);, чтобы скрыть отчеты PHP. Следует также отметить, что функция set_error_handler не нужна, так как обратный вызов register_shutdown_function получает даже ошибки E_NOTICE, такие как использование определенных констант, которые не были определены. При таком понимании обработка исключений (try/catch) не нужна для многих программ, которые останавливаются при первой обнаруженной ошибке. - person David Spector; 31.01.2020

Скрипт с ошибкой синтаксического анализа всегда прерывается и не может быть обработан. Поэтому, если скрипт вызывается напрямую или с помощью include/require, вы ничего не можете сделать. Но если он вызывается с помощью AJAX, flash или любым другим способом, существует обходной путь для обнаружения ошибок синтаксического анализа.

Мне нужно было это для обработки скрипта swfupload. Swfupload — это флэш-память, которая обрабатывает загрузку файлов, и каждый раз, когда файл загружается, он вызывает скрипт обработки PHP для обработки файловых данных, но вывод браузера отсутствует, поэтому скрипту обработки PHP нужны эти настройки для целей отладки:

  • предупреждения и уведомления ob_start(); в начале и сохранить содержимое в сеансе с помощью ob_get_contents(); в конце скрипта обработки: это может отображаться в браузере другим скриптом
  • фатальные ошибки register_shutdown_function() для установки сеанса с тем же трюком, что и выше.
  • ошибки синтаксического анализа, если ob_get_contents() расположен в конце скрипта обработки, а ошибка синтаксического анализа возникла раньше, сессия не заполняется (она равна нулю). Сценарий отладки может справиться с этим следующим образом: if(!isset($_SESSION["swfupload"])) echo "parse error";

Примечание 1 null означает от is not set до isset()

person Jan Turoň    schedule 02.09.2010