I recently worked with the league/oauth2-server php framework for OAuth2 by @alexbilbie which is -by the way- very easy to use and integrate. Many thanks to the author ūüôā !

If you take a closer look at the source code of this framework you are maybe as thrilled as I am about the cleanness of the code given by the clear structure and wise naming.

The fact that impressed me most was the way the exception classes are defined and grouped. Instead of defining a separate exception class for each case there are just two classes: An OAuthServerException and an UniqueTokenIdentifierConstraintViolationException.

The last one looks like a usual exception for the case that there is a special constraint violation as you can already guess by its name. The exception will be raised if there is an error when creating a new token identifier which already exists. However, the static factory method to create an instance was a bit unfamiliar for me at first sight.

The first exception class, however, contains more lines of code than usual exceptions. The class consists of some members and dynamic methods. These hold and give access to typical data about the occurred exception like the causing exception, an additional payload and the incoming http request. Furthermore it contains the http status code, an error message as well as an additional hint to signal and explain the error to the client.

So far it looks like a usual exception class containing special information for http stuff.
And yet there is one difference: the static methods just as the one in the other exception class. This time we can count nine of them. Each returns a new instance of the exception class for a special case.

Which looks a bit unfamiliar at first, makes the usage and the structure for exception handling very easy. If there is an invalid requests e.g. because of a missing parameter you can simply call throw OAuthServerException::invalidRequest('name');. The factory method then creates a new instance of the exception class and passes all required arguments for this case which results in a very smart and helpful exception message containing the hint Check the `name` parameter.

The factory methods ensure that there is one source of truth for defining the error codes, messages or hints. This helps that you won’t mess up with error strings all over the source code.
Additionally, it allows to identify the places where the exceptions are thrown by using the “show usages” feature of your IDE.

Conclusion

I recommend you to take a closer look at the exception handling used in the league/oauth2-server php framework.

In my former projects I usually ended up structuring exception classes in a kind of hierarchy using inheritance. This sometimes turned to a big mess using lots of instance of checks to differentiate the exception.

However, grouping similar exceptions into one class makes a lot of sense when combining it with factory methods for each exception case.