Как я могу поймать ВСЕ ошибки в PHP?

Я нашел много попыток реализации комплексных обработок ошибок, и я подумал, что мог бы написать вики-стиль, чтобы, надеюсь, предоставить полное решение, которое я придумал.

Вопрос в том:

«Как можно перехватить, обработать или перехватить ВСЕ типы ошибок в PHP?»

Сейчас — это может быть сочтено «переписанным» некоторыми — но я не знаю, было ли предложено комплексное решение.

1

Решение

Есть три вида обработчиков ошибок, которые вам нужны:

  • set_exception_handler, поймать любые иные непонятные исключения.

  • set_error_handler ловить «стандартные» ошибки PHP. Я хотел бы сначала проверить это против error_reporting чтобы увидеть, если это ошибка, которую следует обработать или игнорировать (я обычно игнорирую уведомления — возможно, это плохо, но это мой выбор), и бросить ErrorException, позволяя обработчику исключений перехватить его для вывода.

  • register_shutdown_function в сочетании с error_get_last. Проверьте значение ['type'] чтобы увидеть, если это E_ERROR, E_PARSE или в основном любые типы фатальных ошибок, которые вы хотите поймать. Эти обычно обходят set_error_handlerтак что ловя их здесь, вы можете их схватить. Опять же, я склонен просто бросить ErrorException, так что все ошибки в конечном итоге обрабатываются одним и тем же обработчиком исключений.

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

3

Другие решения

Существует несколько уровней ошибок PHP, некоторые из которых требуют установки отдельных обработчиков ошибок, и для того, чтобы перехватить каждую ошибку, которую может вызвать PHP — вы должны написать что-то, что охватывает все эти «типы» ошибок — запуск ». время выполнения »и исключения.

Мое решение поймать каждую (насколько я могу судить) ошибку, которая приходит по трубе:

  • Пара глобальных переменных
  • Метод инициализации
  • 4 «не OOP» методы обработки ошибок
  • Класс ErrorHandler с методом AppendError, где можно вносить изменения в то, как именно выводятся ошибки или нет (в этом случае ошибки просто выводятся на экран в некотором минимальном HTML-коде из этого метода)

// Moved this line to the bottom of the 'file' for usability -
// I keep each of the above mentioned 'pieces' in separate files.
//$ErrorHandler = new ErrorHandler();

$ErrorCallback = "HandleRuntimeError";
$ExceptionCallback = "HandleException";
$FatalCallback = "HandleFatalError";

$EnableReporting = true;
$ErrorLevel = E_ALL;

function InitializeErrors()
{
if($GLOBALS["EnableReporting"])
{
error_reporting($GLOBALS["ErrorLevel"]);

if( isset($GLOBALS["ErrorCallback"]) && strlen($GLOBALS["ErrorCallback"]) > 0 )
{
set_error_handler($GLOBALS["ErrorCallback"]);

// Prevent the PHP engine from displaying runtime errors on its own
ini_set('display_errors',false);
}
else
ini_set('display_errors',true);

if( isset($GLOBALS["FatalCallback"]) && strlen($GLOBALS["FatalCallback"]) > 0 )
{
register_shutdown_function($GLOBALS["FatalCallback"]);

// Prevent the PHP engine from displaying fatal errors on its own
ini_set('display_startup_errors',false);
}
else
ini_set('display_startup_errors',true);

if( isset($GLOBALS['ExceptionCallback']) && strlen($GLOBALS['ExceptionCallback']) > 0 )
set_exception_handler($GLOBALS["ExceptionCallback"]);
}
else
{
ini_set('display_errors',0);
ini_set('display_startup_errors',0);
error_reporting(0);
}
}

function HandleRuntimeError($ErrorLevel,$ErrorMessage,$ErrorFile=null,$ErrorLine=null,$ErrorContext=null)
{
if( isset($GLOBALS['ErrorHandler']))
{
//  Pass errors up to the global ErrorHandler to be later inserted into
// final output at the appropriate time.
$GLOBALS['ErrorHandler']->AppendError($ErrorLevel,"Runtime Error: " . $ErrorMessage,$ErrorFile,$ErrorLine,$ErrorContext);

return true;
}
else
{
PrintError($ErrorLevel,$ErrorMessage,$ErrorFile,$ErrorLine,$ErrorContext);
return true;
}
}

function HandleException($Exception)
{
if( isset($GLOBALS['ErrorCallback']))
{
// Parse and pass exceptions up to the standard error callback.
$GLOBALS['ErrorCallback']($Exception->getCode(), "Exception: " . $Exception->getMessage(), $Exception->getFile(), $Exception->getLine(), $Exception->getTrace());

return true;
}
else
{
PrintError($Exception->getCode(), "Exception: " . $Exception->getMessage(), $Exception->getFile(), $Exception->getLine(), $Exception->getTrace());
return true;
}
}

function HandleFatalError()
{
$Error = error_get_last();

// Unset Error Type and Message implies a proper shutdown.
if( !isset($Error['type']) && !isset($Error['message']))
exit();
else if( isset($GLOBALS['ErrorCallback']))
{
// Pass fatal errors up to the standard error callback.
$GLOBALS["ErrorCallback"]($Error['type'], "Fatal Error: " . $Error['message'],$Error['file'],$Error['line']);

return null;
}
else
{
PrintError($Error['type'], "Fatal Error: " . $Error['message'],$Error['file'],$Error['line']);
return null;
}
}

// In the event that our 'ErrorHandler' class is in fact the generator of the error,
// we need a plain-Jane method that will still deliver the message.
function PrintError($ErrorLevel,$ErrorMessage,$ErrorFile=null,$ErrorLine=null,$ErrorContext=null)
{
if( class_exists("ErrorHandler"))
$ErrorTypeString = ErrorHandler::ErrorTypeString($ErrorLevel);
else
$ErrorTypeString = "$ErrorLevel";

if( isset($ErrorContext) && !is_array($ErrorContext) && strlen($ErrorContext) > 0 )
$ErrorContext = str_replace("#", "<br/>\r\n#", $ErrorContext);

$ReturnValue = "";
$ReturnValue .= "<div class=\"$ErrorTypeString\" style=\"margin: 10px;\">\r\n";

$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Error Level:</span> <span class=\"ErrorValue\">$ErrorTypeString</span></p>\r\n";

if( isset($ErrorFile) && strlen($ErrorFile) > 0 )
$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">File:</span> <span class=\"ErrorValue\">'$ErrorFile'</span></p>\r\n";

if( isset($ErrorLine) && strlen($ErrorLine) > 0 )
$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Line:</span> <span class=\"ErrorValue\">$ErrorLine</span></p>\r\n";

if( isset($ErrorContext) && is_array($ErrorContext))
$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">" . var_export($ErrorContext,true) . "</span></p>\r\n";
else if( isset($ErrorContext) && strlen($ErrorContext) > 0 )
$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">$ErrorContext</span></p>\r\n";

$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Message:</span> <span class=\"ErrorValue\">" . str_replace("\r\n","<br/>\r\n",$ErrorMessage) . "</span></p>\r\n";

$ReturnValue .= "</div>\r\n";

echo($ReturnValue);
}

class ErrorHandler
{
public function AppendError($ErrorLevel,$ErrorMessage,$ErrorFile=null,$ErrorLine=null,$ErrorContext=null)
{
// Perhaps evaluate the error level and respond accordingly
//
// In the event that this actually gets used, something that might
// determine if you're in a production environment or not, or that
// determines if you're an admin or not - or something - could belong here.
// Redirects or response messages accordingly.
$ErrorTypeString = ErrorHandler::ErrorTypeString($ErrorLevel);

if( isset($ErrorContext) && !is_array($ErrorContext) && strlen($ErrorContext) > 0 )
$ErrorContext = str_replace("#", "<br/>\r\n#", $ErrorContext);

$ReturnValue = "";
$ReturnValue .= "<div class=\"$ErrorTypeString\" style=\"margin: 10px;\">\r\n";

$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Error Level:</span> <span class=\"ErrorValue\">$ErrorTypeString</span></p>\r\n";

if( isset($ErrorFile) && strlen($ErrorFile) > 0 )
$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">File:</span> <span class=\"ErrorValue\">'$ErrorFile'</span></p>\r\n";

if( isset($ErrorLine) && strlen($ErrorLine) > 0 )
$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Line:</span> <span class=\"ErrorValue\">$ErrorLine</span></p>\r\n";

if( isset($ErrorContext) && is_array($ErrorContext))
$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">" . var_export($ErrorContext,true) . "</span></p>\r\n";
else if( isset($ErrorContext) && strlen($ErrorContext) > 0 )
$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">$ErrorContext</span></p>\r\n";

$ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Message:</span> <span class=\"ErrorValue\">" . str_replace("\r\n","<br/>\r\n",$ErrorMessage) . "</span></p>\r\n";

$ReturnValue .= "</div>\r\n";

echo($ReturnValue);
}

public static function ErrorTypeString($ErrorType)
{
$ReturnValue = "";

switch( $ErrorType )
{
default:
$ReturnValue = "E_UNSPECIFIED_ERROR";
break;
case E_ERROR: // 1 //
$ReturnValue = 'E_ERROR';
break;
case E_WARNING: // 2 //
$ReturnValue = 'E_WARNING';
break;
case E_PARSE: // 4 //
$ReturnValue = 'E_PARSE';
break;
case E_NOTICE: // 8 //
$ReturnValue = 'E_NOTICE';
break;
case E_CORE_ERROR: // 16 //
$ReturnValue = 'E_CORE_ERROR';
break;
case E_CORE_WARNING: // 32 //
$ReturnValue = 'E_CORE_WARNING';
break;
case E_COMPILE_ERROR: // 64 //
$ReturnValue = 'E_COMPILE_ERROR';
break;
case E_CORE_WARNING: // 128 //
$ReturnValue = 'E_COMPILE_WARNING';
break;
case E_USER_ERROR: // 256 //
$ReturnValue = 'E_USER_ERROR';
break;
case E_USER_WARNING: // 512 //
$ReturnValue = 'E_USER_WARNING';
break;
case E_USER_NOTICE: // 1024 //
$ReturnValue = 'E_USER_NOTICE';
break;
case E_STRICT: // 2048 //
$ReturnValue = 'E_STRICT';
break;
case E_RECOVERABLE_ERROR: // 4096 //
$ReturnValue = 'E_RECOVERABLE_ERROR';
break;
case E_DEPRECATED: // 8192 //
$ReturnValue = 'E_DEPRECATED';
break;
case E_USER_DEPRECATED: // 16384 //
$ReturnValue = 'E_USER_DEPRECATED';
break;
}

return $ReturnValue;
}
}

$ErrorHandler = new ErrorHandler();

Теперь — код с дороги …

Чтобы реализовать это, достаточно просто включить этот файл и выполнить метод InitializeErrors. Помимо этого, вам решать, что вы хотите сделать с ошибками; это просто оболочка для каждой ошибки, которую может сгенерировать PHP — и для внесения изменений в то, как обрабатывается любая конкретная ошибка, это в основном так же просто, как оценить ее в методе AppendError и ответить соответствующим образом.

— Я должен отметить — я реализовал возвращаемые значения по причинам, которые я не могу объяснить, и я должен пересмотреть свою собственную работу в этом направлении — но я не очень уверен, что это имеет какое-то отношение к результату.

3

В файл [Config.php] в [phpMyAdmin-4.6.5.2-all-languages] вы можете найти:

/**
* Error handler to catch fatal errors when loading configuration
* file
*
*
* PMA_Config_fatalErrorHandler
* @return void
*/
public static function fatalErrorHandler()
{
if (!isset($GLOBALS['pma_config_loading'])
|| !$GLOBALS['pma_config_loading']
) {
return;
}

$error = error_get_last();
if ($error === null) {
return;
}

PMA_fatalError(
sprintf(
'Failed to load phpMyAdmin configuration (%s:%s): %s',
Error::relPath($error['file']),
$error['line'],
$error['message']
)
);
}

и только в конце файла:

register_shutdown_function(array('PMA\libraries\Config', 'fatalErrorHandler'));
0