Skip to content

Instantly share code, notes, and snippets.

@evaisse
Created January 28, 2011 17:05
Show Gist options
  • Select an option

  • Save evaisse/800563 to your computer and use it in GitHub Desktop.

Select an option

Save evaisse/800563 to your computer and use it in GitHub Desktop.
A hardened error handler for PHP script
<?php # -*- coding:utf-8 -*-
/**
* handle exceptions, errors ( convert them into ErrorException instances) ,
* even fatal errors too via shutdown registrered function.
* Provide a pretty smart debug pageon demand
*
* @example
* register_shutdown_function('ErrorHandler::handleFatalError');
* ErrorHandler::setup();
* // only for PHP5.3
* ErrorHandler::registerEvent('handleException', function($error) {
* mail('debug@admin.com', 'Error on','message' );
* });
* function myfunction() {
* return true;
* }
* ErrorHandler::registerEvent('handleException', 'myfunction');
*/
class ErrorHandler
{
static $debug = false;
static $errnames = array(
1 => "E_ERROR", 2 => "E_WARNING",
4 => "E_PARSE", 8 => "E_NOTICE",
16 => "E_CORE_ERROR", 32 => "E_CORE_WARNING",
64 => "E_COMPILE_ERROR", 128 => "E_COMPILE_WARNING",
256 => "E_USER_ERROR", 512 => "E_USER_WARNING",
1024 => "E_USER_NOTICE", 2048 => "E_STRICT",
4096 => "E_RECOVERABLE_ERROR", 8191 => "E_ALL",
0 => "E_FATAL" );
protected static $isSet = false;
protected static $events = array();
/**
* set or call events
*
*/
public static function registerEvent($name, $args=array())
{
$eventName = (string)$eventName;
if( !isset(self::$events[$eventName]) )
self::$events[$eventName] = array();
if( $args instanceof Closure ) {
// set a closure as hook for event $eventName
$events[$eventName][] = $args;
} else {
// set a classic string callback
$events[$eventName][] = (string)$args;
}
return;
}
// ErrorHandler::event()
/**
*
* @param string $eventName
* @param array $args arguments to pass to
*
* @return mixed return event handler return value
*/
protected static function triggerEvent( $eventName, $args = array() )
{
/*
Call registered events
*/
if(!isset(self::$events[$eventName] )) return;
foreach( self::$events[$eventName] as $event ) {
call_user_func_array( $event, array() );
}
}
//- ErrorHandler::triggerEvent()
/**
* setup error handling environnement.
*
* @param int $errorLevel
*
* @return void
*/
public static function setup($errorLevel=0)
{
if(self::$isSet) return null;
$errorLevel = !is_null($errorLevel) ? $errorLevel : error_reporting();
set_exception_handler( 'ErrorHandler::handle' );
set_error_handler( 'ErrorHandler::handle', error_reporting() );
register_shutdown_function( 'ErrorHandler::handle' );
}
//- ErrorHandler::setup()
/**
*
*
*/
public static function handle($e)
{
if( $event = self::triggerEvent(__FUNCTION__, func_get_args()) ) {
return $event;
}
// use of @ before an expression must shutdown error handling
if( error_reporting() == 0 )
return null;
/*
if the function is called as error handler, error properties are
directly called as parameters.
*/
$e = func_get_args();
if( empty($e) ) {
self::handleFatalError();
} elseif( count($e) == 1 ) {
self::handleException($e[0]);
} elseif( count($e) == 4 ) {
self::handleError($e);
} else {
return null;
}
}
//- ErrorHandler::handle
/**
*
*
* @param array $e error informations
*/
public static function handleError($e)
{
if( $event = self::triggerEvent(__FUNCTION__, func_get_args()) ) {
return $event;
}
$e = new ErrorException( "[".self::$errnames[$e[0]]."] ".$e[1],
0, $e[0], $e[2], $e[3] );
self::handleException($e);
}
//- ErrorHandler::handleError()
/**
* check if there is a fatal error
*
* fatal error can be catched by setting a on shutdown
* error controller
*
* @param mixed $e error parameters or nothing
* @return void
*/
public static function handleFatalError()
{
if( $event = self::triggerEvent(__FUNCTION__, func_get_args()) ) {
return $event;
}
/*
no error, we are into test for fatal error ( shutdown function )
in this specific context, exception must not be throwned.
So we call directly the exception handler to keep the application
error handling logic.
*/
// check if there is a fatal error
if( ( $e = error_get_last() )
// compare bit-wise to current error-reporting level
and ( $e['type'] & error_reporting() )
) {
$e = array_values($e);
$e = new ErrorException( "[FATAL_".self::$errnames[$e[0]]."] ".$e[1],
0, $e[0], $e[2], $e[3]);
self::handleException($e);
}
}
//- ErrorHandler:::handleFatalError()
/**
* handle exception and print error message
*
* @param object Exception $e
*
* @return void
*/
public static function handleException( Exception $e )
{
if( $event = self::triggerEvent(__FUNCTION__, func_get_args()) ) {
return $event;
}
ob_clean();
header('HTTP/1.0 500 Internal Server Error');
error_log($e);
if(!self::$debug) return;
$title = get_class($e);
$tables = '';
$tables .= '<table class="datatable">
<thead><tr><th colspan=2>TRACE</th></tr></thead><tbody>';
foreach(array_reverse($e->getTrace()) as $k => $v) {
$v['line'] = isset($v['line']) ? $v['line'] : '0';
$v['file'] = isset($v['file']) ? $v['file'] : '.';
$v['function'] = isset($v['function']) ? $v['function'] : '.';
$v['class'] = isset($v['class']) ? $v['class'] : '.';
$v['type'] = isset($v['type']) ? $v['type'] : '.';
$tables .= '<tr><th>'.html($v['class'].$v['type'].$v['function'])
.'()</th>'
.'<td>'.html(@$v['line'].':'.$v['file'],true)
.'</td></tr>';
}
$tables .= '</tbody></table>';
$debugs = array( 'GET' => &$_GET, 'POST' => &$_POST,
'COOKIE' => &$_COOKIE, 'SERVER' => &$_SERVER,
'FILES' => &$_FILES );
if(isset($_SESSION)) $debugs['SESSION'] = &$_SESSION;
foreach($debugs as $name => $v) {
$tables .= '<table class="datatable">
<thead><tr><th colspan=2>'.html($name).'</th></tr></thead><tbody>';
if(isset($v)) {
foreach($v as $key => $val) {
$val = (is_array($val) or is_object($val))
? print_r($val,1)
: (string)$val;
$tables .= '<tr><th>'.html($key).'</th>'
.'<td>'.html($val).'</td></tr>';
}
}
$tables .= '</tbody></table>';
}
print '
<html>
<head>
<title>'.$title.'</title>
<style>
.datatable { width:100%;font-family:monospace;margin:10px 0px 5px;
border:1px solid #CCC;border-collapse:collapse; }
.datatable thead { background:#DDD;color:#444 }
.datatable th,td {border:1px solid #CCC;padding:4px 5px;text-align:left}
.datatable tbody th {text-align:right;width:200px}
.datatable tbody tr:nth-child(even) {background: #EEE}
.message {background-color:#EEE;border:1px solid #CCC;padding:12px}
.message pre { background-color:#555;color:#DDD;padding:9px;margin:5px;}
</style>
</head>
<body>
<h2 style="color:red;margin:0px 0px 10px;background-color:#FCC;
border:1px solid #DAA;padding:12px;">'.$title.'</h2>
<div style="padding:10px;margin:10px 0px;
border:1px solid #C00;color:#C00">'.$e->getMessage().'</div>
<div style="padding:10px;margin:10px 0px;
color:#555;border:1px solid #CCC">
Line <tt>'.$e->getLine().'</tt> in <tt>'.$e->getFile().'</tt>
</div>'.$tables.'
</body>
</html>';
exit();
}
//- ErrorHandler::handleException()
}
//- ErrorHandler
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment