I finally had some time to study your answers and to read that excellent article.
To pinpoint the situation: we're in production and with an unchanged index.php, this means display errors is disabled. Next, a fatal error, exception or PHP parse error happens, something we're not catching/handling in our own code. What happens?
CodeIgniter registers all of these:
set_error_handler('_error_handler');
set_exception_handler('_exception_handler');
register_shutdown_function('_shutdown_handler');
Therefore, it is in control of such unexpected errors, not Apache.
In all the error examples I gave, it will ultimately call "_error_handler" from Common.php, and here we see the heart of the matter:
// Should we display the error?
if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors')))
{
$_error->show_php_error($severity, $message, $filepath, $line);
}
exit(1); // EXIT_ERROR
As we are in production, the only thing CI will do in this case is to just exit the process. It will NOT show anything to the user.
I consider this an oversight of the framework. You always want to show something to the user. In production, you should not show the error itself or its stack trace, yet you will definitely want some generic branded error page.
CodeIgniter does not take this responsibility. Either it lets the web server handle it, or if it doesn't, just let the browser show a generic "crash" page, which is awful. Apache error documents do not work for all types of errors, they're sensitive and relying on configuration makes the application less portable. That's why I consider this a gap in the framework.
CodeIgniter is wrong to conclude that when display errors is disabled, it means the user should see nothing at all. You will always want to show something.
As unfortunately this behavior is defined in Common.php, I had to do this override there:
if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors')))
{
$_error->show_exception($exception);
} else {
$_error->show_exception_fallback($exception);
}
In the "else" situation, we're in production with display errors off, and I'm calling a custom "show_exception_fallback" method, which is my own method defined in MY_Exceptions.php. That method shows a generic branded page to the user.