Error handling stack in PHP 5.3+

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 #6 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.

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:

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 set_error_handler 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 set_error_handler.

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:
PHP Manual for set_error_handler and anonymous functions and closures.
My book, Expert PHP and MySQL, also talks about closures in depth starting on page 78.

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.

Tagged with:
Posted in Code
4 comments on “Error handling stack in PHP 5.3+
  1. parjo says:

    What is ‘handeling’?

  2. @parjo A typo. It should have said ‘handling’

    I just fixed it, but I left it in the URL so that links to this article will still work. Thanks for catching that.

  3. Petah says:

    I few things that I notice is:

    What happens if an error occurs while handling errors? An infinite loop maybe.
    What if the call used the error suppressing operator? The error handler is still called.
    What if output buffering is enabled? The error messages could get sucked up and not displayed.

    Ive been using something similar to this for a while, Ill paste a snippet for you to have a look

    http://pastebin.com/YMCvtcVv

  4. @Petah, those are good question and ones that I didn’t know the answer so I ran some tests.

    First, the question about when an error handler itself throws an error: no, it does not cause an infinite loop (or any loop for that matter). PHP is smart about it and immediately falls through to the default PHP error handler. In the snippet you posted it does the same thing. I was not, able to get your “if (self::$handling_error) {” to trigger (at least in my version of PHP).

    Second, as far as output buffering goes: you are correct. In my example, the output of the handler would be sucked up and never displayed.

    Technically, the behavior that should happen (to be consistent with PHP) is to honor output buffering on warnings and notices and kill output buffering on errors.

    With your code, what might work is to put a try/catch block around the loop in the error handler and put your fall through code inside the “catch” block.

    Note: I used PHP 5.3.2 and this is something that I can definitely see changing between versions. Older versions might cause loops, as you say.

    Also, just to be clear, the code in this article is not intended for production but more as an example of recursion and closures in PHP 5.3. A production ready error handler should be a lot more robust than this.

1 Pings/Trackbacks for "Error handling stack in PHP 5.3+"
  1. [...] This post was mentioned on Twitter by Andrew Curioso, Danny Kopping. Danny Kopping said: @AndrewCurioso Thanks mate! RT @AndrewCurioso New blog/code entry: create a stack of error handlers in PHP http://bit.ly/bxg8zt #PHP [...]

Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>