PHP的spl_autoload_register如何用require_once解析循环依赖项?

时间:2022-08-23 08:01:26

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

PHP的spl_autoload_register如何用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

cat.php

class Cat extends Animal {}

animal.php

animal.php

require_once('cat.php');
class Animal extends Creature {}

creature.php

creature.php

class Creature {}

Let's say we also have a script that has an autoloader and creates an instance of Animal:

假设我们还有一个脚本,它有一个自动阅读器并创建了一个动物实例:

run.php

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运行”运行此脚本。php“将导致php致命错误:

PHP Fatal error: Class 'Animal' not found in .../Cat.php

PHP致命错误:类“Animal”在…/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. autoloader尝试载入动画。php
  3. Loading animal.php causes cat.php to load due to the require_once()
  4. 加载的动物。php引起猫。由于require_once()而加载的php
  5. Loading cat.php fails becasue it extends Animal, and Animal can't be loaded twice by the autoloader.
  6. 加载的猫。php失败是因为它扩展了动物,并且动物不能被自动加载器加载两次。

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. 的动物。php不应该有一个require_once(' Cat .php'),这似乎是最好的解决方案,因为它有效地消除了动物和猫之间的循环依赖关系
  3. Animal class should not extend Creature
  4. 动物等级不允许延伸生物
  5. Instead of using the Autoloader in run.php, just have a require_once() for both animal.php and creature.php
  6. 而不是在运行中使用自动阅读器。php,对于这两个动物都有一个require_once()。php和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. 为什么# 2的工作吗?为什么动物不扩展生物导致动物和猫之间的圆形依赖关系的解决?
  3. Why does #3 work? Isn't the autoloader just doing a require_once() under the hood?
  4. 为什么3号工作吗?autoloader不是只是在引擎盖下面做一个require_once()吗?

The complete code (with some additional logging) from this examples can be found here

可以在这里找到这个示例的完整代码(以及一些附加日志记录)

3 个解决方案

#1


3  

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.

如果您使用require_once而不是require,那么无论您是扩展它还是创建一个对象,它仍然只加载一次。

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.

因此,只需使用您在问题中发布的代码,并在您的动物中删除require_once()。自autoloader已经需要它了。


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

如果您想这样做,您需要先安装composer。然后,创建一个名为composer的文件。json位于基本目录中,包含以下内容

{
    "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!

如果您已经将您的类放在basedirectory/src中,那么您现在必须为所有类提供一个名称空间,在这种情况下,如果是名称空间您的项目。你终于完成了!

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

现在进入你的基本目录并创建一个文件,让我们叫它index.php:

require_once ('vendor/autoloader.php');

$a = new YourProject\Animal();

Sorry for long side note, sir!

不好意思,先生!

#2


0  

Remove require_once('cat.php'); from animal.php

删除require_once(“cat.php”);从animal.php

If an object does not exist, the autoloader is called making a separate call each object that does not exist. So if every object has its own file name you are alright.

如果一个对象不存在,那么autoloader就会被调用单独调用不存在的对象。如果每个对象都有自己的文件名,那就没问题了。

The codeflow currently go's a little like this:

现在的代码流有点像这样:

$a = new Animal();

#animal does not exist, spl_autoload('animal');
#include cat.php
#creature does not exist, spl_autoload('creature')

However, this go's by script path location. Meaning cat.php is perhaps not in the same directory as animal.php, thats why the file may not exist.

然而,这是由脚本路径位置决定的。意义的猫。php可能与animal不在同一个目录中。php,这就是为什么文件可能不存在。

However, for a more dynamic autoloading system I recommend the the PSR-4 autoloader. It uses namespaces to determine the location of the class within the directory tree.

然而,对于更动态的自动加载系统,我推荐PSR-4自动加载器。它使用名称空间来确定目录树中类的位置。

new \path\to\Animal();

Would cause the auto loader include the file of /path/to/animal.php

会导致自动加载器包含/path/to/ anim. php文件吗?

#3


0  

The reason your example is failing is that you're attempting to use the definition of Animal before it's defined.

您的例子失败的原因是您试图在动物定义之前使用它的定义。

First, you attempt to autoload Animal:

首先,你试着自动控制动物:

$a = new Animal(); // This triggers the require_once of animal.php

In animal.php, you require cat.php before you define the Animal class

在动物。php,您需要的猫。在定义动物类之前

require_once('cat.php');    // This file will be fully evaluated before the next line
class Animal extends Creature {}  // Since the prior line relied on the existence of Animal, you get a compile error.

The Animal class can't be autoloaded inside cat.php, since you already called require_once on its containing file; it just hasn't fully been evaluated.

在猫的体内,动物类是不能被自动控制的。php,因为您已经在其包含的文件上调用了require_once;只是还没有完全评估。

As others have said, this is a result of mixing direct require_once calls with an autoloader. As another workaround, you could move your require_once('cat.php') to the end of the file, and it should work as you're expecting it to.

正如其他人所说,这是将直接的require_once调用与autoloader混合的结果。作为另一个解决方案,您可以将require_once('cat.php')移动到文件的末尾,它应该像您期望的那样工作。

#1


3  

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.

如果您使用require_once而不是require,那么无论您是扩展它还是创建一个对象,它仍然只加载一次。

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.

因此,只需使用您在问题中发布的代码,并在您的动物中删除require_once()。自autoloader已经需要它了。


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

如果您想这样做,您需要先安装composer。然后,创建一个名为composer的文件。json位于基本目录中,包含以下内容

{
    "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!

如果您已经将您的类放在basedirectory/src中,那么您现在必须为所有类提供一个名称空间,在这种情况下,如果是名称空间您的项目。你终于完成了!

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

现在进入你的基本目录并创建一个文件,让我们叫它index.php:

require_once ('vendor/autoloader.php');

$a = new YourProject\Animal();

Sorry for long side note, sir!

不好意思,先生!

#2


0  

Remove require_once('cat.php'); from animal.php

删除require_once(“cat.php”);从animal.php

If an object does not exist, the autoloader is called making a separate call each object that does not exist. So if every object has its own file name you are alright.

如果一个对象不存在,那么autoloader就会被调用单独调用不存在的对象。如果每个对象都有自己的文件名,那就没问题了。

The codeflow currently go's a little like this:

现在的代码流有点像这样:

$a = new Animal();

#animal does not exist, spl_autoload('animal');
#include cat.php
#creature does not exist, spl_autoload('creature')

However, this go's by script path location. Meaning cat.php is perhaps not in the same directory as animal.php, thats why the file may not exist.

然而,这是由脚本路径位置决定的。意义的猫。php可能与animal不在同一个目录中。php,这就是为什么文件可能不存在。

However, for a more dynamic autoloading system I recommend the the PSR-4 autoloader. It uses namespaces to determine the location of the class within the directory tree.

然而,对于更动态的自动加载系统,我推荐PSR-4自动加载器。它使用名称空间来确定目录树中类的位置。

new \path\to\Animal();

Would cause the auto loader include the file of /path/to/animal.php

会导致自动加载器包含/path/to/ anim. php文件吗?

#3


0  

The reason your example is failing is that you're attempting to use the definition of Animal before it's defined.

您的例子失败的原因是您试图在动物定义之前使用它的定义。

First, you attempt to autoload Animal:

首先,你试着自动控制动物:

$a = new Animal(); // This triggers the require_once of animal.php

In animal.php, you require cat.php before you define the Animal class

在动物。php,您需要的猫。在定义动物类之前

require_once('cat.php');    // This file will be fully evaluated before the next line
class Animal extends Creature {}  // Since the prior line relied on the existence of Animal, you get a compile error.

The Animal class can't be autoloaded inside cat.php, since you already called require_once on its containing file; it just hasn't fully been evaluated.

在猫的体内,动物类是不能被自动控制的。php,因为您已经在其包含的文件上调用了require_once;只是还没有完全评估。

As others have said, this is a result of mixing direct require_once calls with an autoloader. As another workaround, you could move your require_once('cat.php') to the end of the file, and it should work as you're expecting it to.

正如其他人所说,这是将直接的require_once调用与autoloader混合的结果。作为另一个解决方案,您可以将require_once('cat.php')移动到文件的末尾,它应该像您期望的那样工作。