Best practices for handling exceptional behavior

Dealing with exceptional situations is often neglected aspect of application development. For most, the first association of the notion is throwing/catching exceptions, but actually it is quite a broad topic.

Without further ado, I'll move to the point noting that developers often decide to go with the approach of returning a null from a method or some boolean/integer return code. By doing so, you are actually foisting callers with problems, having them to write if ($result == null) sort of statements instead of a try/catch block which is a more clearer and meaningful way of expressing our intents in a code. Thereby, if you're tempted to return a null from a method, throw an Exception instead or perhaps return a Special Case.

This article is particularly concerned with Exceptions, so here's a suitable example:

$user = $this->usersGateway->fetchOneById($userId);

if (!$user) {
    throw new Exception('User with the ID: ' . $userId . ' does not exist');
}

So that's all, simply throw an exception, it's that easy? Well, not quite...

Custom Exception classes

Primary problem with the above example is that it is not expressive enough. One would have to read the whole message in order to understand the purpose of that particular exception. Also, by having only one type of exception, you are making it harder for the caller to act differently based on the type of exception.

Solution is in defining custom exception classes for every logical whole in which exceptional situations can arise:

class UserNotFoundException extends RuntimeException
{
}

//...

throw new UserNotFoundException('User with the ID: ' . $userId . ' does not exist');

Better? Definitely, but still far from perfect.

Formatting exceptions

Now that you have custom exception classes, it is their responsibility to deal with formulating and formatting exception messages specific for the particular type of an exception. Formatting logic in a place at which exceptions are thrown results in distracting and noisy code, which can only get worse as more detailed messages are needed.

Formatting exceptions is based on a named constructor idiom and there's an excellent article that describes this technique in detail. In essence, the idea is to place logic for building exception message into some meaningfully named factory method of the Exception class itself:

class UserNotFoundException extends RuntimeException
{
    public static function forUserId(string $userId) : self
    {
        return new self(sprintf(
            'User with the ID: %s does not exist',
            $userId
        ));
    }
}

And when throwing the exception, you simply invoke factory method that you've added:

throw UserNotFoundException::forUserId($userId);

Now we're getting somewhere.

Cohesive exception classes

Just like in the case of any other type of classes, adherence to the Single responsibility principle is mandatory when it comes to writing Exception classes. Here's an example of poorly designed exception class:

class UserException extends Exception
{
    public static function forEmptyEmail() : self
    {
        return new self("User's email must not be empty");
    }

    public static function forInvalidEmail(string $email) : self
    {
        return new self(sprintf(
            '%s is not a valid email address',
            $email
        ));
    }

    public static function forNonexistentUser(string $userId) : self
    {
        return new self(sprintf(
            'User with the ID: %s does not exist',
            $userId
        ));
    }
}

We can clearly notice two different responsibilities in this exception class, one concerned with User validation and the other for a situation of requesting non-existent User:

class InvalidUserException extends DomainException
{
    public static function forEmptyEmail() : self
    {
        return new self("User's email address must not be empty");
    }

    public static function forInvalidEmail(string $email) : self
    {
        return new self(sprintf(
            '%s is not a valid email address',
            $email
        ));
    }
}

class UserNotFoundException extends RuntimeException
{
    public static function forUserId(string $userId) : self
    {
        return new self(sprintf(
            'User with the ID: %s does not exist',
            $userId
        ));
    }
}

This makes even more sense if you try to make an analogy between these two exception types and appropriate HTTP status codes, which are 400 and 404 in this particular case.

Exception codes

It is handy to have a code that uniquely identifies error that happened which is typically required in case of API type of applications. PHP Exception classes natively accept code as a second argument so this one is a matter of adding it your custom classes:

class UserNotFoundException extends RuntimeException
{
    public static function forUserId(string $userId) : self
    {
        return new self(
            sprintf(
                'User with the ID: %s does not exist',
                $userId
            ), 
            ErrorCodes::ERROR_USER_NOT_FOUND
        );
    }
}

As you notice, I prefer to keep error codes constants in a single class, in which besides error codes themselves I usually put logic for their mapping into appropriate HTTP status code.

Component level exceptions

Approach of having a component level Exception type that can be caught for any exception that emanates within a component is particularly a good practice in case of library code. But also, applications themselves have layers and it is convenient to have a mechanism for determining context from which Exception has been raised. That is accomplished by applying what is known as a Marker Interface:

namespace App\Domain\Exception;

interface ExceptionInterface
{
}

class UserNotFoundException extends RuntimeException impements ExceptionInterface
{
    public static function forUserId(string $userId) : self
    {
        return new self(
            sprintf(
                'User with the ID: %s does not exist',
                $userId
            ), 
            ErrorCodes::ERROR_USER_NOT_FOUND
        );
    }
}

It's just a matter of creating the ExceptionInterface in every distinct Exception namespace and having custom Exception classes implement it. For example, you might have custom exceptions for your controllers, domain layer and similar. This affords the caller any number of ways to catch the exception.

Voila!

Error handling

So far, this article was all about writing and organizing Exception classes. How and where you catch exceptions is equally important topic.

I prefer to have a system designed in a way that my domain code can freely raise exceptions, which ultimately results in an error response being sent to the client. Catching exception and presenting them to the client is the crucial part of the whole story and ways for solving it are very diverse.

Common approach in MVC-like applications is to catch exceptions in controllers/actions:

class UserController extends BaseController
{
    public function viewUserAction(RequestInterface $request)
    {
        try {
            $user = $this->userService->get($request->get('id'));

            return new JsonResponse($user->toArray()); 
        } catch (\Exception $ex) {
            return new JsonResponse([
                'error' => $ex->getCode(),
                'message' => $ex->getMessage(),
            ], 500);
        }
    }
}

Problem with this kind of solution is that logic for converting exceptions into appropriate error responses can become complex. Just think of a content-based error handling, whereas you need to have HTML but also JSON response in case of AJAX request. Also, you don't want to reveal exception messages raised from lower layers of application, i.e. database connection errors, so you need some filtering logic, too. Indeed, you can put common stuff in the base controller class so that it can be reused in other controllers, but you would still have those try/catch blocks in controller actions. Everything indicates that logic should be placed somewhere else.

Frameworks usually provide mechanism for having a global error handler, that is typically based on having that same try/catch block from the example, but above the controllers, in a dispatcher, that invokes controller/action itself. Catching of an exception usually results in emitting an event about the error that happened or some similar mechanism that allows for having a single place in the application responsible for handling errors.

If something like that is not available, I go with the approach of creating/registering a global PHP exception handler. There are also libraries that facilitate just that, like Whoops, so I usually end up using some of those. Here's an example of how Whoops should be used:

use Whoops\Run;
use Whoops\Handler\PrettyPageHandler;
use Whoops\Handler\JsonResponseHandler;

$run = new Run();

$run->pushHandler(new PrettyPageHandler());

if (\Whoops\Util\Misc::isAjaxRequest()) {
    $jsonHandler = new JsonResponseHandler();
    $run->pushHandler($jsonHandler);
}

$run->register();

Additionally, I push a handler to filter/prepare Exception message and code that among other things makes sure that I return something like "An error has occurred" instead of some "Can't connect to MySQL server on 192.168.33.10" sort of message:

$run->pushHandler(function (Exception $ex, Inspector $inspector, Run $run) {
    //Some logic for filter/preparing Exception
});

There are many different ways for dealing with error handling and if you know some interesting alternative you are applying, I would like to see it, so drop some comment.

Next time I'll present you error handling solution in a Zend Expressive-powered application.


exceptions error handling oop clean code