Developer

No more cryptic error messages with PHP custom error handlers

PHP comes with built-in tools to let the developer trap script errors and divert them to their own user-defined error handler. You can program this handler to display a more informative error message, log the error to a file or database and/or take remedial action.

If you've been working with PHP for any length of time, you probably already know what happens when your PHP script turns out to have an error. Typically, the PHP parser will generate a message on the screen — maybe something like Fatal error: Call to undefined function on line 19 — and immediately stop processing the code. This message will scare your customer, who will immediately start yelling on the phone at you to fix it. If you're really unlucky, this phone call will come at 2.14 AM, just when you've begun dreaming about your next vacation to Aruba...

Fortunately, there's a solution. PHP comes with built-in tools to let the developer trap script errors and divert them to their own user-defined error handler. You can then program this handler to display a more informative error message, log the error to a file or database and/or take remedial action. Heck, you can even program the handler to ignore the error altogether (although this is not recommended if you'd like to keep your job).

In this article, I'll show you how you can use PHP's error handling API to construct your own custom error handler, and use it to manage the display and management of script errors in a simple and user-friendly fashion.

Error types and reporting levels

Let's begin with the basics. PHP comes with three basic error types, ranging in severity from least (notices) to medium (warnings) to most (errors orfatals). Typically, warnings and notices do not result in the script being halted; however, fatal errors are usually critical faults (such as calling a function which does not exist or referencing a non-existent object) that result in immediate script termination. These errors may be generated at startup, at parse-time, compile-time or run-time, or on demand by the script.

Keywords such as E_NOTICE, E_ERROR and so on are used to refer to the different error types and levels. You can obtain a complete list of these, together with corresponding bitmasks, from the PHP manual.

Error display at the per-script level is controlled by the error_reporting() function. This function takes a list of arguments corresponding to the error levels that are to be reported. To see this in action, consider the following script (Listing A), which only reports warnings and errors:

Listing A


<?php
// display warnings and errors
error_reporting(E_WARNING | E_ERROR);

// this will generate a notice, which will never be displayed
echo $undefinedVar;

// this will generate a fatal error, which will be displayed
callUndefFunc();
?>

Contrast it with this one (Listing B), where all errors — even fatal ones — are hidden from view:

Listing B


<?php
// turn off error display
// no errors will be displayed
error_reporting(0);

// this will generate a notice
echo $undefinedVar;

// this will generate a fatal error
callUndefFunc();
?>

Or this one (Listing C), where all errors — even simple notices — are displayed:

Listing C


<?php
// all errors will be displayed
error_reporting(E_ALL);

// this will generate a notice
echo $undefinedVar;

// this will generate a fatal error
callUndefFunc();
?>

As the three snippets above illustrate, the error_reporting() function is important in controlling which errors are displayed to the user. The key word here is displayed: just because an error is not displayed, does not mean that it does not take place. Thus, even with an error reporting level of 0, a fatal error — for example, an incorrect function call — will still stop script execution; however, no message will be displayed to the user to indicate this event.

The following example (Listing D) illustrates:

Listing D


<?php
// no errors will be displayed
error_reporting(0);

// start a task
echo "Starting task...";

// call an undefined function
// a fatal error occurs during task processing
callMe();

// end the task
echo "Successfully completed task...";
?>

Here, a fatal error takes place between the two calls to echo(), and script execution is therefore halted at the point of error. However, because error display is turned off, the user has no knowledge of this and may well assume that the script has successfully executed. The lesson here should be obvious: turning off error display is inherently dangerous, because it may lead to incorrect conclusions about whether a process was successfully completed or not.

Tip: Calling error_reporting() without any arguments returns the current reporting level.

Custom error handlers

Since it's clear that turning error display off is a bad idea, you're probably wondering what the other options are. Well, one of the most elegant solutions to the problem involves replacing PHP's default error-handling system with your own. This custom error handler can be set up to handle errors in exactly the manner you decide, right from how messages are displayed to the format in which they are logged and tracked.

The PHP function to do this is named set_error_handler(), and it accepts the name of a user-defined function to which errors are passed. When an error occurs, this function is automatically called, and passed four arguments: the error code and message, the name of the script that generated the error, and the line number of the statement that generated the error. The function then becomes responsible for managing the error.

Here's a simple example (Listing E):

Listing E


<?php
// define custom handler
set_error_handler('myHandler');

// custom handler code
function myHandler($code, $msg, $file, $line) {
    echo "Just so you know, something went wrong at line $line of your script $file. The system says that the error code was $code, and the reason for the error was: $msg. Sorry about this!";
}

// generate a notice
echo $undefVar;
?>

And when you run the script, you should see something like this:

Just so you know, something went wrong at line 11 of your /dev/error1.php. The system says that the error code was 8, and the reason for the error was: Undefined variable: undefVar. Sorry about this!

Here, PHP's default error handler was replaced by the code inside the user-defined myHandler() function. When the $undefVar variable was invoked, a PHP notice about undefined variables was generated by the run-time engine and passed upwards to the myHandler() function, together with the location of the defective code. The myHandler() function then printed a friendly message explaining what went wrong, using the error information passed to it.

Note: It's important to note that parse errors and fatal errors, because of their very nature, will bypass the custom error handler and will be displayed using the standard PHP error-handling mechanism. Display of these errors can be controlled using the standard error_reporting() function, as discussed previously.

Example #1: Dynamic error pages and e-mail alerts

Here's another example (Listing F), this one taking things a step further by dynamically generating an HTML error page and e-mailing the Webmaster when a problem occurs with a script:

Listing F


<?php
// define custom handler
set_error_handler('myHandler');

// custom handler code
function myHandler($code, $msg, $file, $line, $context) {
    // print error page
    echo "<html><head></head><body>";
    echo "<h2 align=center>Error!</h2>";
    echo "<font color=red size=+1>";
    echo "An error occurred while processing your request. Please visit our <a href=http://www.domain.dom>home page</a> and try again.";
    echo "</font>";
    echo "</body></html>";

    // email error to admin
    $body = "$msg at $file ($line), timed at " . date ("d-M-Y h:i:s", mktime());
    $body .= "\n\n" . print_r($context, TRUE);
    mail ("webmaster@domain.dom", "Web site error", $body);


    // halt execution of script
    die();
}

// generate a notice
echo $undefVar;
?>

Here, the custom error handler dynamically generates an HTML error page every time it encounters an error. The error information is also captured in an email message, which is transmitted to the designated administrator email account using PHP's built-in mail() function.

One addition here is the new $context argument to myHandler(). This fifth argument is an optional supplement to the error information that myHandler() automatically gets, and it contains a snapshot of the current variable state. Including this information in the message helps the administrator obtain a contextual picture of the error, and can significantly reduce debugging time.

Example #2: Custom error logs

Here's another example (Listing G), this one demonstrating how a custom error handler can be used to log detailed error information to a file:

Listing G


<?php
// define custom handler
set_error_handler('myHandler');

// custom handler code
function myHandler($code, $msg, $file, $line) {
    // print error page
    echo "<html><head></head><body>";
    echo "<h2 align=center>Error!</h2>";
    echo "<font color=red size=+1>";
    echo "An error occurred while processing your request. Please visit our <a href=http://www.domain.dom>home page</a> and try again.";
    echo "</font>";
    echo "</body></html>";
   
    // log error to file, with context
    $logData = date("d-M-Y h:i:s", mktime()) . ", $code, $msg, $line, $file\n";
    file_put_contents("web.log", $logData, FILE_APPEND);

    // halt execution of script
    die();
}

// generate a warning
echo is_float();
?>

Similar to the previous example, this one generates an error page and also logs the error meta-data to a file, suitable for later review by the administrator. The data is stored in CSV format, simplifying the task of analysis or report generation. Notice that, in both this example and the previous one, the die() function is called at the end of the error-handling code, to ensure that no further script execution takes place.

As the examples above illustrate, a custom error handler allows you to deal with PHP script errors in the manner that is best suited to application (and user) requirements. You can be as creative as you like with this feature, remembering always that the additional flexibility it gives you is accompanied with a corresponding increase in script overhead and execution time. Have fun, and happy coding!

Editor's Picks

Free Newsletters, In your Inbox