Logging fatal/parse errors in PHP5 - php

I'm writing an error logging service that will be integrated into websites running on my server, that will email me error batches, etc.
So I've been trying to find out if there's a way to handle fatal and parse errors, however not using the tricks to handle it in PHP code (output buffer, shutdown function). I'm quite happy to write some C code or something to handle it outside of my PHP code. I would also like to issue a redirect if possible (my sites use output buffering so there shouldn't be any headers sent).
I'm pretty sure this could be done with a PHP module, but I've never written one and have no idea where to start.

There is no way to catch a fatal or parse error in PHP. But..
In 5.2, they added error_get_last(). You can call it inside a shutdown function and perform logging. An untested 5.3 example for firing off a mail when there was a fatal error:
<?php
register_shutdown_function(function(){
$err = error_get_last();
if(is_array($err) && array_key_exists('type', $err) $err['type'] > 0
&& ($err['type'] == E_ERROR || $err['type'] == E_PARSE) {
error_log("Oh noes, a fatal: " . var_export($err, true), 1, 'fatals#example.com');
}
});
(You'll need to use a callback if you aren't on 5.3 and can't do anonymous functions.)
Unfortunately because this is handled in a shutdown function, chances are that headers have already been emitted and you might not be able to provide anything useful to the user. This depends on the rest of the application, though, so it might work out for you. Try it and find out!

By default all errors are passed to web server error log but you can change it in php.ini by specifying path to your own file via error_log setting. So what is left to do is to write some separate script/app to parse / send data / truncate log file every day / whatever and run it as cron job.

Related

Influence the exit code within a shutdown function

I am using a library which connects to a remote service. The library will generate a PHP Fatal error / Uncaught ErrorException in case the connection terminates unexpectedly.
My PHP script is running as a daemon (via Systemd), so I would like to automatically restart the daemon after a while to reconnect. All this is setup in Systemd, so if PHP exits with the status 4, the daemon will be restarted after some time.
PHP uses the exit code 255 by default for fatal errors, so I resolved to using a shutdown function similar to the following:
function shutdown() {
// Do a bit of this and that (e.g. assert
// there actually was the relevant error)
exit(4);
}
register_shutdown_function('shutdown');
trigger_error('Test', E_USER_ERROR);
But whatever I try, I cannot influence the exit code in any way. I have not found anything helpful in the documentation.
Is there any known way to manipulate the exit code within a shutdown function or by other means after the fatal error has already been generated?
This is a bug in PHP, which apparently has been filed multiple times over the years:
https://bugs.php.net/bug.php?id=65275
https://bugs.php.net/bug.php?id=62725
https://bugs.php.net/bug.php?id=62294
https://bugs.php.net/bug.php?id=23509
It is supposed to be fixed already, but as of PHP 5.5.30, I am still experiencing it myself.
I suppose you could try reverting to PHP 5.3, which was reportedly working as expected. Or you could check out one of the most recent releases (5.6.18 or 7.0.3 as of this answer), to see if the bug is indeed fixed.

PHP task terminated but no error condition raised

A PHP script is using the ZipArchive class and is potentially long running.
Since it is dying silently but writing a partial zip file, I wrapped error_log() statements around the $zip->close(). (ini_set sets error logging to a file and E_ALL just before this code)
error_log("calling zip->close()");
$rc = $zip->close();
error_log("zip->close() returned $rc");
The logging file shows the first error_log, but never the second.
A unix top command shows the process running for a total CPU time of c. 2.5 minutes before it goes defunct.
I have also tried trapping the error with set_error_handler(), and the handler using error_log() to record the catch. But nothing shows up in the log file.
I'm assuming the process is being bounced, maybe by Apache (I have no control over Apache or PHP).
My question is: why can't I see this error in the file being used by error_log?
Thanks for the suggestions for circumventing the problem, but my question remains:
Why can't I see this error in the log? Or why can't I catch this error via set_error_handler()?
set_time_limit(0);
0 = Indefinitely

How do I track down an "Exception thrown without a stack frame in Unknown on line 0" in PHP?

I'm working on a large (inherited) codebase in PHP, and the error Exception thrown without a stack frame in Unknown on line 0 has started showing up at the bottom of every page. I understand what the error means: an exception is getting thrown someplace it can't be thrown. I've even managed to track it down somewhat—it's happening during the time shutdown functions are being called.
I've put logging in all the functions which get registered with register_shutdown_function, and it's not happening in any of those. Unfortunately, I can't seem to get any more information than that; I know what the last shutdown function to get called successfully is, but I have no idea what code gets executed between that and the point where the error happens. I don't even know what part of the PHP machinery is calling that last shutdown function. It might be something with the logging framework, or the session framework, or anything of a half-dozen things.
Does anyone know how to pinpoint where the error is occurring?
This can happen in destructors and exception handlers which don't have a stack frame. But since the message is so very helpful, your only option is to try to use echo to find the bug (and maybe ob_end_flush()). It may be that a destructor is throwing an exception, or is calling a function that throws an exception. Once you've located the buggy function, add a try...catch around the exception throwing part.
Note that if your framework uses its own error handling, you have to turn off warnings and notices in the PHP configuration. Especially if you have something like ErrorException, since it turns warnings into exceptions.
This message appears when an exception in thrown within your exception handler or your error handler (and maybe also in shutdown functions)
You should look for theses methods see if nothing strange appens in here.
Just found your question after experiencing the same error in a web application deployed to a Ubuntu 11.04 server, running PHP 5.3.5. I agree to #Eisberg that this issue seems to be an issue with the PHP 5.3-version exclusively, as the error haven't been present with previous, other PHP versions on other environments, where my application have been deployed to.
As #jmz mentions, I have also utilized an error handler that turns errors into exceptions for easier debugging at my staging servers.
To figure out what caused this mysterious behaviour, I debugged the application using XDEBUG & my IDE (Eclipse) and found out that one of my libraries tried to access & modify the global$_SESSION variable, when no session-data were set. Wrapping my code in an if-statement checking isset($_SESSION) made the issue disappear.
Why the exception didn't bubble up completely all the way to the browser, as other errors have done when trying to access non-set variables, is a complete mystery for me, especially as I got below error settings set, but maybe altering the setting in error_reporting() would have made a difference.
Error handling settings, for reference:
error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", 1);
ini_set("html_errors", 1);
I get the same error msg too. MySQL database quota was set by awardspace as 50mb. Using PHPmyAdmin to optimise files showed a size of 34 mb but at cPanel the size was shown as 57mb. Every time when I visit my website and this error msg occurred, all I need to do was to login into awardspace, choose database, management, and reset file permissions. Then the error msg goes away, and my website is back up.
I was getting this issue in PHPUnit. I have added below code in function tearDown and helps me to get the actual error. You should wrap destructor inside a try-catch block, as the stack-frame only gets lost as soon as the exception is getting outside the destructor. Might be this can help someone else too. Source
function __destruct()
{
try
{
/*
your code
*/
}
catch(Exception $e)
{
echo $e->__toString();
}
}

How to let Apache send 500 in case of PHP error?

I'm developing a PHP application that uses HTTP response codes for communication as well as response bodies. So this is a typical scenario in the PHP code:
try {
doSomething();
}
catch (Exception $e) {
header('HTTP/1.1 500 Internal Server Error');
}
... and the typical code in clients looks like:
switch(responseCode) {
case 200 :
// all fine
// ....
case 500 :
// trouble!
}
This is cool, as long as every error is well caught in PHP code.
The problem: If, for any reason in the php code occures an uncaught error or an uncatchable error like syntax errors, Apache will send 200 OK. But I wan't apache to say 500 Internal Server Error. Maybe per .htaccess or so.
You can, of course, write your own error handler. However, not all PHP errors are catchable. For instance, a syntax error won't even allow your code to run, including your error handler.
To handle catchable errors, you can use the auto_append_file and auto_prepend_file directives to place your error handling code code.
Non-catchable errors are a different issue. If PHP runs as Fast CGI, it will automatically generate a 500 status code for you. However, you're probably running PHP through some other SAPI (such as Apache module). No idea about that, sorry. (I'll report back if I find something.)
Response headers are not sent until PHP echoes the first byte of response body. You can change headers (and the status code) in the mean time. Keeping that in mind, here is a solution:
Set your script to send a 500 response code at the beginning of script and 200 at the end. Here is an example:
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error');
if (rand(0, 1) == 1) {
die("Script terminated prematurely");
}
header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK');
echo "Success";
Just ensure that 200 response code is set at only one location in your code.
Note: you can use http_response_code (PHP >= 5.4) instead of header.
Nowadays it's no more a problem - since 5.3 PHP learned at last to send 503 on error, not 200
Err, it seems it was 5.2.4:
Changed error handler to send HTTP 500 instead of blank page on PHP errors.
You need to set display_errors = off to make it work
I have the exact behavior on my windows Apache 2.4 with PHP 5.4.5

Dirty errors with PHP popen()

I call a file with popen():
$fp = #popen("/usr/bin/openssl ...");
the specific details don't matter, but what happens here is that when I run the command I receive these errors on a Windows CMD:
The system cannot find message text
for message number 0x3 in the message
file for System.
I know that the file don't exist on Windows, but why doesn't # silence the errors? My library just tries a few different ways to achieve the same thing.
This is not a error message issued by PHP, so PHP's error suppression won't apply.
You'll have to check whether you can run the operation beforehand or suppress the output of system errors.
For suppressing, 2>NUL should work, as you say. Wouldn't it be better though to test whether the file exists at all? On a Windows machine, your executable is most probably not located in /usr/bin/openssl.
if (file_exists("/usr/bin/...."))
popen(".....");
Here's the answer I found:
I just wrote " 2>NUL" in the end of the CMD call to make all Windows errors to disappear.

Categories