TP5.1 源码窥探之类的自动加载机制
由于受到疫情影响,小智典营销,线下课预售暂停一段时间,本来推荐使用微信直播的功能,但由于领导层强烈推荐使用第三方工具小鹅通,导致项目暂缓,仅仅对于线上业务进行开发维护。那就趁有时间学习一下新知识。然而学习什么新知识不重要,最重要的就是不要不学习。我也很迷茫,不知道先学什么,总是,这也想学,那也想学,然而到最后还是在原地踏步,痛定思痛,我先定一个计划,说学什么就规定在一段时间内就学这个,其他的先不管。学完一个再说。之前面试的时候,经常有人问,有研究过某个框架的源代码没。那就先从研究框架的源代码吧。由于最近的项目是用TP5.1.39开发的,那就研究tp5.1吧。
TP5.1中类的自动加载机制
首先进入项目入口文件
前提是你安装了tp5.1.39
,composer create-project topthink/think=5.1.* tp5
public\index.php
//1、定义命名空间
namespace think;
//2、包含并运行base.php文件
require __DIR__ . '/../thinkphp/base.php';
//3、执行应用并响应
Container::get('app')->run()->send();
进入base.php文件,看看都做了哪些操作
1、引入Loader
类,需要注意的是,仅仅是引入类,并没有执行什么。
2、执行Loader::register()
方法,注册自动加载机制
我们进入该方法,首先利用
spl_autoload_register
注册系统自动加载机制,含义是:系统在new一个对象的时候,发现类不存在时,就是调用Loader::autoload
的静态方法去引入加载对应的类然后获取项目的根目录,设置项目的composer的目录,引入
autoload_static.php
文件,然后获取已经声明的类文件,并弹出最后一个类名,此类名就是autoload_static.php
文件中声明的类,并将此类的属性赋值给Loader类的属性,可以把此步骤理解为注册app
,think\composer
命名空间以及对应的目录然后再注册
think
,traits
命名空间以及对应的目录然后注册类名映射,
runtime/classmap.php
,此类文件是由后期优化生成的,php think optimize:autoload
然后注册自动加载目录
extend
3、然后执行Error::register()
方法
由于
think\Error
类并没有事先引入,所以系统调用上一步注册的系统自动加载机制,执行Loader::autoload
方法,此时参数为think\Error
,进入autoload
方法,首先查看类库别名中是否有此类名,如果有的话,就使用class_alias
函数起别名,并自动加载。如果不存在类库,那就根据类名去查找文件,也就是执行findFile
方法,首先查看是否存在类库映射(classMap),如果存在就返回文件目录地址,不存在的话,就查找psr-4并返回文件目录地址,如果找不到就继续查找psr-4自动加载目录,如果再找不到就查找psr-0规范的文件目录,如果再差找不到,就查找psr-0自动加载目录,返回文件目录地址,最后引入该文件然后执行
register
方法,注册异常处理
4、注册类库别名,Loader::addClassAlias
,方便autoload
Loader::addClassAlias([
'App' => facade\App::class, // 对应的类文件目录 => thinkphp\library\think\facade\App.php
'Build' => facade\Build::class,
'Cache' => facade\Cache::class,
'Config' => facade\Config::class,
'Cookie' => facade\Cookie::class,
'Db' => Db::class,
'Debug' => facade\Debug::class,
'Env' => facade\Env::class,
'Facade' => Facade::class,
'Hook' => facade\Hook::class,
'Lang' => facade\Lang::class,
'Log' => facade\Log::class,
'Request' => facade\Request::class,
'Response' => facade\Response::class,
'Route' => facade\Route::class,
'Session' => facade\Session::class,
'Url' => facade\Url::class,
'Validate' => facade\Validate::class,
'View' => facade\View::class,
]);
扩展
1、由于autoload_static.php
文件中的类名一直在变化,我们无法得到固定的类名,怎么做才能将此类的属性与Loader的属性合并?
//主要使用两个函数 get_declared_classes property_exists
//获取类名
$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};
}
}
2、为什么使用命名空间映射?
节省资源,方便调用,避免每次循环查找
3、PSR4 标准顶级命名空间映射数组,用了两个数组,第一个是用命名空间第一个字母作为前缀索引,然后是 顶级命名空间,但是最终并不是文件路径,而是 顶级命名空间的长度。为什么呢?
因为 PSR4 标准是用顶级命名空间目录替换顶级命名空间,所以获得顶级命名空间的长度很重要,与Loader::findFile
方法中字符串截取拼接类文件所在位置有用到length
4、如果需要增加一个新的命名空间,或者新增一个自动加载目录,你会怎么做?
主要是使用方法Load::addNamespace
和Loader::addAutoLoadDir
,在Loader
内部外部都可以,但为了不修改源代码,建议在入口文件调用添加
5、类库别名class_alias
eg.直接使用Config
类的话,自动加载机制会直接调用Loader::$classAlias
属性中对应键名的键值类库
namespace app\index\controller;
use Config;//此时的Config类,在编辑器中鼠标点击是跳转不了的
class Index
{
public function index()
{
$appConfig = Config::get('app.');
var_dump($appConfig);
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: