Nobody wants production code spewing potential security hole advertisements all over their pages, so it's generally considered good practice to define your own error handler--which can send you an email or an IM whenever something breaks--and turn PHP's error reporting functionality off entirely. PHP provides a very simple mechanism for accomplishing this, but unfortunately, it doesn't give you a hook to catch fatal errors caused by invalid code.
I've seen a lot of crazy people come up with a lot of crazy solutions to this problem, but most have been unimaginative and needlessly complicated and I have no idea why they proliferate so wildly when simpler solutions are so readily possible with output buffers and a bit of ini_set() trickery. Bits and pieces of this solution can be found sprinkled about in the efforts of many others, but none of them ever seem to put the parts together as well as they could. I'm sure someone else must have figured it all out somewhere, but I've never seen it, and neither have most PHP developers I've encountered, so I'm posting it up somewhere Google will index someday in the hopes that the resultant karma will eventually catch up with me and I'll have less messy code coming in for repair.
In order for this to work, you'll need to execute some startup code BEFORE anything else happens in your scripts, including any output buffering. This can be readily accomplished by adding the startup code to your auto_prepend_file as defined in php.ini. If you don't have an auto_prepend_file, make one. It's one of the most powerful and most overlooked features PHP has to offer.
You will absolutely take a measurable performance hit by running a preg_match over the output of every single page, but in my experience the difference is imperceptible under normal conditions. Still, you may want to define a global "off switch" to disable this functionality for scripts that don't need it, or for those times when you're debugging something and would prefer to see the default error spewage right there on the page.
I've seen a lot of crazy people come up with a lot of crazy solutions to this problem, but most have been unimaginative and needlessly complicated and I have no idea why they proliferate so wildly when simpler solutions are so readily possible with output buffers and a bit of ini_set() trickery. Bits and pieces of this solution can be found sprinkled about in the efforts of many others, but none of them ever seem to put the parts together as well as they could. I'm sure someone else must have figured it all out somewhere, but I've never seen it, and neither have most PHP developers I've encountered, so I'm posting it up somewhere Google will index someday in the hopes that the resultant karma will eventually catch up with me and I'll have less messy code coming in for repair.
In order for this to work, you'll need to execute some startup code BEFORE anything else happens in your scripts, including any output buffering. This can be readily accomplished by adding the startup code to your auto_prepend_file as defined in php.ini. If you don't have an auto_prepend_file, make one. It's one of the most powerful and most overlooked features PHP has to offer.
Code:
//This may seem counterintuitive since our goal is to
//hide errors from users, but our output buffer callback will
//need to see everything in order to catch the bad stuff.
ini_set('display_errors', 'On');
//Use the normal mechanism for catching non-fatal errors.
//This process is very well documented, so the definition
//of this error handler function's body is left as an
//exercise for the reader.
set_error_handler('regular_error_handler');
//Define some bogus, invalid HTML tags that no sane
//person would ever put in an actual document and tell
//PHP to delimit fatal error warnings with them.
ini_set('error_prepend_string', '<phpfatalerror>');
ini_set('error_append_string', '</phpfatalerror>');
//Start an output buffer with a filter callback
ob_start('fatal_error_handler');
//Skim the buffer for our bogus HTML tags. If we find any
//then we know a fatal error has occurred and should react accordingly.
//It doesn't matter how the error string is formatted or if our page
//contains similar looking text because we're looking for our own
//funky tags exclusively; false positives are highly improbable.
function fatal_error_handler($bufferContents)
{
$output = $bufferContents;
$matches = array();
$errors = "";
if ( preg_match('|<phpfatalerror>.*</phpfatalerror>|s', $output, &$matches) )
{
foreach ($matches as $match)
{
$errors .= strip_tags($match) . "\n\n---\n\n";
}
//Do your error logging/emailing/AIMing here
//and overwrite $output so errors are not displayed.
}
return $output;
}
You will absolutely take a measurable performance hit by running a preg_match over the output of every single page, but in my experience the difference is imperceptible under normal conditions. Still, you may want to define a global "off switch" to disable this functionality for scripts that don't need it, or for those times when you're debugging something and would prefer to see the default error spewage right there on the page.