In modern web applications knowing immediately when something is going wrong is vital in order to apologize to clients where necessary as well as fixing any problems.
Personally I find notifications via email the easiest (with backup via error log files), however many people choose to use exception tracking services such as Airbrake (previously Hoptoad), or Lighthouse.
I find Gmail combined with carefully written subjects is very handy as each exception is grouped along with similar ones.
Unfortunately with PHP its not always that simple, as simply detecting the issue in the first place can be quite a pain.
In this article I hope to explain how to catch exceptions that wouldn’t usually be available for your application to report. (Note this is not perfect and you should always be checking your error log files as a last resort – as well as checking errors are written to them).
Three different ways of picking up errors in PHP…
Step 1. set_error_handler
Out of the many design flaws present in PHP, the use of two separate error reporting mechanisms is rather frustrating however can be powerful at times.
Thankfully if you only wish to deal with Exceptions (the standard in most other languages) it is quite easy to convert logged errors to exceptions with the following simple code:
<?php set_error_handler(function($errno, $errstr, $errfile, $errline ) { throw new ErrorException($errstr, 0, $errno, $errfile, $errline); });
This simple mechanism will cause all errors to be thrown as exceptions. Note that this will also disable the silence operator which is probably a good thing. It does this by completely ignoring the current error_reporting level, which you can obviously check if you wish and act accordingly.
Step 2. set_exception_handler
The second thing you should do is set an exception handler. This is the method that is called when an exception is not caught at all. It may be preferable to simply wrap your entire code in a single try{} catch{} block and whichever you choose to do is completely up to you.
Inside the exception handler you need to be very careful not to raise another exception as this will often confuse your error handing (rather than receiving reports and/or error logs from the application you will only see the error raised inside the handler).
I recommend having a handler that sends yourself the details of the exception, including a backtrace and notifies the user. If the email failed to send or raised another exception, it can be useful to simply ask the end-user to send an email to support as a final backup.
The actual contents of one of these exception handlers deserves a blog post on its own, and I’ll try write a better one up here as soon as possible. I’ve kept the actual handler as a separate method so that it may be accessed by the shutdown function explained below.
function exception_handler($e) { /* Send an email */ /* Tell the user something went wrong (dont include ->getMessage() except to developers) */ print 'Something went wrong: '.$e->getMessage()."n"; exit(1); } set_exception_handler('exception_handler');
Step 3. register_shutdown_function
The above two methods are suggested practice, however this one exploits a rather strange behavior in PHP. Fatal errors such as calling an undefined function or including a file with a syntax error are not sent to the error handler, however they are recorded and the “shutdown function” is called afterwards.
A simple trick to email yourself (and display a neat message) when these errors happen involves implementing a shutdown function in which you call ‘error_get_last()’ to determine if anything went wrong.
Note that you can’t throw exceptions from inside a shutdown function, so I recommend simply calling your exception handler designed in the previous step directly.
register_shutdown_function(function() { if ($error = error_get_last()) { exception_handler( new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']) ); } });
Step 4. Turn display_errors off
By default some installations have the ‘display_errors’ ini configuration turned on by default. PHP includes quite a lot of information in these dumps and it is highly discouraged to leave this enabled on a server that may be accessed publicly.
The dumps are very useful and I recommend writing your own with better back-traces and explanations to help out developers. You’ll need to ensure that only developers see this, but the easiest way to achieve that is with a secret cookie (read “password”) that is only set on their machines.