How does PHP's spl_autoload_register resolve circular dependencies with require_once?
Circular dependencies can be resolved some cases, but not all. Let's start with an example of when it fails. Suppose we have three classes defined in separate files:
cat.php
class Cat extends Animal {}
animal.php
require_once('cat.php');
class Animal extends Creature {}
creature.php
class Creature {}
Let's say we also have a script that has an autoloader and creates an instance of Animal:
run.php
spl_autoload_register(function($className) {
require_once("$className.php");
});
$a = new Animal();
Running this script with "php run.php" will result in a PHP Fatal error:
PHP Fatal error: Class 'Animal' not found in .../Cat.php
I think this makes intuitive sense to me, because of the circular dependency between Animal and Cat:
- The autoloader attempts to load animal.php
- Loading animal.php causes cat.php to load due to the require_once()
- Loading cat.php fails becasue it extends Animal, and Animal can't be loaded twice by the autoloader.
Here are some modifications to ensure that we don't get a fatal
- animal.php should not have a require_once('cat.php')
- This seems like the best solution as it effectively removes the circular dependency between Animal and Cat
- Animal class should not extend Creature
- Instead of using the Autoloader in run.php, just have a require_once() for both animal.php and creature.php
Questions:
- Why does #2 work? Why does Animal not extending Creature result in the resolution of the circular dependency between Animal and Cat?
- Why does #3 work? Isn't the autoloader just doing a require_once() under the hood?
The complete code (with some additional logging) from this examples can be found here