[PHP] How to handle a fatal error

Fatal errors are seemingly impossible to catch. A try-catch block can handle an Throwable, but a raw PHP notice, warning, or error will slip right through:

try { define(Pi, 3); } catch (Throwable $throwable) { echo 'Not executed!'; }

The raw PHP errors aren’t thrown objects, so they can’t be caught in a try-catch block. But they can be turned into exceptions using set_error_handler:

$onError = function ($level, $message, $file, $line) { throw new ErrorException($message, 0, $level, $file, $line); }; try { set_error_handler($onError); define(Pi, 3); } catch (Throwable $throwable) { echo 'Throwable: ', $throwable->getMessage(), "\n"; } finally { restore_error_handler(); }

This lets us catch raw PHP errors! The restore_error_handler returns everything to normal, which is critical for libraries that shouldn’t make global changes to exception handling that would affect the rest of the script.

This lets us catch raw PHP errors. But, unfortunately, only the mildest of errors. The more severe errors cannot be caught this way (E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and many E_STRICT errors).

The trouble is that PHP shuts down when it encounters a fatal error, so our try-catch block won’t even run. But there is one thing that will run just before PHP shuts down: a shutdown function.

$onShutdown = function () { $error = error_get_last(); if ($error === null) { return; } $exception = new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']); echo 'Error: ', $exception->getMessage(), "\n"; }; register_shutdown_function($onShutdown); error_reporting(0); // Fatal error require '';

This lets us handle even a fatal error!

But there is a little more we can do. We can define a global exception handler, so we can catch any thrown object that makes it up to the global scope unexpectedly:

$onException = function ($exception) { echo "Exception: ", $exception->getMessage(), "\n"; }; set_exception_handler($onException); throw new Exception('Armageddon');

At this point, we’re feeling pretty safe:

But there are still a few fatal errors that we cannot catch! For example, a compile-time syntax error or a run-time maximum-function-nesting error can’t be caught from within the PHP script itself. To catch those types of errors, we also need to be monitoring the exit code and the STDERR of the script.