Why You Should Use SPL Exceptions in PHP, for Better Exception Handling

Since PHP 5.1.0, we have had access to the SPL Exceptions.

Before they came along most people just used plain old throw new Exception(...) or created their own Exception class that extended the base Exception class.

But now we have the option of using (or extending) specific exception types.

And you should always be using the SPL exceptions (or extending them with a subclass), and never using plain old /Exception anymore!

What are the SPL exception classes?

There are around a dozen SPL exception classes, which all eventually extend the base \Exception class.

They don't really do anything special by themselves, but can be used for reporting and handling certain error types.

They are used in the exact same way - just use throw new OverflowException.

Why should you use SPL exceptions?

They give more detailed error reporting. It is bad practise to have 100s of lines of code throwing just Exception. Ideally you should have your own custom Exceptions (which should extend the SPL exceptions), but it isn't practical to do this for every time you can throw an exception.

The 13 SPL exceptions cover a wide range of common situations that you'll face, such as invalid arguments (InvalidArgumentException) or overflows (OverflowException).

How to select the appropriate exception

Sometimes it can be really obvious which exception to throw (or extend with a subclass), however there are some cases where it isn't obvious or where it could fit into two or more exception classes.

When it isn't clear which one to select, you can always go with a parent exception class. There are two 'main' classes:

  • LogicException which includes the following subclasses:
    • BadFunctionCallException
      • BadMethodCallException
    • DomainException
    • InvalidArgumentException
    • LengthException
    • OutOfRangeException
  • RuntimeException which includes the following subclasses:
    • OutOfBoundsException
    • OverflowException
    • RangeException
    • UnderflowException
    • UnexpectedValueException

So you can always just select the relevant parent exception (but don't go for the base \Exception class any longer!)

The majority tend to be a RuntimeException variant.

The 13 SPL Exceptions - and when to use them!

LogicException

This is a parent class to around half of the SPL exceptions (RuntimeException is the parent class to the others).

It is quite commonly used (although in many cases you should use one of its subclasses). Use it when there is an error in the program logic.

BadFunctionCallException

This exception doesn't tend to be used much in my experience. You should throw it if a callback refers to an undefined function or if some arguments are missing.

BadMethodCallException

This is a subclass of the BadFunctionCallException

This is similar to BadFunctionCallException, but for methods. It should be used when a class method doesn't exist, or it has the wrong parameters (i.e. missing parameters).

You will often find BadFunctionCallException being thrown in a class' __call($method, $params) method if self::$method() does not exist.

DomainException

This exception should be used if a value does not adhere to a defined valid data domain.

It is often used for 'sanity checks'. For example, if you are trying to simulate throwing a 6 sided dice, then if the value is > 6 you can throw a DomainException (as the values should always be from 1 to 6). It is used quite a bit in Laravel's database classes if you try to do some action that the database driver doesn't support (for example, doing doAdvisoryLock() will throw a DomainException if your connection is to a SQLite database, as SQLite does not support advisory locks.

InvalidArgumentException

This is another commonly used exception. You see it when an argument (such as method or function parameter) is not valid.

For example, if you are expecting $input to be an array, you could throw InvalidArgumentException if is_array($input) != true.

LengthException

This exception is designed to be thrown if a length is invalid. For example if a string length is too large or too small.

For example, if you are dealing with ISBN-10 barcodes (which always have a length of 9 characters), if strlen($input) != 9 then you should throw a LengthException.

OutOfBoundsException

An OutOfBoundsException should be thrown if a value is not a valid key.

This is commonly used when trying to access items in an array that do not exist, or an offset is too high.

It is only used for key value that aren't indexes (e.g. $array['some-key-that-does-not-exist'].)

If you are dealing with an integer index that is out of bounds then you should use the next exception: OutOfRangeException.

RuntimeException

This is a parent class to around half of the SPL exceptions, and it should be thrown if an error which can only be found on runtime occurs.

OutOfRangeException

This is similar to OutOfBoundsException but should be used for array indexes (integers) that are not valid.

OverflowException

If you have a container class, and you try to add an element to it but it is full then you should throw an OverflowException. They can also be used in situations where you have a maximum number of retries, and after $i > $maxRetries it would be a good idea to throw an OverflowException.

UnderflowException

This is the opposite to the OverflowException, and is used when removing elements on an empty container.

If you have a container class with zero items in it, and your code tries to remove an item then you should throw an UnderflowException.

RangeException

This is used when a value is not within a specific range of values.

It is similar to DomainException but this class extends RuntimeException.

UnexpectedValueException

You should throw an UnexpectedValueException if a value does not match with a set of values.

This would normally be when a function calls another function and expects the return value to be of a certain type or value not including arithmetic or buffer related errors.

Comments Why You Should Use SPL Exceptions in PHP, for Better Exception Handling