What To Do When It All Goes Wrong: Using the Try/Catch Structure in Drupal Coding
One of the great things about PHP is that it’s so simple, anyone with a text editor and a book can start programming. In fact, I’m convinced that Open Source’s most successful projects grew precisely because PHP’s relatively loose coding structure let the average Joe skip the details and dive right in.
While this is great for introducing newcomers to programming, it leads to code that’s simultaneously sloppy, but still manages to work. That can be a problem because it tends to give programmers a false sense of security: Hey, it works! What could go wrong?
Of course, if you’ve ever had to debug a script and found that the cause was some completely unexpected user error or the return of an empty database set, you know that there’s no such thing as fail-proof code.
One of the greatest tricks you can have in your programming arsenal is a coherent approach to error handling so you can stop execution of bad code before it ever starts. Let’s take a look at one of PHP’s little-used, techniques: The try/catch structure for error handling.
The try/catch structure is a bit like an if/then statement. It allows you to “try” to do something, and if that doesn’t work, “catch” the error and continue doing something else. Usually during the Catch, you’ll either display or log an error message you’ve defined, or has been defined for you by other code (usually a class).
You don’t see this used very often in Drupal, or for that matter many other PHP scripts. But if there’s one system that would benefit from more extensive use of error handling, it’s Drupal. (In fact, I’d argue that 99% of the White Screen Of Death errors Drupal is prone to displaying could be prevented by more the more aggressive use of exception handling in code.)
Here’s an example of a try/catch in Drupal code, taken straight from Drupal’s Example module:
What does this do? Most of the time, nothing. But imagine a scenario where the dbtng_example table was somehow deleted from the database. Without the try/catch structure, Drupal would -- if you’re lucky -- just not do anything. More likely, Drupal would display the White Screen of Death and you’d have no idea what caused the problem. (Of course, the worst case scenario in any debugging situation is when the error only happens once in awhile, making debugging almost impossible.)
But with a specific strategy of error handling, the catch block comes to the rescue. Now, instead of a white screen, Drupal will display a message telling the developer exactly what happened. (It’s probably better to write the error message to watchdog, or -- best of all -- to write the message to Watchdog, and only display a direct message to the screen during debugging.)
You’ll note that the error message in this example includes the query and another message that contains more specifics about what went wrong. That’s because in Drupal 7, the Database API contains built in error handling. (Many -- though not all -- or Drupal’s APIs have this feature.)
So what happens when there is no exception object returned by a failed call? In that case, you can write your own. Here’s an example taken from Stack Overflow (http://stackoverflow.com/questions/9041173/throwing-exceptions-in-a-php-try-catch-block) :
In this case the author has created their own error message using the syntax
throw new Exception("The field is undefined.");
This message will be displayed if the catch block is called.
Try/Catch routines are beautiful structures since they can solve so many headaches. But as with many things in the PHP world, they are not perfect and not without controversy. For example, there are many schools of thought about when and where to use them. Some people say, use them literally everywhere. Some say, only when your code failing would initiate a fatal PHP code error. Some even say never, since PHPs overall implementation of exception handling is fundamentally flawed. (This last argument is a little arcane, and seems a little extreme.)
Where and how you use them is up to you, however, here are a few rules of thumb that will probably help your module development.
Use a try/catch block:
Around any hand-written database query. Whether it’s in a custom module, or a snippet of test code, if it’s a query, it should have a try/catch wrapper
Any time the routine involves a function that’s out of your control. (For example, if your code involves image processing, it’s likely going to be passed off to the GD Library, which is run on the server. Since GD doesn’t usually give many good clues about what happens when it can’t handle a process, you should Always use a try/catch.
Any time you are even remotely tempted to suppress native PHP errors by putting an ‘@’ in front of the function. (Instead, use a try/catch)
Any time you are debugging a function and have no idea why the function is failing. (The try/catch is your best friend.)