The exceptions in PHP are not a new concept. They have been around ever since PHP 5 was released. However, they did not encompass all of PHP's error handling because errors were not considered to be exceptions. PHP, at the time, had two-error handling systems. This made it tricky to deal with, as traditional errors were not catchable via the try...catch blocks exceptions. Certain tricks were possible, where one could have used the set_error_handler() function in order to set a user-defined error handler function, basically listening for errors and turning them into exceptions.
Let's look at the following example:
<?php
class Mailer
{
private $transport;
public function __construct(Transport $transport)
{
$this->transport = $transport;
}
}
$transport = new stdClass();
try {
$mailer = new Mailer($transport);
} catch (\Exception $e) {
echo 'Caught!';
} finally {
echo 'Cleanup!';
}
PHP 5 would not be able to catch this, and instead throws Catchable fatal error, as shown here:
Catchable fatal error: Argument 1 passed to Mailer::__construct() must be an instance of Transport, instance of stdClass given, called in /index.php on line 18 and defined in /index.php on line 6.
By adding the implementation of set_error_handler() before this code, as follows, we could turn that fatal error into an exception:
set_error_handler(function ($errno, $errstr) {
throw new \Exception($errstr, $errno);
});
With the preceding code in place, the try...catch...finally blocks would now kick in as intended. However, there were error types that could not be caught with set_error_handler, such as E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler is called.
The PHP 7 release improved the overall error handling system by introducing the Throwable interface, and moving the errors and exceptions under its umbrella. It is now the base interface for any object that can be thrown via a throw statement. While we cannot extend it directly, we can extend the \Exception and \Error classes. While \Exception is the base class for all PHP and user exceptions, \Error is the base class for all internal PHP errors.
We could now easily rewrite our preceding try...catch...finally block into one of the following:
<?php
// Case 1
try {
$mailer = new Mailer($transport);
} catch (\Throwable $e) {
echo 'Caught!';
} finally {
echo 'Cleanup!';
}
// Case 2
try {
$mailer = new Mailer($transport);
} catch (\Error $e) {
echo 'Caught!';
} finally {
echo 'Cleanup!';
}
Notice the use of \Throwable in the first example catch block. Even though we cannot extend it, we can use it as a shorthand for catching both \Error and \Exception in a single catch statement.
Implementation of \Throwable brings a much needed alignment between errors and exceptions, making them easier to reason with.