Influence the exit code within a shutdown function - php

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.

Related

What does "i360: Error in global initialization" mean in PHP?

A PHP software I wrote has been working fine for years suddenly throws this error: i360: Error in global initialization
This error is being thrown from the callback function register_shutdown_function('my_shutdown');
The callback function is just something this:
function my_shutdown ()
{
chdir(getcwd());
$e = error_get_last();
if ($e)
trigger_error($e['message'].' on '.$e['file'].' ('.$e['line'].')', E_USER_ERROR);
}
The full error message that trigger_error throws is:
i360: Error in global initialization 1 on Unknown (0).
It doesn't give much clue. Any ideas what could be causing it?
Update 1:
If I comment out the entire my_shutdown() function, the script works fine but I am still intrigue as to why this error happened just today after years of working fine.
Update 2:
Tentative info: this appears to be related to Imunify360, a security software for web servers (which my host uses that I'm not aware of or have control over). Investigation ongoing.
This issue is caused by Imunify360 because of a recent update to include a feature called "Proactive Defense":
https://www.imunify360.com/blog/meet-imunify360-with-proactive-defense-the-sophisticated-protection-against-any-kind-of-malware-all-in-one-nice-package
To fix this you need to have your host disable the extension over all PHP versions:
sed -i "s/extension=i360.so/;extension=i360.so/g" /opt/alt/php*/etc/php.ini
That should fix the problem for the time being.
This error happened just today. It disappears when I delete my cron jobs on Hawk Host cpanel.
This error was confirmed to be coming from Imunify360 (a security software for web servers). At the time of this writing, the issue was already fixed on their end. I presume an update was applied to servers using it so if you're still getting this error, contact your host.

PHP call_user_func_array doesn't catch database error

I am currently building a database handler. Running on PHP 5.4.16 on top of a Firebird database.
When I have made a prepared statement, I need to execute the SQL with the arguments. I made this function:
private function assignParameters($args)
{
return call_user_func_array('ibase_execute',$args);
}
The problem is when I make an error in the args (not giving it the values that are expected), I expect the return to give false(error). ibase_execute normally returns false when an error has occurred.
But right now my PHP installation(XAMPP) simply crashes and restarts. I have tried using a try/catch without any luck. The call_user_func_array doesn't fail but I would expect it to return the result from the ibase_execute. I'm confused.
I should mention that there are no entries in PHP/Apache error logs. The only entry is that the Apache2 server have restarted.
The interbase configuration from phpinfo();
Firebird/InterBase Support dynamic
Compile-time Client Library Version Firebird API version 25
Run-time Client Library Version WI-V6.3.2.26539 Firebird 2.5
Any ideas - is this the driver crashing?
EDIT:
As requested from Mariuz here is what I got from the debug at crash time. The crash creates two dump report from the http process witch is running the PHP code.
They both contain a lot of information. To much to dump here but here's the excetions.
Dump 1
Exception Information
In httpd__PID__10168__Date__02_21_2014__Time_03_40_11PM__949__Second_Chance_Exception_C0000008.dmp the assembly instruction at 0x76ed12c7 which does not correspond to any known native module in the process has caused an unknown exception (0xc0000008) on thread 0
Dump 2
Exception Information
MSVCR80!UNWINDUPVEC+50In httpd__PID__10064__Date__02_21_2014__Time_03_40_05PM__321__Second_Chance_Exception_C0000005.dmp the assembly instruction at msvcr80!UnwindUpVec+50 in C:\Windows\winsxs\x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.6195_none_d09154e044272b9a\msvcr80.dll from Microsoft Corporation has caused an access violation exception (0xC0000005) when trying to read from memory location 0x00000000 on thread 5
This seems to be a segfault in php please provide the script and a backtrace to it
https://bugs.php.net/bugs-generating-backtrace-win32.php

Very occasional fatal PHP error, "couldn't execute" autoload function

I have no idea how to go about solving this very occasional funny state that PHP gets into. So any ideas whatsoever will be appreciated.
Maybe 3 or 4 times in the past year, my site will start returning 500 errors, and the logs show:
PHP Fatal error: Couldn't execute method dw__autoload in Unknown on line 0
dw__autoload is registered with spl_autoload_register. When I restart apache the problem is gone and I forget about it for several months. I am running APC, php 5.3.2-1ubuntu4.10 on ubuntu 10.04.3. I realize there is not much to go on here!
Following the source code from the point of this error, I'd risk (psychic debugging since you have no more details) to say this happens because the autoload is being called with already an exception thrown.
That said, I can't see exactly how this can happen. The current exception is cleared both before calling the autoloader and after calling each SPL autoloader function. I don't think we ever can get to a conclusion without a script that reproduces this problem. The full set of possibilities is:
The execution was already shut down
There's a current exception
The function is no longer valid (I see that happening only due to a bug in PHP, but anyway it's not likely because it almost certainly would generate a warning before)
The autoloader function requires a reference (again, this would also generate a warning before, and your autoloader would never work anyway)

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();
}
}

Logging fatal/parse errors in PHP5

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.

Categories