Throwing Your Own Library Exceptions in PHP
Published: 2020-01-28 09:40:26

---
title: Throwing Your Own Library Exceptions in PHP
published: true
tags: 
canonical_url: https://codeinphp.github.io/post/throwing-your-own-library-exceptions-in-php/
---

In the [previous post](http://codeinphp.github.io/post/exceptions-are-bad-yet-awesome/), we saw the difference between errors and exceptions, how exceptions can be useful and created our custom exception handler. In this post, we will look how we can create custom exceptions specific to our application, library or company. We also talk about [SPL Exceptions](http://php.net/manual/en/spl.exceptions.php) that should be used in your code where possible as best practice.

## Background

Let's start with bit of background. If you worked on PHP's DOM, PDO, MySQLi or some other extensions or frameworks such as Symphony, Laravel, Slim or any other third party libraries, you might have noticed usually they throw their own exceptions:

```
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
```

In case of wrong database connection, it would throw `PDOException`:

```
PDOException: SQLSTATE[HY000] [1045] Access denied for user 'dbuser'@'localhost'
```

Where did `PDOException` come from ? Or how did PDO gave that exception ? It is simple. As I had pointed out in my [previous post](http://codeinphp.github.io/post/exceptions-are-bad-yet-awesome/), [`Exception`](http://php.net/manual/en/class.exception.php) is a class like any other normal class that can be [extended](http://php.net/manual/en/language.exceptions.extending.php). That's exactly what PDO does here, it simply `extends` the `Exception` class so PDO extension should be doing something like this under the hood to be able to throw `PDOException`:

```
class PDOException extends Exception {}
```

And that's _usually_ it. And then PDO should now be throwing that new exception instead of `Exception`:

```
// something went wrong
throw new PDOException('message here');
```

And then user of PDO sees those `PDOExceptions`.

Now that we know `PDOException` actually extends `Exception`, we can come to conclusion that we can catch exceptions thrown by PDO either by using `PDOException` or `Exception` through _nested_ catch blocks. If exception is NOT caught for `PDOException`, it will fall back to base `Exception`:

```
try{
    // any PDO code here
}
catch(PDOException $e){
    // handle PDOException
}
catch(Exception $e){
    // handle Exception
}
```

## Throwing Your Own Exceptions

As said above, we can throw our own exceptions by extending the `Exception` class:

```
class MyAwesomeLibraryException extends Exception {}
```

or

```
class MyCompanyException extends Exception {}
```

That's easy but one might ask why throw custom exceptions? I can see these reasons:

- It makes it easy to recognize which thing (library, class, extension, etc) generated exception in code hierarchy
- This helps developer of orginal library easily spot problems in their code
- It brands your library exceptions like PDO, DOM, etc do.

Now as developer of some library/application, any time we find we need to throw exception, we simply throw our own custom exceptions:

```
// something wrong, throw our custom exception
throw new MyAwesomeLibraryException('some message');
```

Of course you can have many exception types as well for your application if you want.

## Prioritizing Your Own Exceptions

Imagine we want to create our own ORM library called **SuperORM** and it uses PDO under the hood. We create our custom exception first:

```
class SuperORMException extends PDOException {}
```

And now we throw `SuperORMException` exception from whole of our ORM where needed. But since we are using PDO under the hood, we get its `PDOException` as well and we don't want to show this _directly_ to the consumers of our SuperORM library, we want to be able to first show them our own exception type. This is how we can do that:

```
class SuperORMException extends PDOException {}

class SuperORM {
    public function connect($dsn, $user, $password) {
        // try connecting to database
        try {
            $dbh = new PDO($dsn, $user, $password);
            $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
        catch (PDOException $e) {
            throw new SuperORMException($e->getMessage(), null, $e);
        }        
    }
}

$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';

$superORM = new SuperORM;
$superORM->connect($dsn, $user, $password);
```

And that would result in our `SuperORMException`:

`SuperORMException: SQLSTATE[HY000] [1045] Access denied for user 'dbuser'@'localhost'`

And consumer of our SuperORM can catch our exception now:

```
try {
    $dsn = 'mysql:dbname=testdb;host=127.0.0.1';
    $user = 'dbuser';
    $password = 'dbpass';

    $superORM = new SuperORM;
    $superORM->connect($dsn, $user, $password);
}
catch (SuperORMException $e) {
    echo $e->getMessage();
}
```

This makes sure consumer of library will now know that SuperORM will always throw exception of type `SuperORMException`.

We see that `SuperORMException` extends `PDOException` and `PDOException` extends `Exception`, this gives consumer the opportunity to catch exceptions in those types like so:

```
try {
    $dsn = 'mysql:dbname=testdb;host=127.0.0.1';
    $user = 'dbuser';
    $password = 'dbpass';

    $superORM = new SuperORM;
    $superORM->connect($dsn, $user, $password);
}
catch (SuperORMException $e) {
    // code to catch exception
}
catch (PDOException $e) {
    // code to catch exception
}
catch (Exception $e) {
    // code to catch exception
}
```

So that's how you can throw your own custom exceptions. In fact that's how some of the ORMs or database libraries from various framework do and throw their own exceptions for consumers to catch.

## Don't Always Throw Your Custom Exceptions

By this I mean there are certain exceptions types that are part of Standard PHP (SPL) known as [SPL Exceptions](http://php.net/manual/en/spl.exceptions.php). They are made to be thrown for _specific_ reasons and considered best practice. You should throw SPL Exceptions _where applicable_ instead of your own custom exceptions. Here they are:

- [LogicException](http://php.net/manual/en/class.logicexception.php) (extends [Exception](http://php.net/manual/en/class.exception.php))
  - [BadFunctionCallException](http://php.net/manual/en/class.badfunctioncallexception.php)
    - [BadMethodCallException](http://php.net/manual/en/class.badmethodcallexception.php)
  - [DomainException](http://php.net/manual/en/class.domainexception.php)
  - [InvalidArgumentException](http://php.net/manual/en/class.invalidargumentexception.php)
  - [LengthException](http://php.net/manual/en/class.lengthexception.php)
  - [OutOfRangeException](http://php.net/manual/en/class.outofrangeexception.php)
- [RuntimeException](http://php.net/manual/en/class.runtimeexception.php) (extends [Exception](http://php.net/manual/en/class.exception.php))
  - [OutOfBoundsException](http://php.net/manual/en/class.outofboundsexception.php)
  - [OverflowException](http://php.net/manual/en/class.overflowexception.php)
  - [RangeException](http://php.net/manual/en/class.rangeexception.php)
  - [UnderflowException](http://php.net/manual/en/class.underflowexception.php)
  - [UnexpectedValueException](http://php.net/manual/en/class.unexpectedvalueexception.php)

As can be seen, those exceptions can be broadly divided into two main categories: `LogicException` and `RuntimeException`. All of those exceptions are self-explanatory from their names. For example `LogicException` represent any errors caused by the logical errors in your code, `RuntimeException` represent any errors that are caused after script has run eg runtime errors; similarly `BadMethodCallException` exception should be thrown if a method in your class doesn't exist and so on.

These SPL exceptions are excellent addition to PHP core because previously if call was made to some method which didn't exist, you usually communicated that to user through a message like:

```
throw new Exception('Method does not exist!');
```

So you communicated through those messages for different types of problems. The problem here was that we needed specific exceptions; if method didn't exist, we needed `BadMethodCallException` exception. This clearly tells developer what type of exception it is and _why_ it might have come about. Another benefit is that such exceptions are useful even for Non-English speaking developers who can also easily spot the problem. Therefore, you must use SPL exceptions for different situations they are made for.

 ![](http://feeds.feedburner.com/~r/codeinphpfeed/~4/_7BKEaNBRKY)