Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does PHP's spl_autoload_register resolve circular dependencies with require_once?

Tags:

php

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:

  1. The autoloader attempts to load animal.php
  2. Loading animal.php causes cat.php to load due to the require_once()
  3. 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

  1. 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
  2. Animal class should not extend Creature
  3. Instead of using the Autoloader in run.php, just have a require_once() for both animal.php and creature.php

Questions:

  1. Why does #2 work? Why does Animal not extending Creature result in the resolution of the circular dependency between Animal and Cat?
  2. 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

like image 526
Richard Pon Avatar asked Oct 20 '16 00:10

Richard Pon


People also ask

How do you resolve circular dependency when it occurs?

To resolve the circular dependency, you must break the loop by replacing the dynamic reference to the bucket resource.

What are circular dependencies among servers and how can they be avoided?

A circular dependency occurs when two classes depend on each other. For example, class A needs class B, and class B also needs class A. Circular dependencies can arise in Nest between modules and between providers. While circular dependencies should be avoided where possible, you can't always do so.

What is circular dependency problem?

In software engineering, a circular dependency is a relation between two or more modules which either directly or indirectly depend on each other to function properly. Such modules are also known as mutually recursive.

Are all circular dependencies bad?

In my experience, the best way to deal with circular dependencies is to avoid them altogether. Circular dependencies are usually an indication of bad code design, and they should be refactored and removed if at all possible.


1 Answers

Since your autoloader does - what its name says - auto load your classes, you dont need any other require then the one in the autoloader function.

If you use require_once instead of require in it, it will still only load it once, no matter if you extending from it or just create an object.

So just use the code you posted in your question and remove the require_once() in your animal.php since the autoloader already requires it.


Side note: If you dont want to deal with creating your own autoloader, you could use the composer autoloader. Its easy to install and very useful, because it deals with sub directories and makes you follow a strict namespace convention.

If you want to do so, you need to install composer first. Then, you create a file called composer.json in your base directory with following content

{
    "autoload": {
        "psr-4": { "YourProject\\": "src/" }
    }
}

You then need to execute following command in your command line:

cd path/to/your/project
composer dump-autoload

If you have done it put your classes in basedirectory/src Note that you now have to give all classes a namespace, in this case if would be namespace YourProject. You are finally done!

Now go in your base directory and create a file, lets call it index.php:

require_once ('vendor/autoloader.php');

$a = new YourProject\Animal();

Sorry for long side note, sir!

like image 104
Manuel Mannhardt Avatar answered Sep 30 '22 04:09

Manuel Mannhardt