I edited this page on the 21 March 2010 because a lot of people seem interested and the code as since improved !

PHP has a pretty interesting feature, you can define a callback method to “catch” any error “thrown” in your code. And I’m sure most of you don’t use it. It’s really usefull when you want to make sure to detect error before any user reports it (which can takes time). This is all about avoiding to demolish with some lame errors your “user experience”.

I now use it in each of my index.php pages (which generally loads every other pages), but to speed things up I make it load the actual method only when the error is “catched”.

This is the code we should use to prepare for inclusion:

function lightErrorHandler($errno, $errstr, $errfile, $errline, $shutdown = false ) {
    // When called from the shutdown function, the relative path doesn't work anymore.
    // You have to load the errorHandler function from its absolute path
    // If you don't like that method, you can always preload this function.
    require_once('/home/website/mysite.com/dev-www/include/error/errorHandler.inc.php');
    return errorHandler($errno, $errstr, $errfile, $errline, $shutdown);
}
set_error_handler( 'lightErrorHandler', E_ALL ^ E_NOTICE);
 
function lightExceptionHandler( $exception ) {
    require_once('./include/error/exceptionHandler.inc.php');
    return exceptionHandler( $exception );
}
set_exception_handler( 'lightExceptionHandler' );
 
function shutdown_function() {
    if(is_null($e = error_get_last()) === false && $e['type'] & (E_ALL ^ E_NOTICE) ) {
        lightErrorHandler( $e['type'], $e['message'], $e['file'], $e['line'], true );
    }
}
register_shutdown_function('shutdown_function');

File include/error/errorHandler.inc.php:

function errorHandler($rrno, $errstr, $errfile, $errline, $shutdown) {
    global $engine;
 
    $tab = array(
            'no'    => $errno,
            'str'   => $errstr,
            'file'  => $errfile,
            'line'  => $errline
    );
 
    $message = 'An error happened :'."\n\n".'Error : '."\n".print_r( $tab, true )."\n\n".'StackTrace : '."\n\n".print_r( debug_backtrace(), true )."\r\n".'Memory state : '."\n".print_r( $GLOBALS, true )."\n";
 
    mail(
            'email@company.com',
            'MyProject : Error : '.$errfile.':'.$errline,
            $message
    );
 
    $target = $errfile.':'.$errline;
 
    if ( ! $engine['bug'] && ! $shutdown ) {
        $engine['bug'] = true;
        Logger::log(array(
            'message'      => 'Error : '.$errstr.' ('.$errno.')',
            'type'         => 'error/codeError',
            'target'       => substr( $target, -min(250, strlen( $target ))),
            'data'         => serialize($GLOBALS),
            'level'            => Logger::CRITICAL
        ));
    }
 
    return false;
}

File include/error/exceptionHandler.inc.php:

function exceptionHandler( $exception ) {
    global $engine;
 
 
    $_SESSION['lastException'] = $exception;
 
    $message = 'An error happened :'."\n\n".'Error : '."\n".$exception."\n\n".'StackTrace : '.debug_backtrace()."\r\n".'Informations diverses : '."\n".print_r( $GLOBALS, true )."\n";
 
    mail(
            'email@company.com',
            'MyProject : Error : '.$exception->getFile().':'.$exception->getLine(),
            $message
    );
 
    $target = $exception->getFile().':'.$exception->getLine();
 
    if ( ! $engine['bug'] ) {
        $engine['bug'] = true;
        Logger::log(array(
            'message'      => 'An exception was thrown',
            'type'         => 'error/exception',
            'target'       => substr( $target, -min(250, strlen( $target ))),
            'data'         => serialize($GLOBALS),
            'level'            => Logger::CRITICAL
        ));
    }
 
    return false;
}

debug_backtrace requires PHP 4.3 and set_error_handler only supports error types since PHP 5.0. So, if you plan on using this on a PHP 4.X host, you have to make sure your code doesn’t throw E_NOTICE errors. My code is never `E_NOTICE error safe.