Error handling stack in PHP 5.3+

13 Oct 2010

Archive Notice: This post is an archive post from my older blog. Comments are disabled and the information contained in it may no longer be accurate.

Update: this article is mentioned in a few places as a practical example of using closures. Some languages pass variables into a closure automatically. In PHP it needs to be done explicitly using the use keyword. See line #7 of the code example.

I was inspired by a question that I was asked on Twitter to write a quick code snippet.

As you may know, set_error_handler can be used to set a custom error handler in PHP. It will catch any errors that happen in the script (with a few notable exceptions). If the function returns false then error handling resumes as normal; otherwise it is assumed that the custom handler took care of things. The problem is that you can only have one error handler active at one time. The purpose of this code is to provide a error handeling stack for PHP.

Using this code you can have more than one error handler while taking advantage of the set_error_handler function.

Because this example uses closures, it will only work in PHP 5.3 or newer.

<?php
function push_error_handler( $error_handler, $error_types = 32767 )
{
  $old_callback = null;
 
  $callback = function( $errno, $errstr, $errfile, $errline, $errcontext )
  use ( &$old_callback, $error_handler )
  {
    $result = call_user_func($error_handler, $errno , $errstr, $errfile, $errline, $errcontext);
 
    if ( $result === false && $old_callback != null )
      return call_user_func( $old_callback, $errno , $errstr, $errfile, $errline, $errcontext );
 
    return $result;
  };
 
  $old_callback = set_error_handler($callback, $error_types);
}

?>

Let’s see it in action:

<?php
function test1( $a, $b, $c, $d, $e ) { echo "Test 1 -- "; return false; }
function test2( $a, $b, $c, $d, $e ) { echo "Test 2 -- "; return false; }
function test3( $a, $b, $c, $d, $e ) { echo "Test 3 -- "; return false; }
function test4( $a, $b, $c, $d, $e ) { echo "Test 4 -- "; return false; }
 
push_error_handler( 'test1' );
push_error_handler( 'test2' );
push_error_handler( 'test3' );
push_error_handler( 'test4' );
 
trigger_error('testing');

?>

The output is:

Test 4 -- Test 3 -- Test 2 -- Test 1 --
Notice: testing in /Users/andrew/recursive_error_handler.php on line 32

It is worth a closer look. First we initialize a variable that will be needed by the closure later on. Then we define the closure (which will be our actual error handling function) and register it as the error handler. The function seterrorhandler returns the old handler which we then assign to the variable that we created earlier. We also return the old handler to keep compatibility with the normal seterrorhandler.

The closure itself calls the new error handler. If the handler returned false (resume error handeling) then we call the previous error handler (if there was one). We also make sure to pass the error code and the old callback function into the closure. It is important that the old callback is passed by reference (note the &).

If you want to learn some more, here are some handy links:

Note: this method only works if almost all the error handlers in the application use this or a similar method. Otherwise the chain will be broken. I say “almost” because the first registered error handler doesn’t have to worry about calling the previous handler since there isn’t any at that point.