Handling PHP Errors Gracefully

Despite your best efforts, sometimes you will push out PHP code that’s chokes in some unexpected circumstances. In some cases it might be desirable to display a user-friendly failure message instead of showing a partially rendered page, or instead of showing PHP error messages.

I spent some time recently ensuring that an application would return coherent content to the user in as many error situations as possible, and here’s what I came up with. It should handle PHP errors and exceptions.

Note the use of register_shutdown_function to handle errors introduced by certain types of errors, including syntax errors in included PHP files.

This isn’t meant to be an actual solution for you to put on your site, just a starting point to implement your own solution.

<?php 
// This is a skeleton of Error catching which prints user-friendly 
// messages to the user You could check the accept headers to 
// conditionally print JSON or HTML instead of plain text or any 
// number of other enhancements 

// For best use, load as its own file before any other files are 
// loaded, maybe wtih auto_prepend_file. Loading as its own file is 
// best because if this file has been parsed, it can handle errors in 
// other files

// By Michael Moore <stuporglue@gmail.com>
// Placed in the public domain

// Log the error function 
catchError($errno, $errstr, $errfile = '', $errline = ''){  
    if(!(error_reporting() & $errno)){         
       return; // We are not supposed to handle this type of error?      
    }
   
   // Log the message so we can fix it
   error_log($errno . ' ' . $errstr . ' ' . $errfile . ':' . $errline);
   while(@ob_end_clean()); // discard all buffers

   // You could also redirect to a static error page or something if you wanted

   header("Content-type: text/plain; charset=UTF-8"); 
   print "Something bad happened! Friendly error message goes here!";     
   exit(); 
} 

// Catch exceptions gently. Turn them into errors! 
function catchException($exception){     
    $msg = $exception->getMessage() . ' : ' .  $exception->getFile() . ':' .  $exception->getLine() . $exception->getTraceAsString();
    catchError(E_ERROR,$msg);
}

// Set_error_handler doesn't catch these types of errors, but if a shutdown 
// function has already been defined it will still run Here we grab the 
// un-catchable errors and pass them up to catchError
function uncleanShutdownFunction(){
    $lasterror = error_get_last();
    if(in_array($lasterror['type'],Array( E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR, E_CORE_WARNING, E_COMPILE_WARNING, E_PARSE))){
        catchError($lasterror['type'],$lasterror['message'],$lasterror['file'],$lasterror['line']); 
    }
}

// OK! Go time: 

// 1: Start a buffer so we don't print partial pages
ob_start(); 

// set the handlers for...

// ...errors
set_error_handler('catchError',E_ALL);

// ...exceptions
set_exception_handler('catchException');

// ...un-handleable errors
register_shutdown_function('uncleanShutdownFunction');

This entry was posted in Computers, Programming, Something Interesting and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *