Chained Autoloaders and Exceptions

spl_autoload_register() can be used to register one or more functions that are called – one after another, in the order they were registered – when a class, interface, or trait is used that has not been declared yet. The purpose of such an autoload function is to load the respective sourcecode file.

Prior to PHP 7.3, PHP would execute all registered autoloader functions until the class, interface or trait in question is available: even if one or more of the registered autoloader functions raised an exception:

spl_autoload_register(
    function (string $type): void
    {
        throw new Exception('Cannot load ' . $type);
    }
);

spl_autoload_register(
    function (string $type): void
    {
        throw new Exception('Cannot load ' . $type);
    }
);

new Example;

Executing the code shown above with PHP 7.2 and earlier prints the output shown below:

Fatal error: Uncaught Exception: Cannot load Example in example.php:5
Stack trace:
#0 [internal function]: {closure}('Example')
#1 example.php(16): spl_autoload_call('Example')
#2 {main}

Next Exception: Cannot load Example in example.php:12
Stack trace:
#0 [internal function]: {closure}('Example')
#1 example.php(16): spl_autoload_call('Example')
#2 {main}
  thrown in example.php on line 12

As you can see, the exception raised by the second autoloader function was chained to the one raised by the first autoloader function.

Executing the same code with PHP 7.3 and later prints the output shown below:

Fatal error: Uncaught Exception: Cannot load Example in example.php:5
Stack trace:
#0 [internal function]: {closure}('Example')
#1 example(16): spl_autoload_call('Example')
#2 {main}
  thrown in /home/sb/test.php on line 5

PHP 7.3 and later stop calling autoloader functions as soon as an exception is raised. This means that raising an exception in an autoloader function prevents the execution of autoloader functions that come later in the sequence.