Handle a fatal error

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

try { define(Pi, 3); } catch (Exception $exception) { echo 'Not executed!'; } catch (Error $error) { 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 the set_error_handler:

$onError = function ($level, $message, $file, $line) { $message = trim($message); $code = null; throw new ErrorException($message, $code, $level, $file, $line); }; try { set_error_handler($onError); define(Pi, 3); } catch (Exception $exception) { echo 'Exception: ', $exception->getMessage(), "\n"; } catch (Error $error) { echo 'Error: ', $error->getMessage(), "\n"; } finally { restore_error_handler(); }

This lets us catch raw PHP errors! And the restore_error_handler returns everything to normal, so there won’t be any side-effects for the rest of our codebase.

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; } $message = trim($error['message']); $code = null; $level = $error['type']; $file = $error['file']; $line = $error['line']; $exception = new ErrorException($message, $code, $level, $file, $line); echo 'Error: ', $exception->getMessage(), "\n"; }; register_shutdown_function($onShutdown); error_reporting(0); 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.