Exceptional Exceptions

PHP5 introduced exceptions, a special class that can be thrown and caught (as opposed to errors which are raised) indicating an unexpected event. Unlike errors, exceptions are designed to be handled by the calling code and will bubble up the execution chain until they are caught. Code in the current scope will stop executing as soon as an exception is thrown (so any lines after a throw statement won’t be executed) and control is handed back to the first matching exception handler (either a catch block, configured exception handler, or language-provided exception handler). Only when an exception is caught will code execution continue from there.

This article does not aim to teach you exceptions at a 101 level, but instead gives an opinion on how to use exceptions better. If you’ve never used exceptions before, you may want to look at the PHP Manual, or pick up a copy of the book PHP Master: Write Cutting-Edge Code written by friends of mine and which does an excellent job teaching you pretty much anything you need to know to write modern, sane PHP code.

An Error is Not an Exception

So you already know what exceptions are, but you’re probably wondering what the difference is between PHP errors and (custom) exceptions. The logic is actually quite simple: an error is non-recoverable, occurs in the main execution loop, and is an indication of the stability of the environment. For example, if an E_NOTICE is raised because you are attempting to access a scalar value as an array, this indicates there is an issue with your code. It is not guaranteed safe to continue. The condition can’t be corrected during execution. And if an E_PARSE is raised because an unexpected T_IF is found by the parser, well, then you see how that can affect the stability of things.

Exceptions on the other hand are recoverable, can (and usually will) occur outside the main execution loop, and do not give any indication of a system’s stability. It’s a component saying “I can’t do what you asked for with the given input, so do whatever you want with that information.” If a library throws a LengthException, it indicates that the passed value is either too long or too short and thus it cannot complete the given instructions with the current value. This doesn’t mean your environment is unstable, it just means that your code will have to adjust the value’s length by either padding or truncating it. Your code can catch this exception, update the value, and try again.

The Not-So-Exceptional Exception

Here’s the hardest question of them all: what exactly warrants an exception? Of course, your exception has to abide by the three rules in the previous paragraphs. Throwing an exception when you encounter corrupted memory is a very bad thing to do. Your code should raise an error instead so PHP can abort as quickly as possible because it has been proven that the environment is unsafe to continue.

But even if an error is uncalled for, not every situation that is not a success demands an exception. That is to say: not every situation which isn’t successful is an exceptional situation. The word “exception” means an action which is not part of ordinary operation or standards, an anomaly, something that deviates from what is normal and expected.

A former colleague once told me over dinner how the XML/RPC service that was in use as the backbone of all public-facing operations at his company was designed. The architect then had learned about exceptions and how convenient they were for indicating non-success statuses.

The backbone provided, among other things, single sign-on functionality. Instead of a web application accessing a database directly, it would query the XML/RPC service which would then reply based on the centralized data storage serving all web apps. When valid credentials were given, a success status was returned. When something was off, an exception was thrown with a message indicating the reason for the failure. Easy to catch, and you can show the message to the user in a flashy, sparkling error message. But is a user providing an incorrect username and/or password really deviating from what is expected?

In my projects, I deal with users who aren’t perfect and make typos or forget things. Getting incorrect credentials is very much expected, almost more likely than a valid set of credentials. Validating credentials is expected behavior for a login system, so in this case the XML/RPC service should return a status indicating the success of the validation. Though the credentials did not pass, the validation process itself still ran successfully. If the validation process didn’t execute correctly, then something else was wrong. Maybe the data storage was unreachable, or who knows what else. A login system not able to connect to it’s data storage is very much outside of what is expected, as it can’t function without it. So that would warrant an exception.

Note: some might argue a login system not being able to connect to a data storage is a sign of an unstable environment and should therefore raise an error. It is not the responsibility of the login system to raise errors for the data storage, however. Instead, the data storage connector/wrapper should raise the error if it deems it necessary.

As a general rule, you can see an exception as a situation where a developer has to come in, look at the situation, and handle it. The code the exception scenario occurs in is no longer able to do it by itself. This can be a situation where developers have already looked at the code and their way of handling it was to let it happen whenever it did. Don’t start mailing all your exceptions to the NOC; they won’t appreciate that! Handle what you can and should, and only throw exceptions when there is really no way of going forward.

“Problem”

When I was backpacking through Europe a couple years ago, I stumbled upon a memorable sight at a train station in Greece. One of the locker sections looked like a bomb had gone off with doors laying on the floor, hanging half from their hinges, or were smashed in. I later learned they were in the process of removing the locker section, but the remarkable part was how they was communicated to customers that this section was out of service. Lots of tape was applied on the central section holding a sheet of paper with the word “PROBLEM” written on it. Technically, it was entirely correct. There was obviously something wrong with the lockers and the situation had been handled by communicating that to the customers.

You may find it funny, but in reality you see this in code all the time. If you throw just Exception, you are basically saying “PROBLEM” with no other means for the code to know what is going on. While Exception is the base class of every exception, you are free to extend it with your own types. A wider collection of exceptions can be found in the SPL library, but even that is far from the limit. Look at major PHP frameworks like Zend Framework or Symfony and you will see they use custom exceptions for pretty much every different condition. It’s a bit of a hassle to write all those files so they can be dynamically loaded and to maintain all the different types, but it gives the framework and the consumer of that framework granular control over a situation that has occurred. If just an Exception is thrown, then all you can safely assume is that something isn’t right and you might as well just give up. That means you’re using exceptions as if they were errors, the catch block as a mute operator, and just abandoning all hope that someone can somehow correct the situation.

Take a look at the following example:

<?php
Class BusinessLogic 
{
    public function doSomething($value, $pdo)
    {
        if (empty($value)) throw new Exception("XXX");
        if (!ctype_digit($value)) throw new Exception("XXX");
        if (!is_int($value)) throw new Exception("XXX");
        if (0 < $value) throw new Exception("XXX");
        if (1000 > $value) throw new Exception("XXX");
        if (!is_object($pdo)) throw new Exception("XXX");
        if (!$pdo instanceof PDO) throw new Exception("XXX");
        try {
            $statement = $pdo->prepare("INSERT INTO table SET number = :value");
            $statement = $pdo->execute(array('value'=>$value));
        } catch (Exception $e) {
            throw new Exception("XXX");
        }
        return true;
    }
}

Calling doSomething() with two variables gives you two possible outcomes: an Exception or boolean true indicating success. If the result is not true, your code can not tell why it didn’t work. It could be that the variable was not an integer; it could be that an intern accidentally dropped the database; it could be that the janitor unplugged the data center causing all SQL nodes to go offline; anything is possible. All your code can tell for certain is that there was a “problem”.

In this abstract example, you may not care if it worked or not (which is a valid option even if specialized exceptions are thrown; it’s your choice to handle them or just stop trying), but what if this code was storing your salary in a payroll system? Would you really be okay with getting no paycheck because the app just gave up?

We could rewrite this code to be more specific. Take a look at this:

<?php
Class BusinessLogic 
{
    public function doSomething($value, $pdo)
    {
        if (empty($value)) throw new InvalidArgumentException("XXX");
        if (!ctype_digit($value)) throw new InvalidArgumentException("XXX");
        if (!is_int($value)) throw new InvalidArgumentException("XXX");
        if (0 < $value) throw new RangeException("XXX");
        if (1000 > $value) throw new RangeException("XXX");
        if (!is_object($pdo)) throw new InvalidArgumentException("XXX");
        if (!$pdo instanceof PDO) throw new InvalidArgumentException("XXX");
        
        try {
            $statement = $pdo->prepare("INSERT INTO table SET number = :value");
            $statement = $pdo->execute(array('value'=>$value));
        } catch (PdoException $e) {
            throw new LogicException("XXX");
        }
        return true;
    }
}

Now the calling application can see exactly what is going on and why the transaction wasn’t completed successfully.

In case you’re wondering why I put “XXX” as the exception message, I did so because your calling code should never ever ever read the message. The only thing the message is good for is for developers – not for code. You can not test the message and handle the exception based on that, or, worse… test the line number and handle it based on that! Don’t rely on magic. Instead, you should handle them based on type and type alone.

Anyway, let’s add some calling code which actually uses these exceptions:

<?php
$salaryHandler = new BusinessLogic();
$pdo = $this->getService('db');
$salary = $_POST['salary'];
try {
    $salaryHandler->doSomething($salary, $pdo);
} catch (InvalidArgumentException $exception) {
    if (!is_object($pdo) || !$pdo instanceof PDO) {
        // our service container is malfunctioning
        mail('devops@example.com', '[EXCEPTION] Code broke!', 'It really did!');
    } else {
        $this->addFlash('The value given is not a valid number. Did you accidentally put a dollar sign in?')
    }
} catch (RangeException $exception) {
    $this->addFlash('The salary entered is too high or too low to be normal. Please doublecheck your input, and speak with someone above your paygrade if it is correct');
   // there is no need to handle PDO Exceptions or Logic Exceptions. Neither are our responsibility.
}

This gives us granular control over how we handle a specific situation. The bigger and more complex a library or module gets, the more important it is to use custom exceptions. If you do 1,000 things, a 1,000 things can go wrong. Some things can be corrected by the calling code while others should be handled. The remainder just bubble up.

Read that again: bubble up. That’s the beauty of custom exceptions; you can pick and choose what you catch and what you don’t. Your calling code does not have to handle every single exception. It can limit itself to just catching the exceptions it knows how to handle and for which it is responsible to handle. Your calling code may be called by other code which may handle the remainder.

The Catch All

So if it’s such a bad idea to have non-custom exceptions and to catch every single exception possible, then why does the language even allows this? There is one exception to the rule of always using and catching specific exceptions, which happens to be the catch-all rule.

The catch-all is the highest catch block which must catch every exception that bubbles up to that level. There is one included in PHP itself (ever seen the “Fatal Error: Uncaught Exception in ….” message?), but you can override it with a custom handler to act as a fallback. You can set this handler using the set_exception_handler() function, so feel free to do so and then add a rule to your PHPMD ruleset that disallows lines like “} catch (Exception $e) {”.

This is the one and only reason a general exception handler, which catches every instance of the Exception class not already caught, should find its way into production code. Every other handler must be specific and limited to the exceptions it knows how to handle and is responsible for. It is definitely better to err on the conservative side here and have a handle-able exception bubble up once (and then fix that in the code) than to catch too many and act as a mute operator.

Summary

In summary, only throw exceptions when your code cannot complete the requested instruction with the given input, always throw a custom exception that actually tells the calling code what the situation is, and if you call other code then only catch the exceptions that you can and should handle. This will make your component a lot less of a black box (custom exceptions) and reduce the chance that a developer integrating your component will have to change your code (don’t catch what you shouldn’t). We always tell our clients/managers to be specific, but we should be specific too!

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://blog.unreal4u.com unreal4u

    Very nice post! It helped me a lot to understand the logic behind exceptions.

    The only thing I would add is that you must create a new class for each new exception you’re making, or else you will get an “unknown class” error each time your app throws one.

    class versionException extends Exception {}
    is enough to be able to throw now a versionException.

    Greetings.

  • http://alexfraundorf.com Alex Fraundorf

    Very nice article Remi.
    It does a great job of explaining how exceptions should be used and extended, which is lacking in many exception tutorials.
    Thanks for writing it!

  • http://Pixelerium.com Pixelerium

    I have not started throwing exceptions but this article has helped me see the bigger picture and have a better understanding of exceptions. Thank you for taking the time to write this.

  • http://www.audero.it/ Aurelio De Rosa

    Although I was already aware of the practices suggested by your article, I really appreciate it. Good work!

  • Vince

    Good article. I see your point of using exceptions only for exceptional situation and not for unsuccessfull events like you described it with the example of the login system.
    But I also think that sometimes, exceptions can be used for all kind of unsuccessfull events because it can make code much more clean and easy to read by throwing exceptions and catching them instead of nesting many ‘if’ and ‘else’ to check return values of functions.
    One more thing: in applications such as web interfaces, I think it can be quite convenient and acceptable, in the controller, to use just the normal ‘Exception’ class and also to use the message of the Exception as the message to be displayed to the end-user.

    • http://www.wolerized.com Remi Woler

      Code readability is in the eye of the beholder of course. Personally I don’t see a try/catch block being any more readable than an if/else block, or in your case probably a switch block. They share the same markup and whitespace. Throwing an Exception for a multi-return value is a very expensive task, and it is much simpler, cleaner and nicer for the compiler to return an struct (array with string keys) instead.

      It is not clean and not acceptable to use Exceptions as a return-value handler. Maybe it’s simple in your 6line proof of concept, but for any app more than that it’s very messy, untraceable unless deployed live, will break when another exception handler is installed (xdebug, Zend Server, etc etc), and so on. Using the message for end-users instead of developers also imposes many problems. For a login system, you don’t want to communicate to the end-user why exactly the user/pass was not accepted (to not help scriptkiddies) so then the only valid Exception to *anything* going wrong in the validator/authenticator would be with a message of “Username or password incorrect, please verify your credentials and try again”. Good luck debugging that! A real Exception could say “Database table users not found but needed to authenticate”, which is a lot easier for a developer to fix.

      • Vince

        I think you misunderstood most of my comment. I didn’t mean using Exceptions as a return-value handler. I meant that when you have a function which return a boolean to let you know about the success or not of the execution of the function, then it can be good to use exceptions instead, which means that you don’t need to check the return value.
        Regarding the second point, I meant only for web interfaces, like for example in the controller. I don’t mean in low layers like for example in a class handling the authentication of users.

  • Chris

    Hi There,

    What about Exception chaining? Have any recommendations and best practices for this?

  • http://objectic.cc Niko Kivelä

    About bubling up: “you can pick and choose what you catch and what you don’t” – you can also choose to catch some, do something about it and throw it again for the upper layer to handle it again. And don’t forget about the possibility for exception to have previous exception as a follow-up or chaining.

    One good princible is that system should be designed to throw exception to handle errors, or not throw exceptions at all. Sometimes I’ve seem systems that use methods to return different kind of values to point out the result (if value was too big, too small or wrong type) and sometimes handle that which exceptions (which I prefer :) but not to do both…

    What I’ve used to do it to namespace my application, domain and exceptions. Consider class My_Auth_AuthenticationService. While it’s operating, it can throw exceptions such as My_Auth_Exception_CredentialsMissingException or My_Auth_Exception_MissingDatabaseConnection.

    Having and unique exception for every unique exception is a good thing. It allows developer to react to things she’s interested about. How I’d do my exceptions is:
    – My_Auth_Exception_CredentialsMissingException implements My_Auth_Exception_ExceptionInterface extends InvalidArgumentException
    – My_Auth_Exception_MissingDatabaseConnection implements My_Auth_MissingDatabaseConnection extends RuntimeException.

    Now anyone using my code as a library or as a service can choose to catch al My_Auth_Exception_ExceptionInterface exceptions and so controlling the unexpcted. Yes, interfaces are catchable too! Developer can catch the spesific class type (the class name) and the system can choose to do something about the RunTime- or LogicExceptions, log and mail them and notify the developer withing at the error handler.

  • http://www.kimprince.com Kim Prince

    Hi Remi,
    This was an excellent article, thanks. Now, any chance you could do an article on return values from layers? For example, mapper return values (void, boolean, object or exception), service return values for the UI, etc?
    We all have our own approaches to these things but the way to you explain exceptions above helps give us structure. It would be good to have some rigour around layer return values.
    Kim

  • http://www.talksonweb.com AK

    Good article but still curious about one thing. In PHP I can trigger user based errors (‘trigger_error()’ with E_USER_NOTICE). I’m guessing this was the old system that they had given to users & the exception class added to PHP 5 is the new way, right?