PHP will (as of version 8) happily send a 200 response when there's a fatal error like, for example, a syntax error on an autoloaded class, even if you define an error handler using set_error_handler
that will output a different HTTP status code.
The problem is, set_error_handler
doesn't work on all error types. A workaround is adding a shutdown function using register_shutdown_function
that checks the last error's type.
* This function catches some errors, but not all of them.
*/
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
header('HTTP/1.1 500 Internal Server Error', TRUE, 500);
});
/**
* This one is needed to catch fatal errors and set the HTTP status code appropriately.
*/
register_shutdown_function(function () {
$last_error = error_get_last();
if ($last_error && in_array($last_error['type'], [E_ERROR, E_PARSE, E_COMPILE_ERROR, E_CORE_ERROR]))
header('HTTP/1.1 500 Terrible Internal Server Error', TRUE, 500);
});
/**
* Without this, in the "terrible" case we'll have already sent output so we won't be able to set the header. The output
* sent is the "Fatal error" message. It's left as an exercise for the reader to figure out how to hide that message.
*/
ob_start();
if (isset($_GET['bad']))
$a = new BadClass(); // Constructor triggers a user error
if (isset($_GET['terrible']))
$a = new TerribleClass(); // Constructor triggers a fatal error
You'd think PHP, a language that primarily deals with web stuff, would do this automatically! 200 responses on fatal errors can lead to catastrophic problems that you won't notice until it's way too late because all the server logs will think things have been going fine for, in my case, upwards of a month.