Question

According to the comment on this answer it is possible to catch Fatal Errors through a shutdown function which cannot be caught using set_error_handler().

However, I couldn't find out how to determine if the shutdown has occured due to a fatal error or due to the script reaching its end.

Additionally, the debug backtrace functions seem to be defunct in the shutdown function, making it pretty worthless for logging the stack trace where the Fatal Error occured.

So my question is: what's the best way to react on Fatal Errors (especially undefined function calls) while keeping the ability to create a proper backtrace?

Was it helpful?

Solution

This works for me:

function shutdown() {
    $error = error_get_last();
    if ($error['type'] === E_ERROR) {
        // fatal error has occured
    }
}

register_shutdown_function('shutdown');

spl_autoload_register('foo'); 
// throws a LogicException which is not caught, so triggers a E_ERROR

However, you probably know it already, but just to make sure: you can't recover from a E_ERROR in any way.

As for the backtrace, you can't... :( In most cases of a fatal error, especially Undefined function errors, you don't really need it. Pinpointing the file/line where it occured is enough. The backtrace is irrelevant in that case.

OTHER TIPS

One way to distinguish between fatal error and proper application shutdown with the register_shutdown_function is to define a constant as the last line of your program, and then check if the constant is defined:

function fatal_error() {
if ( ! defined(PROGRAM_EXECUTION_SUCCESSFUL)) {
        // fatal error has occurred
    }
}

register_shutdown_function('fatal_error');

define('PROGRAM_EXECUTION_SUCCESSFUL', true);

If the program reaches the end, it could not have encountered a fatal error, so we know not to run the function if the constant is defined.

error_get_last() is an array with all the information regarding the fatal error that you should need to debug, though it will not have a backtrace, as has been mentioned.

Generally, if your php program has encountered a fatal error (as opposed to an exception), you want the program to blow up so you can find and fix the problem. I've found register_shutdown_function useful for production environments where you want error reporting off, but want some way to log the error in the background so that you can respond to it. You could also use the function to direct the user to a friendly html page in the event of such an error so that you don't just serve up a blank page.

Just a nice trick to get the current error_handler method =)

<?php
register_shutdown_function('__fatalHandler');
function __fatalHandler()
{
    $error      = error_get_last();

    //check if it's a core/fatal error, otherwise it's a normal shutdown
    if($error !== NULL && $error['type'] === E_ERROR) {
        //Bit hackish, but the set_exception_handler will return the old handler
        function fakeHandler() { }
        $handler = set_exception_handler('fakeHandler');
        restore_exception_handler();
        if($handler !== null) { 
            call_user_func($handler, new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
        }
        exit;
    }
}
?>

Also i wan't to note that if you call

<?php
ini_set('display_errors', false);
?>

Php stops displaying the error, otherwise the error text will be send to the client prior to your error handler

I use "ob_start" for this.

function fatal_error_handler($buffer) {
    if (preg_match("|(Fatal error:)(.+)|", $buffer, $regs) ) {
        //Your code
    }
    return $buffer;
}
ob_start("fatal_error_handler");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top