Handle Fatal Errors in PHP Using Register_Shutdown_Function()

Handle fatal errors in PHP using register_shutdown_function()

This works for me:

function shutdown() {
$error = error_get_last();
if ($error['type'] === E_ERROR) {
// fatal error has occured
}
}

register_shutdown_function('shutdown');

spl_autoload_register('foo');
// throws a LogicException which is not caught, so triggers a E_ERROR

However, you probably know it already, but just to make sure: you can't recover from a E_ERROR in any way.

As for the backtrace, you can't... :( In most cases of a fatal error, especially Undefined function errors, you don't really need it. Pinpointing the file/line where it occured is enough. The backtrace is irrelevant in that case.

Can register_shutdown_function() and set_error_handler() catch the same error?

The shutdown function will be executed when the script execution is finished whether there is an error, exception or not. It has nothing to do with errors or exceptions, errors or exceptions don't trigger it , and it does not catch them, it will be called anyway at the end of the script, so it is useful if you want to do some work even if an exception or fatal error happened because error handler function does not get executed if Fatal error or exception happened.

The error handler function will be executed when error is triggered. This is quoted from the manual

The following error types cannot be handled with a user defined
function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING,
E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the
file where set_error_handler() is called.

<?php

function shutdownFunction(){
echo "shutdownFunction is called \n";
}

function errorHandlerFunction(){
echo "errorHandlerFunction is called \n";
}
register_shutdown_function('shutdownFunction');
set_error_handler('errorHandlerFunction');

//echo "foo\n"; // scenario 1 no errors
//echo $undefinedVar; //scenario 2 error is triggered
//undefinedFunction(); //scenario 3 Fatal error is triggered
//throw new \Exception(); //scenario 4 exception is thrown

scenario 1 (no errors) outputs

foo 
shutdownFunction is called

scenario 2(error is triggered) outputs

errorHandlerFunction is called 
shutdownFunction is called

scenario 3 (Fatal error is triggered) outputs

Fatal error: Call to undefined function undefinedFunction() in /tmp/execpad-b2a446c7f6a6/source-b2a446c7f6a6 on line 15
shutdownFunction is called

scenario 4 (exception is thrown) outputs

Fatal error: Uncaught exception 'Exception' in /tmp/execpad-0b3a18f0ea06/source-0b3a18f0ea06:16
Stack trace:
#0 {main}
thrown in /tmp/execpad-0b3a18f0ea06/source-0b3a18f0ea06 on line 16
shutdownFunction is called

see for yourself https://eval.in/1073642

How to Handle Parse error using register_shutdown_function() in php?

Parse errors can only be caught if they occur in scripts included or required. (also see https://stackoverflow.com/a/1900272/2123530)

So, sorry, this won't work the way you did it but can work this way :

<?php
register_shutdown_function('ShutDown');

include 'include.php';

function catchError($errno, $errstr, $errfile = '', $errline = ''){

echo "Eroor Type : " .$errno. "<br>";
echo "Eroor Message : " . $errstr . "<br>";
echo "Line Number : " . $errline;
exit();
}
function ShutDown(){
$lasterror = error_get_last();
if(in_array($lasterror['type'],Array( E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR, E_CORE_WARNING, E_COMPILE_WARNING, E_PARSE))){
catchError($lasterror['type'],$lasterror['message'],$lasterror['file'],$lasterror['line']);
}
}
?>

Content of include.php

<?php 
echo "Hi" // generate error == Parse error: syntax error, unexpected 'echo' (T_ECHO), expecting ',' or ';' in file_naem on line 5;
echo "Hello";
?>

Fatal error catched by register_shutdown_function and update json_encode

Why move one array to another array and then echo the second array.

Why not just do this

function shutdown(){
$error = error_get_last();
echo json_encode($error);
}

Or even this

function shutdown(){
echo json_encode(error_get_last());
}

Apart form the use of an unnecessary array, this will give you all the information available from get_last_error()

Of course it could be that the error_get_last() information is just not available at this late stage in the shutdown process. If this is the case then you can pass extra parameters to the shutdown function and this may be what you need to do.

register_shutdown_function('shutdown', get_last_error());

and

function shutdown($last_error){
echo json_encode($last_error);
}

EDITED:

First I have added error_reporting(~E_ERROR); // don't report fatal errors

The first one worked for me I mean

error_reporting(~E_ERROR); // don't report fatal 

register_shutdown_function('shutdown');

function shutdown(){
$error = error_get_last();

if ($error['type'] === 1){
echo json_encode($error);
}

For my needs I wrote:

function shutdown(){
$error = error_get_last();

if ($error['type'] === 1){ // 1 means fatal error
$res['message'] = $error['message'];

$res['success'] = true;

header('Content-Type: application/json');

echo json_encode($res);
}
}

How do I handle parse and fatal errors?

If it can't parse your script, it won't be able to parse your custom error handler.

You should have display_errors off in your php.ini and also set error_reporting to none when your site is in production.

Also, I believe set_error_handler() can handle fatal errors.

PHP Pass Variables to register_shutdown_function - Fatal Error Handler

See the docs :

Working directory of the script can change inside the shutdown
function under some web servers, e.g. Apache.

So be sure to use an absolute path when doing this, i.e

file_put_contents('<absolute-path-to>/resultLogs/log.txt', $write, FILE_APPEND);

As you wrote "All my logs are saved in a different folder than the .php file", and I guess that your problem is, that you are having the $file constant relative to that .php file but the directory changes upon error..

Why does fopen fail within a register shutdown function?

Are you using a relative path to your custom error log? If so, this note on the register_shutdown_function page might be relevant:

Working directory of the script can change inside the shutdown function under some web servers, e.g. Apache.

In fact, the second comment says:

If you want to do something with files in function, that registered in register_shutdown_function(), use ABSOLUTE paths to files instead of relative. Because when script processing is complete current working directory chages to ServerRoot (see httpd.conf)

Other things I'll mention:

Your variable $error, and the logic if ($error) { ... is misleading. See, when you register a function using register_shutdown_function, you are telling PHP to invoke that function every single time your script finishes executing - regardless of whether or not there was an error. This means that fatalHandler is invoked even when your script finishes with a non-fatal error, or even no error at all!

Since you have an alternative method for dealing with non-fatal errors (using set_error_handler), you should specifically check for fatal errors in fatalHandler:

function fatalHandler() {
if( $error !== NULL && $error['type'] == E_ERROR) {
errorHandler($error["type"], $error["message"], $error["file"], $error["line"]);
header("HTTP/1.1 500 Internal Server Error");
exit();
}
}

See the list of PHP error levels here.

You may not be aware of this, but PHP has a built-in error-logging function. By default, it writes to the file specified for error_log in your php.ini.

If you are interested in more advanced logging, and generally taking your PHP-fu to the next level, I suggest looking into the Monolog package. It is more or less considered the universal standard for logging in professional PHP communities.



Related Topics



Leave a reply



Submit