Team LiB
Previous Section Next Section

Custom Error Handlers

Earlier, we mentioned that PHP will use its default error handler if a custom handler is not defined. Now let's take a look at how we can override that error handler with one of our own. Custom error handlers can receive and process any error condition produced by PHP except the following error codes: E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, and E_PARSE. These severities are considered too dangerous to allow any further script code to execute, even for the purposes of error handling. Therefore, these error codes are always handled by PHP's default error handler.

A custom error handler is identified when a script calls the function set_error_handler() with the name of an error handler function, or with an array containing a class name or object instance as the first value and a method name as the second value.

By default, an error handler callback will receive every error (except those dangerous severities mentioned earlier). To limit an error handler to only certain types of errors, such as the user-generated ones only, you can pass a bit mask of error codes as the second argument of set_error_handler().

set_error_handler('my_error_handler', 
                  E_USER_NOTICE | E_USER_WARNING | E_USER_ERROR);

Any error handler, be it a simple function or a class method, should expect to receive five parameters and return a Boolean value indicating whether it was able to handle the error. A simple error handler is shown next:

set_error_handler('simple_error_handler');
function simple_error_handler(
  $errcode, $errstring, $filename, $lineno, &$scope
) {
 echo $errstring;

 return true;
}

This error handler echos any error message, regardless of severity or the use of the error suppression operator, and returns true to indicate that the error message was handled. Now let's take a look at a slightly more complex error handler:

/* The following line assures that we won't have undefined constants in PHP4 */
if (!defined('E_STRICT')) define('E_STRICT', 2048);

set_error_handler('my_logging_error_handler');

function my_logging_error_handler(
  $errcode, $errstring, $filename, $lineno, &$scope
) {
 $warning_names = array(E_WARNING=>'E_WARNING',
E_USER_WARNING=>'E_USER_WARNING',
E_COMPILE_WARNING=>'E_COMPILE_WARNING', 
E_CORE_WARNING=>'E_CORE_WARNING');
 switch ($errcode) {
  case E_STRICT:
  case E_NOTICE:
  case E_USER_NOTICE:
   break;
  case E_WARNING:
  case E_USER_WARNING:
  case E_COMPILE_WARNING:
  case E_CORE_WARNING:
   error_log('[' . date('n/j/Y g:i a') . 
     "] Warning: [{$warning_names[$errcode]}] $errstring in $filename on $lineno");
   break;
  case E_USER_ERROR:
   echo $errstring;
   $message = "A serious error has occurred!\n Filename: $filename\n" . 
              "Line Number: $lineno\n\n$errstring\n\n";
   $message .= print_r($scope, true);
   mail('developer@example.com', "Error in script execution", $message);
   break;
  default:
   return false;
 }

 return true;
}

Going through this code, we see three different handling mechanisms implemented. First, informational errors are completely ignored. As stated earlier, informational errors don't represent a threat to our code, so for the sake of this exercise we can pretend as though they don't exist. Next, the actionable errors are formatted into a human-readable error message and sent to the error log. Taking a look at a few lines of such a log, we might see something like this:


[3/11/2005 11:14 am] Warning: [E_WARNING] Unable to connect to LDAP server,
connection timed out in /home/jdoe/public_html/myscript.php on line 514
[3/18/2005 3:42 pm] Warning: [E_WARNING] Failed to open stream, no such file or directory
 in /home/jdoe/public_html/reporting/mygraph_generator.php on line 29
[4/9/2005 10:51 pm] Warning: [E_USER_WARNING] Invalid data received from user input,
possible hacker? in /home/jdoe/public_html/login.php on line 11

The last error type handled is the one form of fatal error that can reach a custom error handler. Should something like this occur, we don't want to wait for the next time we happen to open the error log to find out something's gone wrong; we want to know about it immediately and fix it even faster. Therefore, we're constructing and sending a detailed error report via email straight to our inbox. A sample error report would probably look something like the following:

A serious error has occurred!
Filename: /home/jdoe/public_html/myscript.php
Line Number: 114

Unable to connect to database server.

Array (
 [userid] => jdoe
 [password] => secret
 [action] => login
 [loop] => 217
 [dbname] => web_application
)

After this error report shows up in our mailbox (perhaps even as a text message sent to a cell phone), we can quickly open up the file in question and diagnose the problem. We could have also included the contents of $GLOBALS and/or the result of debug_backtrace() to have even more debugging information.

NOTE

As of PHP 5.1.0, when a custom error handler issues return false; to indicate that it has not handled the error condition, PHP will send the error message to its internal handler instead.


    Team LiB
    Previous Section Next Section