Yii 类自动加载机制解析(一)

Yii 类的自动加载,依赖于php的 spl_autoload_register(),注册一个自己的自动加载函数,并插入到自动加载函数栈的最前面,确保Yii的autoloader会被最先调用

1、在yii入口文件 index.php 中引入yii本身及第三方的autoloader文件

1
2
3
4
5
6
7
8
9
10
11
12
13
//composer的autoloder
require(__DIR__ . '/../../vendor/autoload.php');
//yii的autoloder
require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/../../common/config/bootstrap.php');
require(__DIR__ . '/../config/bootstrap.php');

$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../common/config/main.php'),
require(__DIR__ . '/../config/main.php')
);

(new yii\web\Application($config))->run();

2、根据入口文件,先查看Yii的autoloder实现(Yii.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
require(__DIR__ . '/BaseYii.php');
class Yii extends \yii\BaseYii
{
}

//此处依赖php的spl_autoload_register(),将Yii类中autoload静态方法注册为自动加载函数
//spl_autoload_register的第三个参数(prepend)为true,所以会将函数注册到SPL __autoload函数队列之首
//因此在入口文件引入autoload文件时,要确保Yii的autoload最后引入

spl_autoload_register(['Yii', 'autoload'], true, true);

//为Yii类中classMap变量赋值,classes.php中包含了yii核心类,类名与对应php文件路径对应关系
//如
//return [
// 'yii\base\Action' => YII2_PATH . '/base/Action.php',
// 'yii\base\ActionEvent' => YII2_PATH . '/base/ActionEvent.php',
// 'yii\base\ActionFilter' => YII2_PATH . '/base/ActionFilter.php',
// 'yii\base\Application' => YII2_PATH . '/base/Application.php',
// ...
//]
Yii::$classMap = require(__DIR__ . '/classes.php');
Yii::$container = new yii\di\Container();

3、接下来查看Yii的autoloder具体实现(\yii\BaseYii中autoloader方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static function autoload($className)
{
//首先查看类名对应的php文件是否已在$className中保存了映射关系
//有的话,再看对应的路径是否为路径别名,是的话转换为真实路径
//没有的话,看类中是否包含 '\',如果没有,则类名不符合规范,直接返回
//如果有 '\',(1)将其转换为‘/’,并加上 .php后缀
//(2)在路径前加上'@'前缀,作为路径别名,进行解析,(所以使用类前,必须注册根别名)

if (isset(static::$classMap[$className])) {
$classFile = static::$classMap[$className];
if ($classFile[0] === '@') {
$classFile = static::getAlias($classFile);
}
} elseif (strpos($className, '\\') !== false) {
$classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false);
if ($classFile === false || !is_file($classFile)) {
return;
}
} else {
return;
}
//使用include加载文件
include($classFile);

if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {
throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?");
}
}