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:
error_reporting = (E_ALL & ~ E_ERROR)
or at runtime using error_reporting(E_ALL & ~ E_ERROR);
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();
}
The unwanted error information is due to the error logging configuration (log_errors
and error_log
), and is completely independent of the display_errors
configuration. The default php.ini configuration logs error details to stderr
when running scripts from the command-line. Under Apache, they show up in /etc/httpd/logs/error_log
.
Check out the php_error_cb
function in main.c
of the PHP source. In particular, line 1056 makes it clear that any messages of the format "PHP %s: %s in %s on line %d" are due to the error logging configuration (error information due to display_errors
will not have the "PHP: " prefix).
Couple of possible solutions, answering because the comments section was getting angry at me.
Edit #3 with solution:
Apparently E_ERROR and display_errors don't play nicely together
You could set error_reporting to E_ALL & ~E_ERROR, and just let your shutdown handler handle the fatal (since it is the last thing that should be called anyhow).
Additionally, E_ALL
does not encompass E_DEPRECATED
prior to PHP 5.4.0, so if you want to catch that too - use ~0 & ~E_ERROR
display_errors may be bugged
Here's a gist on how you can get it to spit out errors even though you told it not to: https://gist.github.com/1483028
If you set display_errors to 0 via ini_set() it will still display Fatal Errors
Although display_errors may be set at runtime (with ini_set()), it won't have any affect if the script has fatal errors. This is because the desired runtime action does not get executed.
Additionally you can send it to stderr as well, so that's awesome.
http://www.php.net/manual/en/errorfunc.configuration.php#ini.display-errors
Edit 2: Make sure you've changed the proper php.ini
Just thought of this, and commented as such, but there is more than one php.ini file. If you're doing this on the command line, you'll need to edit the cli one (/etc/php5/cli/php.ini on Ubuntu), and not the web one (/etc/php5/apache2/php.ini)
I'd guess you just need to set php.ini to have display_errors 0.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With