Вы можете сделать это:
function handleError($errno, $errstring, $errfile, $errline, $errcontext) {
if (error_reporting() & $errno) {
// only process when included in error_reporting
return processError($errno, $errstring);
}
return true;
}
function handleException($exception){
// Here, you do whatever you want with the generated
// exceptions. You can store them in a file or database,
// output them in a debug section of your page or do
// pretty much anything else with it, as if it's a
// normal variable
}
function processError($code, $message){
switch ($code) {
case E_ERROR:
case E_CORE_ERROR:
case E_USER_ERROR:
// Throw exception and stop execution of script
throw new Exception($message, $code);
default:
// Execute exception handler and continue execution afterwards
return handleException(new Exception($message, $code));
}
}
// Set error handler to your custom handler
set_error_handler('handleError');
// Set exception handler to your custom handler
set_exception_handler('handleException');
// ---------------------------------- //
// Generate warning
processError(E_USER_WARNING, 'This went wrong, but we can continue');
// Generate fatal error :
processError(E_USER_ERROR, 'This went horrible wrong');
Альтернативный подход:
function handleError($errno, $errstring, $errfile, $errline, $errcontext) {
if (error_reporting() & $errno) {
// only process when included in error_reporting
return handleException(new \Exception($errstring, $errno));
}
return true;
}
function handleException($exception){
// Here, you do whatever you want with the generated
// exceptions. You can store them in a file or database,
// output them in a debug section of your page or do
// pretty much anything else with it, as if it's a
// normal variable
switch ($code) {
case E_ERROR:
case E_CORE_ERROR:
case E_USER_ERROR:
// Make sure script exits here
exit(1);
default:
// Let script continue
return true;
}
}
// Set error handler to your custom handler
set_error_handler('handleError');
// Set exception handler to your custom handler
set_exception_handler('handleException');
// ---------------------------------- //
// Generate warning
trigger_error('This went wrong, but we can continue', E_USER_WARNING);
// Generate fatal error :
trigger_error('This went horrible wrong', E_USER_ERROR);
Преимущество последней стратегии заключается в том, что вы получаете параметр $errcontext
, если вы делаете $exception->getTrace()
в функции handleException
.
Это очень полезно для определенных целей отладки. К сожалению, это работает, только если вы используете trigger_error
непосредственно из своего контекста, что означает, что вы не можете использовать функцию / метод-оболочку для псевдонима функции trigger_error
(поэтому вы не можете сделать что-то вроде function debug($code, $message) { return trigger_error($message, $code); }
, если хотите контекстные данные в вашей трассировке).
EDIT
Я нашел один dirty
способ решения проблемы trigger_error
.
Рассмотрим следующий код:
define("__DEBUG__", "Use of undefined constant DEBUG - assumed 'DEBUG'");
public static function handleError($code, $message, $file, $line, $context = false) {
if ($message == __DEBUG__) {
return static::exception(new \Exception(__DEBUG__, E_USER_WARNING));
} else {
if (error_reporting() & $code) {
return static::exception(new \Exception($message, $code));
}
return true;
}
}
public static function handleException($e) {
global $debug;
$code = $e->getCode();
$trace = $e->getTrace();
if ($e->getMessage() == __DEBUG__) {
// DEBUG
array_push($debug, array(
'__TIME__' => microtime(),
'__CONTEXT__' => array(
'file' => $trace[0]['file'],
'line' => $trace[0]['line'],
'function' => $trace[1]['function'],
'class' => $trace[1]['class'],
'type' => $trace[1]['type'],
'args' => $trace[0]['args'][4]
)
));
} else {
// NORMAL ERROR HANDLING
}
return true;
}
С этим кодом вы можете использовать оператор DEBUG;
, чтобы сгенерировать список всех доступных переменных и трассировку стека для любого конкретного контекста. Этот список хранится в глобальной переменной $debug
. Вы можете добавить его в файл журнала, добавить в базу данных или распечатать.
Это ОЧЕНЬ, ОЧЕНЬ грязный хак, поэтому используйте его по своему усмотрению. Тем не менее, это может значительно облегчить отладку и позволяет создать чистый интерфейс для вашего кода отладки.