⬆️ ⬇️

How to handle Fatal Error in PHP

In one of our projects (social genealogical network ), which I wrote about in this topic , we use a queue of deferred events implemented on a memkesh. Its architecture is as follows: the application writes to this queue various events and data related to them (event type, incoming parameters, and the handler function of this event). After that, the queue manager (s) parses this queue and executes the pending events. In particular, this queue is used to collect statistics, but also for other more critical tasks.

Therefore, it is very important to ensure high availability for the queue manager (s).



But since if the queue handler comes to us from outside, then 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 errors (Fatal Error), and this can be a problem ...





')

For tracking processes (daemons), it is very convenient to use process watchers such as monit , we use monit to monitor system demons. By the way, on Habré recently there was an article about the monta .

But it's not about him :-)



I asked one of the developers of my team to make a normal handler of a fatal error in the code of the queue manager, namely, the fork of the new instance of the handler and the error logging by event type. To this I received the answer that in php fatal errors cannot be processed in principle and it is shameful not to know about it and that: “computer science at the current stage of its evolutionary development does not yet have algorithms capable of solving the set task relying on the capabilities of php is correct ...”



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.



<?php



ini_set( "display_errors" , "on" );

error_reporting(E_ALL);

ini_set( 'html_errors' , 'on' );



function fatal_error_handler($buffer) {

if (preg_match( "|(Fatal error</b>:)(.+)(<br)|" , $buffer, $regs) ) {

//

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. "<br/>" ;



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 f-i fatal_error_handler

The remaining errors (all but fatal) are handled by the handle_error function.

If there are no errors, the code is executed normally :-)



Yes, it is also not the only means of high availability and fault tolerance.

We are trying to run a daemon every minute of the krone, and the daemon’s code starts with a function



<?php



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.



Open to all opinions and, if possible, I will respond to 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 a HTML error. Because from under the CLI, they were given without HTML tags, and the preg_match ("| (Fatal error:) (. +) (<br) |", $ buffer, $ regs) condition was simply not met. // This is how it is.



UUPD: I updated the code a bit, the point is that forca for a new process through the f-iy system, you need to take care to kill the process that forks the current one, because the handler function will wait for the result of executing the f-ii system, and it will not return, as we know it, because we are creating a daemon, in this regard, you will get a bunch of processes in memory that will eventually clog it completely.

Source: https://habr.com/ru/post/73589/



All Articles