I must be missing something in my understanding of handling PHP errors, specifically suppressing their output. When a fatal error occurs, I expect my shutdown handler function to process it gracefully and terminate script execution. This works as expected. However, I can't seem to prevent PHP from outputting the information about the fatal error.
My php.ini file contains the following directives:
error_reporting = E_ALL | E_STRICT
display_errors = Off
I set error_reporting
to report everything and I use a custom error handler to throw exceptions. My expectation is that display_errors = Off
will prevent ANY error messages from being displayed.
Anyway, when a fatal error occurs the custom error handler is bypassed (because script execution stops immediately) and the shutdown handler executes.
Now, on to my simplified code:
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 'Off');
function shutdown_handler()
{
$err = error_get_last();
$fatal = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR);
if ($err && in_array($err['type'], $fatal)) {
echo "\n\ntest fatal error output\n\n";
}
exit();
}
register_shutdown_function('shutdown_handler');
To test it I generate an "Allowed memory size exhausted" fatal error like so:
// max out available memory
$data = '';
while(true) {
$data .= str_repeat('#', PHP_INT_MAX);
}
Because i have display_errors = Off
I expect this to only produce the following output (as per the shutdown handler):
test fatal error output
But instead I continue to receive:
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 2147483648 bytes) in /home/daniel/mydev/php/test0.php on line 24
PHP Stack trace:
PHP 1. {main}() /home/daniel/mydev/php/test0.php:0
PHP 2. str_repeat() /home/daniel/mydev/php/test0.php:24
test fatal error output
What am I missing that will prevent this error trace from outputting?
CONCLUSION
It appears, as @Cthos sagely noted, that "E_ERROR and display_errors don't play nicely together."
This is also the case for E_PARSE (and I assume E_CORE_ERROR/E_COMPILE_ERROR, but I didn't break my PHP installation to test it). I suppose it makes sense that PHP would force the error traceback into STDOUT in these cases because if it didn't you might never know if/why things were going wrong.
So a solution in this case would be:
- As @Cthos suggested, silence E_ERROR notifications in php.ini:
error_reporting = (E_ALL & ~ E_ERROR)
or at runtime usingerror_reporting(E_ALL & ~ E_ERROR);
- Update the shutdown handler to check if the last error was of type E_ERROR and perform the appropriate actions if so.
As for the other fatals like E_PARSE, E_CORE_ERROR, etc. you just have to make sure your code is correct and that your PHP works. If you try to silence E_PARSE errors and handle them in your shutdown function it won't work because parse errors prevent PHP from ever getting that far.
So, an updated/working shutdown handler looks like this:
error_reporting(E_ALL & ~ E_ERROR);
function shutdown_handler()
{
$err = error_get_last();
if ($err && $err['type'] == E_ERROR) {
$msg = 'PHP Fatal Error: '.$err['message'].' in '.$err['file'].
' on line '.$err['line'];
echo $msg, PHP_EOL;
}
exit();
}