Demystifying Exceptions
Tags: programming, php, errors, exceptions
Published: 2020-03-09 18:38:38

A solid understanding of exceptions is crucial to writing reliable programs. This article aims to lift the veil of mystery surrounding peculiar aspects of exception handling by unraveling their inner workings to familiar concepts.

The code examples and some nuances are specific to PHP, but the principles are transferable to other languages — particularly JavaScript.

## Introduction

Exceptions are a special language mechanism designated for error handling. It relieves programmers of the need to reinvent ad hoc error formats and means for error carryover across the call stack. Without support on the language level, source code would be muddled with tedious error pass-through boilerplate.

Exceptions do not necessarily mean errors, although these words are often used interchangeably. They represent [negative scenarios](https://en.wikipedia.org/wiki/Negative_testing) in an otherwise optimistic execution flow. In a broader sense, they’re exceptional situations defined by a programmer as worthy distinguishing in a particular domain.

Let the syntax guide us in unwinding the exception-handling behavior.

## throw

The `throw` operator emits an exception that stops the normal sequential execution flow of a program, passing control to the exception-handling mechanism.

Common way to trigger an exception:
```php
throw new \Exception;
```

Resist the temptation to ascribe extra significance to the `throw new` expression. Keywords `throw` and `new` belong to independent paradigms (exceptions and OOP, respectively). Exceptions just happen to be object-oriented and `throw` works on exception objects carrying debug information.

Here’s a more verbose equivalent:
```php
$e = new \Exception();
throw $e;
```

Exceptions are often created inline right before use, but that doesn’t have to always be the case. An exception object can be instantiated, passed around, and kept in memory arbitrarily long before being thrown or even discarded.

Exceptions are ordinary objects with properties and methods. They can use inheritance to form hierarchies like other classes. There’s one unique trait: Exceptions must extend a class, such as `\Exception`, that can be thrown.

PHP 7 features `\Throwable` interface implemented by both `\Exception` and `\Error` classes, with the latter replacing the [error-handling mechanism](https://www.php.net/manual/en/book.errorfunc.php).

Whether you can locate a source code that triggered an exception or not, assume there’s a `throw` statement somewhere in there. See official docs on exceptions thrown by internal functions, as you cannot view their sources.

## try..catch

Surrounding code with the `try..catch` block allows to handle exceptions that may be thrown during its execution at any call depth. The blocks can be nested directly or indirectly. Exceptions “bubble up” from their origin to the closest `catch` matching the exception type.

Basic exception-handling syntax:
```php
try {
    doSomethingDangerous();  // throw new \Exception;
} catch (\Exception $e) {
    // ...
}
```

The caught exception can be either handled in full or processed partially and rethrown to give higher-level blocks a chance to participate in its handling. The empty catch block essentially suppresses or “swallows” an exception. Once fully handled, the execution proceeds to the next statement following the `try..catch` resuming the normal control flow of a program.

In the worst-case scenario, the exception remains unhandled and the built-in handler halts the program after outputting the debug information. Think of it as a `try..catch` surrounding the entire program, whose implementation `echo $e; exit(255);` is deliberately hidden from your eyes.

Handling of multiple exception types:
```php
try { 
    doSomethingDangerous();
} catch (\InvalidArgumentException $e) { 
    // ...
} catch (\LogicException $e) { 
    // ...
} catch (\RuntimeException $e) { 
    // ...
}
```

The behavior of multiple `catch` clauses and dependency on declaration order can be understood using the following equivalent transformation:
```php
try { 
    doSomethingDangerous();
} catch (\Exception $e) { 
    if ($e instanceof \InvalidArgumentException) { 
        // ...
    } else if ($e instanceof \LogicException) { 
        // ...
    } else if ($e instanceof \RuntimeException) { 
        // ...
    } else { 
        throw $e; 
    }
}
```

Alternative branches are mutually exclusive following the `else if` analogy. Exceptions of irrelevant types propagate further, as implied by `throw $e`.

The declaration order is important, as the evaluation stops at the first match. Parent-child relations between exception classes are carefully considered. For instance, if the `\LogicException` clause was declared first, it would match the subtype `\InvalidArgumentException`, rendering its specialized handler unreachable. Moving `\RuntimeException` around wouldn’t make a difference in this example, as it’s unrelated to `\LogicException` in the class hierarchy.

It is up to a programmer to decide which types of exceptions to handle on a particular application layer and which ones to pass through. Frameworks impose an error-handling philosophy to guide such decisions.

## Rethrow

Rethrowing a caught exception is fundamentally no different from initially throwing an exception. The syntax is the same, except that `throw` is now called from within `catch`, which does have access to the exception instance. A brand new exception can be thrown instead of rethrowing the caught one.

One question arises, though: Will subsequent `catch` blocks of the same `try` construct that had caught an exception also catch a rethrown one?
```php
try { 
    doSomethingDangerous();
} catch (\InvalidArgumentException $e) { 
    // ...
    throw new \LogicException();
} catch (\LogicException $e) { 
    // Will it catch the exception thrown above? 
    // ...
} catch (\RuntimeException $e) { 
    // ...
}
```

No, they won’t, as evident from the rewritten equivalent:
```php
try { 
    doSomethingDangerous();
} catch (\Exception $e) { 
    if ($e instanceof \InvalidArgumentException) { 
        // ...
        throw new \LogicException(); 
    } else if ($e instanceof \LogicException) { 
        // Will it catch the exception thrown above? Nope 
        // ...
    } else if ($e instanceof \RuntimeException) { 
        // ...
    } else { 
        throw $e; 
    }
}
```

## try..finally

The `try..finally` construct is used to specify the finalization logic to execute regardless of whether an exception occurs or not:
```php
doSomethingSafe();
try { 
    doSomethingDangerous();
} finally { 
    undoSomethingSafe();
}
```

This is equivalent to the following `try..catch`:
```php
doSomethingSafe();
try { 
    doSomethingDangerous(); 
    undoSomethingSafe();
} catch (\Throwable $e) { 
    undoSomethingSafe(); 
    throw $e;
}
```

As you can see, in the general case, the “undo” logic has to be duplicated. However, when a rethrow is not intended, the finalization logic can go after the `try..catch` without resorting to `finally`:
```php
doSomethingSafe();
try { 
    doSomethingDangerous();
} catch (\Throwable $e) { 
    // Handle without throwing exceptions...
}
undoSomethingSafe();
```

## try..finally..return

Besides the copy-paste relief, `finally` is useful in conjunction with `return`.
```php
doSomethingSafe();
try { 
    return getSomethingDangerous();
} finally { 
    undoSomethingSafe();
}
```

This equivalent implementation reveals what’s going on underneath:
```php
doSomethingSafe();
try { 
    $result = getSomethingDangerous(); 
    undoSomethingSafe(); 
    return $result;
} catch (\Throwable $e) { 
    undoSomethingSafe(); 
    throw $e;
}
```

The `finally` version is more concise and doesn’t use an extra variable.

## finally..return

The behavior of `return` inside of `finally` is somewhat unintuitive.
```php
try {
    return getSomethingDangerous();
} finally {
    return 'value returned no matter what';
}
```

The result of the call will always be discarded and any exceptions silenced!

Rewriting the code without `finally` makes it clear that the execution will never reach `return $result` or `throw $e` statements:
```php
try { 
    $result = getSomethingDangerous(); 
    return 'value returned no matter what'; 
    return $result;
} catch (\Throwable $e) { 
    return 'value returned no matter what'; 
    throw $e;
}
```

The `finally..return` construct indicates a coding mistake to be avoided.

---

Pass on the exception-handling insights to your fellow programmers!