继 生命周期的第二篇,大家尽可放心,不会随便鸽文章的
第一篇中,我们提到了入口脚本,也说了,里面注册了自动加载的功能
- 本文默认你有自动加载和命名空间的基础。如果没有请 看此篇文章 php 类的自动加载与命名空间
自动加载机制
php 的自动加载是Loader
类中实现的,这个类在 base.php
中被引入
//base .php
// 载入Loader类
require __DIR__ . '/library/think/Loader.php';
// 注册自动加载
Loader::register();
我们程序在这里执行了 Loader 中静态方法 ,同时这也是一个全部的类register()
我们进入 Loader.php
,按照上面执行顺序看看其核心是什么?
此方法行数过长,我们一点一点来分析
// 注册系统自动加载
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
这就是注册我们的自动加载函数,$autoload
这个变量是传的参数,然而我查找 tp5.1 源码,也没有发现有任何传参的动作。我认为是多余的参数,直接写'think\\Loader::autoload'
就可以了。
如果不了解这个函数的同学,请看文章最顶部的那个连接,上面有详细讲解。
$rootPath = self::getRootPath();
self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR;
// Composer自动加载支持
if (is_dir(self::$composerPath)) {
if (is_file(self::$composerPath . 'autoload_static.php')) {
require self::$composerPath . 'autoload_static.php';
$declaredClass = get_declared_classes();
$composerClass = array_pop($declaredClass);
foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
if (property_exists($composerClass, $attr)) {
self::${$attr} = $composerClass::${$attr};
}
}
} else {
self::registerComposerLoader(self::$composerPath);
}
}
为了支持 composer 拓展,在自动注册时候,把composer 也顺带一起注册了,把composer
目录映射关系存放到 prefixDirsPsr4
这个静态变量中,方便对拓展的调用。
// 注册命名空间定义
self::addNamespace([
'think' => __DIR__,
'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
]);
// 加载类库映射文件
if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
}
// 自动加载extend目录
self::addAutoLoadDir($rootPath . 'extend');
这后面的代码都大同小异,都是把 所需要用到的类,映射到prefixDirsPsr4
这个静态变量中。到时候方便我们使用命名空间进行调用。
register()
函数这里就大概分析结束了。 接下来我们讲一讲autoload()
自动加载
上面我们讲spl_autoload_register
的时候,说他注册了 autoload()
函数,使我们在找不到类的情况下,来调用此函数。
//函数整体内容
public static function autoload($class)
{
if (isset(self::$classAlias[$class])) {
return class_alias(self::$classAlias[$class], $class);
}
if ($file = self::findFile($class)) {
// Win环境严格区分大小写
if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
return false;
}
__include_file($file);
return true;
}
}
我们截取片段一点一点分析。
if (isset(self::$classAlias[$class])) {
return class_alias(self::$classAlias[$class], $class);
}
这一段是判断我们是否是使用了 TP5.1的 静态调用接口(Facade)
,具体解释可以参照官方文档的解释,这个是在 base.php
的末尾注册的
if ($file = self::findFile($class)) {
// Win环境严格区分大小写
if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
return false;
}
__include_file($file);
return true;
}
findFile($class)
主要功能是根据类的名称,配合我们之前存储映射关系的静态数组prefixDirsPsr4
来找寻文件的目录。
如果找到就消除 linux 和 window 对路径名称的差异。(linux 严格区分大小写,而win 没有严格区分)
这里主要是担心在window环境下,路径名称大小写没分,所以我们根据linux的目录规则重写了文件路径
之后再加我们的目录文件
如果没有找到,就报错并写入日志