
How to handle Fatal Error in PHP
In one of our projects (social genealogy network ), which I wrote about in this topic , we use the queue of pending events implemented on the memkesh. Its architecture is as follows: the application writes to this queue various events and data related to them (type of event, incoming parameters, and function handler of this event). After that, the queue manager (s) parse this queue and execute pending events. In particular, such a queue is used to collect statistics, but also for other tasks more critical to the execution.
Therefore, it is important to ensure high availability for the queue manager (s).
But since If the queue handler comes to us from outside, we are not responsible for the quality of this event handler, i.e. if the handler suddenly throws an error, then we need to process it and continue the work of the queue manager. But sometimes it happens that handlers throw Fatal Error, and this can become a problem ...
For tracking processes (daemons), it is very convenient to use process observers such as monit , we use monit to monitor system daemons. By the way, on a habr there was recently an article about monit .
But it's not about him :-)
I asked one of the developers of my team to make a normal fatal error handler in the queue manager code, namely a fork of the new handler instance and logging the error by event type. To this I received the answer that in php it is impossible to handle fatal errors in principle and it is shameful not to know about it and that: “computer sciences at the current stage of their evolutionary development still do not have algorithms capable of solving the task based on the capabilities of php correctly ..."
After that, I wrote such code that handles fatal, as well as all other errors in the php application. If he helps someone else, then I will only be glad.
A little explanation on the current code.
Fatal Error - we catch through output buffering in the fatal_error_handler file
The remaining errors (all but fatal) are handled by the handle_error file.
If there are no errors, the code runs fine :-)
Yes, this is also not the only means of high availability and fault tolerance.
We try to start the daemon every minute on the crown, and the daemon code starts with
those. if the daemon is running, then we stop execution.
I am open to all opinions and if possible I will answer all comments.
UPD: pay attention to ini_set ('html_errors', 'on'); I spent half an hour without understanding why the handler does not work from under the CLI. It was just about HTML errors. Because from under the CLI they were given without HTML tags, and the preg_match ("| (Fatal error:) (. +) (
UUPD: I updated the code a bit, the fact is that a forked new process through f-ju system needs to be taken care of to kill the process that forked the current one, because the handler function will wait for the result of the system function execution, but it will not return for certain, because we are creating a daemon, in connection with this you will get a bunch of processes hanging in memory, which will eventually clog it completely.
Therefore, it is important to ensure high availability for the queue manager (s).
But since If the queue handler comes to us from outside, we are not responsible for the quality of this event handler, i.e. if the handler suddenly throws an error, then we need to process it and continue the work of the queue manager. But sometimes it happens that handlers throw Fatal Error, and this can become a problem ...
For tracking processes (daemons), it is very convenient to use process observers such as monit , we use monit to monitor system daemons. By the way, on a habr there was recently an article about monit .
But it's not about him :-)
I asked one of the developers of my team to make a normal fatal error handler in the queue manager code, namely a fork of the new handler instance and logging the error by event type. To this I received the answer that in php it is impossible to handle fatal errors in principle and it is shameful not to know about it and that: “computer sciences at the current stage of their evolutionary development still do not have algorithms capable of solving the task based on the capabilities of php correctly ..."
After that, I wrote such code that handles fatal, as well as all other errors in the php application. If he helps someone else, then I will only be glad.
ini_set("display_errors", "on");
error_reporting(E_ALL);
ini_set('html_errors', 'on');
function fatal_error_handler($buffer) {
if (preg_match("|(Fatal error:)(.+)(
//Форкаем новый инстанс демона и готовимся к заавершению выполнения текущего скрипта
file_put_contents("php://stderr", "before fork (pid: " . getmypid() . ")\n");
system("php tester.php " . getmypid() . " &" );
return "ERROR CAUGHT, check log file" ;
}
return $buffer;
}
function handle_error ($errno, $errstr, $errfile, $errline)
{
if($errno & E_ALL){
// Логирование ошибки как в ф-ии выше
//switch в котором, собственно обрабатываем ошибку
switch ($errno) {
case E_USER_ERROR:
case E_USER_WARNING:
case E_USER_NOTICE:
default:
//do something
break;
}
ob_end_clean();
echo "CAUGHT OTHER THAN FATAL ERRORS!!! " . $errstr;
exit;
}
}
//code between ob_start and ob_end_flush is included by MQ Handler, so we know nothing about it, and this code could fire a Fatal Error
if(isset($_SERVER["argv"][1])){
file_put_contents("php://stderr", "kill {$_SERVER['argv'][1]}: ".var_export(posix_kill($_SERVER['argv'][1], 15), true)."\n");
}
ob_start("fatal_error_handler");
set_error_handler("handle_error");
while(true) {
//Just a Warning
//$a = 9/0;
sleep(10);
file_put_contents("php://stderr", "live\n");
//Fatal error - вызов необъявленной ф-ии
if(rand(1,10) % 2 == 1) {
ololo(123);
}
}
/*
Код без ошибок
*/
$a = rand(1,10);
echo $a."
";
ob_end_flush();
echo "Program still executing....";
?>
* This source code was highlighted with Source Code Highlighter.
A little explanation on the current code.
Fatal Error - we catch through output buffering in the fatal_error_handler file
The remaining errors (all but fatal) are handled by the handle_error file.
If there are no errors, the code runs fine :-)
Yes, this is also not the only means of high availability and fault tolerance.
We try to start the daemon every minute on the crown, and the daemon code starts with
if (!checkSingleProcess()) {
exit;
}
function checkSingleProcess() {
$res = exec('ps aux | grep mq_manager.php | grep -v grep | grep -v '.getmypid(), $output, $return);
return $output == array();
}
* This source code was highlighted with Source Code Highlighter.
those. if the daemon is running, then we stop execution.
I am open to all opinions and if possible I will answer all comments.
UPD: pay attention to ini_set ('html_errors', 'on'); I spent half an hour without understanding why the handler does not work from under the CLI. It was just about HTML errors. Because from under the CLI they were given without HTML tags, and the preg_match ("| (Fatal error:) (. +) (
UUPD: I updated the code a bit, the fact is that a forked new process through f-ju system needs to be taken care of to kill the process that forked the current one, because the handler function will wait for the result of the system function execution, but it will not return for certain, because we are creating a daemon, in connection with this you will get a bunch of processes hanging in memory, which will eventually clog it completely.