PHP: Catch all exceptions and errors in a normalized and consistent matter

For my small website myhymnal.net I wanted a nice error handler that caught all errors and presented them as nice error pages. The problem is that you have at least three kinds of errors which you have to handle in PHP. Uncaught exceptions, regular errors and fatal errors. After some experimenting I think I’ve found a way to catch them all in a close to consistent matter so that they are easy to deal with.

error_reporting(ENV === 'dev' ? E_ALL : 0);

set_error_handler("error_handler");
set_exception_handler("error_handler");
register_shutdown_function("error_handler");

function error_handler()
{
    // Check for unhandled errors (fatal shutdown)
    $e = error_get_last();

    // If none, check function args (error handler)
    if($e === null)
        $e = func_get_args();

    // Return if no error
    if(empty($e))
        return;

    // "Normalize" exceptions (exception handler)
    if($e[0] instanceof Exception)
    {
        call_user_func_array(__FUNCTION__, array(
            $e[0]->getCode(),
            $e[0]->getMessage(),
            $e[0]->getFile(),
            $e[0]->getLine(),
            $e[0]));
        return;
    }

    $e = array_combine(array('number', 'message', 'file', 'line', 'context'), array_pad($e, 5, null));
   
    var_dump($e);
    exit;
}

This way the only irregularity should be the last property, context. In cases of errors this will be an array with variables in the context where the error occurred, while for exceptions I’m setting it to the Exception itself. Normally you’d then pass $e to something that would format the information nicely to the user, do some logging or whatever.

  • http://sembromation.tumblr.com Ayala

    I was trying to do the exact same thing but I had two different functions: one for the error handler and one for shutdown function. I didn’t treat exceptions because when they are not caught, they become fatal errors with a message of “uncaught exception blablabla + stacktrace”, which would already be handled in the shutdown.

    I like the way you did everything in one function. Do you mind if I use part of your code?