PHP provides a variety of tools for handling errors and exceptions, in particular extensible handlers for managing errors and exceptions as they occur. However, there is a range of fatal errors that PHP does not directly provide tools for you to handle them. In the best of scenarios, these fatal errors will result in partially outputted pages, or the “white screen of death.”
These errors include:
E_ERROR
- Fatal run-time errors. These indicate errors that can not be recovered from, such as a memory allocation problem. Execution of the script is haltedE_PARSE
- Compile-time parse errors. Parse errors should only be generated by the parserE_CORE_ERROR
- Fatal errors that occur during PHP’s initial startup. This is like anE_ERROR
, except it is generated by the core of PHPE_COMPILE_ERROR
- Fatal compile-time errors. This is like anE_ERROR
, except it is generated by the Zend Scripting Engine.
By leveraging a combination of shutdown function, error_get_last, and output buffering it is possible to put a structure in place that will allow you to trap fatal errors. The theory of operation is quite simple:
- Register a shutdown function.
- When the shutdown function is called, check to see if the script finished executing as expected by interrogating error_get_last for any fatal errors.
- Handle the fatal error as you see fit.
Let’s look at some code:
Bad Stuff Happend'; echo 'But that is okay
'; echo '' . print_r($error, true) . '
';
}
}
}
ob_end_flush();
?>
The overall page generated is wrapped in an output buffer before being sent to the client. The use of output buffering here allows us to get rid of the partially generated content when the fatal error occurs and replace it with a custom error message. For example:
Bad Stuff Happend'; echo 'But that is okay
'; echo '' . print_r($error, true) . '
';
}
}
}
?>
My Bad Day
What a bad day...
The failed require call generates a fatal E_COMPILE_ERROR. We trap the error, and instead of telling the client about “my bad day” we retract that and give the user an ever so comforting message that all is okay even though our website blew up. Instead of doing something smart like logging the error, we display it:
Bad Stuff Happend
But that is okay
Array
(
[type] => 64
[message] => require() [function.require]: Failed opening required 'FileNotFound.php' (include_path='.:/usr/local/lib/php')
[file] => /home/michael/www/eggplant/public_html/samples/fatal/index.php
[line] => 28
)
If we fix the missing required file by adding the following script:
…we are able to catch the E_PARSE
fatal error in our shutdown handler:
Bad Stuff Happend
But that is okay
Array
(
[type] => 4
[message] => syntax error, unexpected T_STRING
[file] => /home/michael/www/eggplant/public_html/samples/fatal/FileWithParseError.php
[line] => 2
)
Let’s fix up that required file with some real code:
…Another caught E_ERROR
:
Bad Stuff Happend
But that is okay
Array
(
[type] => 1
[message] => Call to undefined function myBadTypo()
[file] => /home/michael/www/eggplant/public_html/samples/fatal/FileThatBlowsMemLimits.php
[line] => 3
)
Finally, let’s say we get the required file sorted out but blow the bank with available memory:
…we are able to catch the E_ERROR
fatal error in our shutdown handler too:
Bad Stuff Happend
But that is okay
Array
(
[type] => 1
[message] => Allowed memory size of 262144 bytes exhausted (tried to allocate 550001 bytes)
[file] => /home/michael/www/eggplant/public_html/samples/fatal/FileThatBlowsMemLimits.php
[line] => 9
)
That’s all pretty good, however there are some limits that need to be taken into consideration:
E_PARSE
errors on the include that builds out the shutdown structure will not be caught, as the interpreter can’t get beyond the parse error to put it into place.E_CORE_ERROR
errors? These are environmental errors caught when PHP itself is bootstrapping – pre-script interpretation. No dice.- When using output buffering in combination with the ob_gzhandler, take care when cleaning the buffer. Chances are the
Content-Encoding: gzip
header was already set and you will need to follow suit. - If your output buffer has already been flushed before hitting a fatal error and the shutdown function, your content to that point is already gone. Check ob_get_status to see if that is the case.
For an similar implementation using a shutdown function, check out eZ Components’ Execution class.