Composer 自动加载的原理

当 PHP 引擎试图实例化未知类的操作时,就会调用 __autoload() ,并将类名当做字符串传递给它。但 __autoload() 有个缺点,就是 一个进程中只能定义一次。它是全局唯一的,如果框架占了这个名字,便会导致框架的用户用不了其它的 __autoload() 了,包括用户自定义的和其它类库带的。这时候就可以使用 spl_autoload_register() 函数。它可以把函数注册到 __autoload 队列中。

Version

Composer version 1.5.1

原理

以 Laravel 项目为例,composer.json 文件:

1
2
3
4
5
6
7
8
{
"autoload": {
"classmap" : [
"app/controllers",
"app/models"
]
}
}

当我们执行

1
composer dump-autoload

文件 /vendor/composer/autoload_clasmap.php 会把类名和绝对地址做个映射,返回一个 类名 => 文件地址 这种对应关系的数组。

1
2
3
4
5
6
7
8
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
'Article' => $baseDir . '/app/models/Article.php',
'BaseController' => $baseDir . '/app/controllers/BaseController.php',
'HomeController' => $baseDir . '/app/controllers/HomeController.php',
);

首页 index.php 调用 composer 的 autoload 文件

1
require '../vendor/autoload.php';

会调用 ComposerAutoloaderInit 类的静态方法 getLoader()

1
2
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInita488c85ce5c077906f9c1829b6492f6b::getLoader();

verdor/composer/ 目录下有四个配置文件:

  • autoload_classmap.php 要加载的class映射
  • autoload_files.php 要加载的文件
  • autoload_namespaces.php psr-0标准
  • psr4.php psr-4标准

getLoader() 这个方法会先把这些配置文件放到 ClassLoader 的私有属性中。

然后通过 $loader->register(true) 来调用 spl_autoload_register()

1
2
3
4
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}

spl_autoload_register()loadClass() 方法注册到了 autoload 队列中,然后当PHP引擎试图实例化未知类的操作时,就会根据类名和命名空间加载这个类文件。

1
2
3
4
5
6
7
8
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);

return true;
}
}

classmap

1
2
3
4
5
6
7
{
"autoload": {
"classmap": [
"database"
],
}
}

classmap 做的是类名和文件所在位置的映射

PSR-0 与 PSR-4 的不同

PSR-4 中,在类名中使用下划线没有任何特殊含义。而 PSR-0 则规定类名中的下划线_会被转化成目录分隔符。

PSR-0

1
2
3
4
5
{
"psr-0" : {
"Foo\\": "src/"
}
}

PSR-0 有此配置,那么会去寻找 src/Foo/Bar/Baz.php

PSR-0 当试图自动加载 Foo\A_B 这个class时,会去寻找 src/Foo/A/B.php 这个文件。

PSR-4

1
2
3
4
5
{
"psr-4" : {
"Foo\\": "src/"
}
}

按照 PSR-4 的规则,当试图自动加载 Foo\Bar\Baz 这个 class 时,会去寻找 src/Bar/Baz.php 这个文件,如果它存在则进行加载。

总结

  1. composer 的 PSR-4 带来更简洁的文件结构
  2. 命名空间前缀对应相应的文件夹
  3. 类名和文件名相同,文件后缀为 PHP