When writing code for handling exceptions, do you repeat yourself ?

damilola jolayemi
Damilola Jolayemi
Published in
6 min readOct 31, 2017

Exception handling is an important subject in software development because it gives us the ability to handle in advance runtime errors that could occur when our program is executed. It’s bad and unprofessional when these errors are not handled because the program execution is terminated and a system error message is displayed to the user. The messages generated are not often user friendly. We don’t want that. I’m sure users don’t.

Photo by Markus Spiske freeforcommercialuse.net

The Solution

Often these exceptions are handled by using handlers. In an handler we can state the actions that should be taken when an exception happens. The php code below shows an exception handler in it’s simplest form.

try {    if (10 < 20) {        throw new Exception("Message");    }    //some other code to execute
//this will not be executed if an exception is thrown
} catch(Exception $exception) { //handle the thrown exception here}

Since exceptions extend the root exception class(this catches all exceptions) in PHP (an exception is required to do this, directly or indirectly), therefore we are able to extend it with our own custom exceptions which means the code above can easily become this:

try {    if (10 < 20) {        throw new AnotherException("Message");    }    //some other code to execute
//this will not be executed if an exception is thrown
} catch(AnException $exception) { //if this is thrown, it will be handle here} catch(AnotherException $exception) { //this one is thrown, it will be handle here} catch(BaseException $exception) { //if this is thrown, it will be handle here} catch(Exception $exception) { //if this is thrown, it will be handle here}

What is happening here is that PHP performs a kind of an “instanceof” check on each catch block and once it finds that the exception thrown is of the same class as the exception in the catch block or if it is a descendant of the one in the catch block, the code in that particular catch block will get executed.

The Possible Problem

This is where the ugliness can begin to creep in. It’s possible to have different try blocks with corresponding multiple catch blocks. I almost had this in a PHP package in am working on. The package communicates with Stripe’s api. In the package, plans can be created, deleted, subscribed to, customers created, charged etc. On Stripes api documentation for error handling there is a code snippet showing a list of exceptions that could be thrown while any of these actions is performed. Take a look at this:

try {    // Use Stripe's library to make requests...} catch(\Stripe\Error\Card $e) {    // Since it's a decline, \Stripe\Error\Card will be caught} catch (\Stripe\Error\RateLimit $e) {    // Too many requests made to the API too quickly} catch (\Stripe\Error\InvalidRequest $e) {    // Invalid parameters were supplied to Stripe's API} catch (\Stripe\Error\Authentication $e) {    // Authentication with Stripe's API failed} catch (\Stripe\Error\ApiConnection $e) {    // Network communication with Stripe failed} catch (\Stripe\Error\Base $e) {    // Display a very generic error to the user, and maybe send    // yourself an email} catch (Exception $e) {    // Something else happened, completely unrelated to Stripe}

Imagine doing this to every api call. Things could go ugly in no time. If you are wondering why the need for other catch blocks since a genetic catch all is provided by PHP. It’s because I wanna be sure which type of exception is thrown and act accordingly (to log it, notify the admin and/or show a message to the user that provides a possible action step to fixing it). Stripe promises to make the error message user friendly.

Curious Search

Even though I came up with a solution that i’ll share in the next paragraphs, I am still curious to see how other developers have handled this. A result from one of my many searches on Google led to this stackoverflow page.

Another Solution

So my solution works by catching the base exception class, since it gives us access to all it’s descendants. When an exception is thrown, we get an instance of the specific exception thrown, we new up a new custom handler with the exception, determine the type it is and respond accordingly. I’ll use my Stripe situation to better explain. In the method responsible for creating a plan, i update code to this.

//plan.phpuse Handler;
public function create ()
{
try {
// Use Stripe's library to make requests... } catch (\Stripe\Error\Base $e) { return (new Handler($e))->respond(); }
}

I created the new class, handler. Added two properties $exception, $expectedExceptions and a constructor to initiate the class. The $expectedExceptions is an array holding a list of exceptions that could be thrown by Stripe.

//handler.phpclass Handler {    protected $exception;    public function __construct($exception)
{
$this->exception = $exception; } protected $expectedExceptions = [ \Stripe\Error\Card::class, \Stripe\Error\RateLimit::class, \Stripe\Error\InvalidRequest::class, \Stripe\Error\Authentication::class, \Stripe\Error\ApiConnection::class, \Stripe\Error\Base::class ];}

Next, I created a new method, unexpected().

//handler.phppublic function unexpected(){    $exceptionClass = get_class($this->exception);    if ( ! in_array($exceptionClass, $this->expectedExceptions) ) {     //something unrelated to stripe happened.
//handle it here
}}

When this method is called, it gets the class of the exception thrown and checks if it’s in the $expectedExceptions array. All that is needed now is one last method respond() to bring it all together.

//handler.phppublic function respond(){$this->unexpected();if($this->exception instanceof \Stripe\Error\Card ) {    //respond accordingly} elseif( $this->exception instanceof \Stripe\Error\RateLimit ) {    //respond accordingly} elseif( $this->exception instanceof \Stripe\Error\InvalidRequestt ) {    //respond accordingly} elseif( $this->exception instanceof \Stripe\Error\Authentication ) {    //respond accordingly} elseif( $this->exception instanceof \Stripe\Error\ApiConnection ) {    //respond accordingly} elseif( $this->exception instanceof \Stripe\Error\Base ) {    //respond accordingly}}

All the respond() method does is handle an unexpected exception, check which specific instance of the caught exception is and respond accordingly. Simple.

Still Solving

Now, if you are concerned about the lengthy if/else block, you are not alone, it concerns me too. We’ll have to do things differently to get rid of it. I created a new property $exceptionClass, to hold the exception class of the exception we caught. And so our $unexpected() method needs an update to reflect this change.

//handler.phpprotected $exceptionClass;public function unexpected(){$this->exceptionClass = get_class($this->exception);if ( ! in_array($this->exceptionClass, $this->expectedExceptions) ) {    //something unrelated to stripe happened    //handle it here}}

I proceeded to add new methods with their names matching each expected exception’s name formatted in a specific manner; the backslash was removed and the first letter was turned to lowercase.

So \Stripe\Error\Card::class has a method in the format stripeErrorCard(), \Stripe\Error\InvalidRequest::class also has a method in the format stripeErrorInvalidRequest(). Same applied to all other expected exceptions.

//handler.phppublic function stripeErrorCard(){    //handle it}public function stripeErrorInvalidRequest(){    //handle it}public function stripeErrorAuthentication(){    //handle it}public function stripeErrorRateLimit(){    //handle it}public function stripeErrorApiConnection(){    //handle it}public function stripeErrorBase(){    //handle it}

Finally, i had to change the code in respond() method to this:

//handler.phppublic function respond(){    $this->unexpected();    $expectedExceptionMethodName = lcfirst (        preg_replace("/[^a-zA-Z]/", "", $this->exceptionClass)    
);
$this->$expectedExceptionMethodName();}

What I am doing is to create different methods, these method have names that correspond to each expected exceptions class name formatted into a string in the particular manner described above. Now when our application throws an exception and it’s caught, we get the caught exception’s class name, we check if it’s in the array of $expectedExceptions, if it is, we format it into a string that matches one of the functions we created previously and call it. When the variable function is called the exception will be handled has required.

I hope you find this helpful. You can get the full code here.

Thanks for reading.

--

--