如何解决PHP的spl_autoload_register与require_once循环依赖关系?
循环依赖可以在某些情况下解决,但不是全部。让我们从一个失败的例子开始。假设在不同的文件中定义了三个类:
cat.php
class Cat extends Animal {}
animal.php
require_once('cat.php');
class Animal extends Creature {}
creature.php
class Creature {}
假设我们也有一个脚本,它有一个自动加载器,并创建了一个Animal的实例:
run.php
spl_autoload_register(function($className) {
require_once("$className.php");
});
$a = new Animal();
使用"php run.php"运行此脚本将导致php致命错误:
PHP致命错误:Class 'Animal' not found in…/Cat.php
我认为这对我来说很直观,因为动物和猫之间的循环依赖:
- 自动加载程序试图加载animal.php
- 由于require_once() 加载animal.php会导致cat.php加载
- 加载cat.php失败,因为它扩展了Animal,并且Animal不能被自动加载器加载两次。
这里有一些修改以确保我们不会得到致命的
- animal.php不应该有require_once('cat.php')
- 这似乎是最好的解决方案,因为它有效地消除了动物和猫之间的循环依赖
- 动物类不应该扩展Creature
- 而不是使用自动加载器在run.php,只是有一个require_once()为animal.php和creature.php
问题:
- 为什么#2有效?为什么动物不扩展生物导致解决动物和猫之间的循环依赖关系?
- 为什么#3有效?自动加载器不只是在后台做require_once()吗?
这个例子的完整代码(包括一些额外的日志记录)可以在这里找到
既然你的自动加载器——正如它的名字所说——自动加载你的类,你不需要任何其他的要求,除了在自动加载器函数。
如果你使用require_once而不是require,它仍然只会加载一次,无论你是从它扩展还是仅仅创建一个对象。
所以只要使用你在你的问题中发布的代码,并删除require_once()在你的animal.php,因为自动加载器已经需要它。
旁注:如果你不想自己创建自动加载器,你可以使用composer自动加载器。它易于安装并且非常有用,因为它处理子目录并使您遵循严格的命名空间约定。
如果你想这样做,你需要先安装composer。然后,创建一个名为composer的文件。在您的基本目录中使用以下内容
{
"autoload": {
"psr-4": { "YourProject''": "src/" }
}
}
然后需要在命令行中执行以下命令:
cd path/to/your/project
composer dump-autoload
如果你已经这样做了,把你的类放在basedirectory/src注意,现在必须为所有类提供一个名称空间,在本例中是namespace YourProject
。你终于完成了!
现在进入基本目录并创建一个文件,命名为index.php:
require_once ('vendor/autoloader.php');
$a = new YourProject'Animal();
对不起,先生,我的边注太长了!
从animal.php中删除require_once('cat.php');
如果对象不存在,则调用自动加载器,对不存在的对象单独调用。所以,如果每个对象都有自己的文件名,那就没问题了。
当前的代码流是这样的:
$a = new Animal();
#animal does not exist, spl_autoload('animal');
#include cat.php
#creature does not exist, spl_autoload('creature')
然而,这是通过脚本路径位置。说明cat.php
和animal.php
可能不在同一个目录下,所以该文件可能不存在
然而,对于一个更动态的自动装填系统,我推荐PSR-4自动装填机。它使用名称空间来确定类在目录树中的位置。
new 'path'to'Animal();
将导致自动加载器包含/path/to/animal.php文件
您的示例失败的原因是您试图在定义Animal
之前使用它的定义。
首先,尝试自动加载Animal:
$a = new Animal(); // This triggers the require_once of animal.php
在Animal .php中,在定义Animal类
之前需要cat.phprequire_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.
Animal
类不能在cat.php
中自动加载,因为你已经在它的包含文件上调用了require_once;它只是还没有完全被评估。
正如其他人所说,这是直接调用require_once
与自动加载器混合的结果。作为另一种解决方法,您可以将require_once('cat.php')
移到文件的末尾,它应该像您期望的那样工作。