所用到的是TP5.1.41+PHP7.3.4+PhpStorm 2024.2.3
先进入到默认的入口文件(public/index.php)

// [ 应用入口文件 ]
namespace think;
// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';
可以看到第二行代码将Loader类载入,注册自动加载机制功能、错误异常机制、日志接口、注册类库别名。
这些机制中最重要的就是自动加载功能,系统会调用Loader::register()方法注册自动加载,这一步完成后,所有符合模范的类库(包括Composer依赖加载的第三方类库)都将自动加载。
首先需要注册自动加载功能,注册主要由以下几部分组成:
1.注册系统的自动加载方法\think\Loader::autoload
2.注册系统命名空间定义
3.加载类库映射文件(如果有的话)
4.如果存在Composer安装,则注册Composer自动加载
5.注册extend扩展目录
首先需要了解一个方法就是在这个spl_autoload_register
先在根目录下创建一个文件夹然后里面创建一个spl.php的文件里面写入一个不存在的类,当然肯定会报错

假设在创建一个Singwa.php里面写入创建的类其实也是不存在的因为在spl.php找不到Singwa.php里面类的所以运行还是报错
但我们在spl.php写一个spl_autoload_register类就可以执行了
第二个参数(throw)是此参数指定 spl_autoload_register()在无法注册callback时是否应抛出异常。需要注意的是从 PHP 8.0.0 开始,将忽略该参数,如果设置为false,则会发出 notice。spl_autoload_register()现在总是会在无效参数上抛出 TypeError。
第三个参数(prepend)是如果是true,spl_autoload_register()会添加函数到队列之首,而不是队列尾部。
这个的功能是:
1.注册自动加载器:spl_autoload_register("autoload",true,true)注册了autoload函数;
第二个函数true表示注册失败时抛出异常
第三个参数true将该自动加载器添加到队列前端
2.触发自动加载:当执行new Singwa()时,Singwa类未定义,PHP会调用注册的autoload函数,传入类名“Singwa”。
3.自动加载逻辑:
输出调试信息:类名:Singwa
尝试包含文件:./Singwa.php
4.类实例化:若./Singwa.php文件存在且包含有效的Singwa类定义,则成功创建对象并调用say()方法。
4.自动加载Loader深度分析
上面是做了一个例子,下面是tp框架下spl_autoload_register这个类
这里跟上面举得例子是一样的,只不过这里运用的是三元
如果在base.php注册自动加载那里传了参数的的话会执行Loader.php中$autoload这里
如果调用不存在的类的时候才会去调用2,就是think下loader文件中的autoload类,也就是本类
而下面这个方法是获得根目录并赋值给$rootPath,我这里的根目录是myDebugTest
下面是将vendor目录的composer目录赋值给$composerPath变量
然后下面是判断的是否是目录,如果是目录会判断这个目录下是否存在所指向的文件,如果条件都满足的话他会引入这个文件
再往下的话会有一个php内置的一个方法get_declared_classes,它的作用是返回由已定义类的名字所组成的数组,可以进行一个简单的打印看一下,这是一个简单的调试过程![]()
当然打印出来后最下面有个类“Composer\Autoload\ComposerStaticInitc88d1afc9b8a71981ca7ef1be123a495”
其实这个就是刚才获取指定目录里面的类
当然可以做个测试,打印一下之前创建的Singwa类
下面有一个array_pop函数,这个是获取最后一个类的然后将其赋值给$composerClass
在下面还有一个函数property_exists作用是检查对象或类是否具有该属性,也就是$attr这里面定义的属性在没在$composerClass这个类里面,最后查看这个autoload_static.php文件里面仅存在三个属性,那self::${$attr} = $composerClass::${$attr};的属性结构其实就这三个属性
可以做一个断点调试看一下效果
其他两个也可以这样去做不一一做演示,后面还会详细介绍
在这里他会去调用addNamespace这样一个方法
然后去看一下这个调用
这个方法这里也可以做一个断点调试
接下来是一个判断,如果是数组的话,则进行foreach循环然后这里将数组的键作为`$prefix`(命名空间前缀),值作为`$paths`(对应的路径或路径数组)
比如$prefix=think/traits,$paths=这一长串路径
在这个循环体里面调用了一个addPsr4这个方法
在addPsr4这个方法里面不会进行第一个if判断直接进行的进行的是elseif判断,因为在$prefixDirePsr4没有定义think/traits
可以进行输出调试看一下
下面是前缀的格式验证,以及注册数据结构
计算前缀字符串长度,前缀必须以反斜杠\结尾否则报错
注册数据结构中$prefixLengthsPsr4作用是二位映射表,$prefixDirsPsr4的作用是前缀到路径的直接映射
可以打印出来看看
这个时候就相等于$prefixLengthsPsr4的值也就是think和traits的命名空间加载进去了
所以这个注册命名空间定义的主要作用就是将think与traits的命名空间数据放到prefixLengthsPsr4与prefixDirsPsr4属性中

这里可以看到没有这个文件
要如何生成出来了,用一条命令即可php think optimize:autoload
这个其实就是类的映射关系
4.1.4自动加载extend目录
上面介绍目录结构的时候说过这个目录是什么,这个可以放一些拓展类
通过addAutoLoadDir()添加全局类搜索路径,用于加载无命名空间的类文件
4.2.类的别名设置
在上面说过这个自动加载在调用不存在的类的时候会触发autoload这个方法,然后在查询之前去做一个调试看看是否会触发异常处理机制
这里触发了那么逻辑就是对的,可以继续进行调试看一下下面判断里面是在判断什么东西
在这个判断里面的$classAlias其实就是base.php里面的addclassAlias方法,然后通过追踪这个方法就可以找到注册类别名具体的调用
然后通过调试,把$alias里面的数据打印出来看一下,其实就是base.php里面的注册类库别名
往下看判断是数组还是字符串类型:
情况一(数组):将传入的数组与已有的类别名数组合并。
情况二(字符串):直接以 $alias 为键,$class 为值,添加到别名数组中。
继续往下走可以看到以一个class_alias函数,其实这个函数就是返回类的一个别名
简单做一个示范,首先是看一下原始输出没有问题
那改成$a=new Sw();看看效果,这个函数其实就是将Singwa别名设置为Sw,当然也可以将Sw改成其他的不做演示了
所以说return class_alias这一段是将$classAlias[$class]或者说是self::$classAlias[$class]的别名设置为$class,如何去理解呢?
其实就是facede\App::class这个类的别名是App,下面都是一样的
这个了解完之后做个验证,比如说调用Config这个类(因为我这里本地搭建的有问题,所以前面要带具体目录一般情况下直接localhost/index/index/test就好)
使用index模块下的index方法里的test方法
当然既然这是可以利用别名来输出的话可以将上面的使用某具体文件注释,下面的Config前面加一个反斜杠就可以,要不然会认为实在app\insex\controller中,这也是利用命名空间去做,然后Loader.php中找到自动加载那一段,使用var_dump打印出来看看是不是找到的Config这个类

整个深度分析围绕着这个图片所讲解

继续往下走是这样一段,这个是判断返回的值是否是一个文件,这个肯定是成立的因为这个只是赋值,返回的内容赋值给$file,然后就会走下面的逻辑,最后去引入这个文件;
作用:主要用于包含类文件并进行文件名大小写校验(特别是在Windows系统下)。
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方法
跟进之后可以看到里面的主体,有个classMap方法,这个方法之前讲过详细可以看加载类库映射文件章节,其实就是类对应到哪个目录的映射关系存储,它存储在了runtime目录下的classmap.php里面
如果这个存在的话他就直接返回这个文件,可以做个调试打印看一下
if (!empty(self::$classMap[$class])) {
// 类库映射
return self::$classMap[$class];
}
然后在往下走之前,把这个classmap.php文件删掉,不让他走这个逻辑了
因为删掉之后他就会走下面的逻辑也就是查找PSR-4,在走下面这个逻辑的时候可以打个断点调试一下看看输出什么
上面可以看到输出的是think目录下的error.php,那$class就是error
下面正常走,$first就是一个t
在下面打个断点就可以清楚地看到判断里面要的值是什么,就是以t为命名空间的这几个
后面不做一一详解,他的主要作用就是根据给定的类名($class)来查找对应的文件路径并返回,在之前讲过的那些主要的目录里面拿到类库文件,而下面这个代码和上面的大同小异,是在extend这个目录里拿到类库文件
foreach (self::$fallbackDirsPsr4 as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}最后一段是查找PSR-0以及PSR-0 fallback dirs,这个其实和上面的是一样的
首先说一下路径生成的逻辑
PSR-0的规范:
1.所有命名空间分隔符\转换为目录分隔符
2.所有类名中的下划线_转换为目录分隔符,
3.必须添加.php后缀
但在这里面他做了一些小优化
1.重用$logicalPathPsr4(PSR-4路径)避免重复计算
2.strrpos($class,‘\\’)找到最后一个命名空间分隔符位置
3.strtr()性能更高(单字节替换)
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
}然后是前缀查找机制
首字母索引$first减少90%+的无效遍历(基准测试数据)
前缀长度排序:内部实现会按前缀长度降序排序,有限匹配更长前缀
if (isset(self::$prefixesPsr0[$first])) {
foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}最后一个就是PSR-0后备目录
兼容无命名空间的遗留代码
处理未通过composer.json注册的第三方库
开发环境快速加载工具类
避免重复查找不存在的类
然后返回类库文件
foreach (self::$fallbackDirsPsr0 as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
return self::$classMap[$class] = false;首先在根目录下创建一个singwa目录然后在这个目录下创建一个ali目录在这个目录创建一个Send.php在文件中写好内容
然后在application->index->controller->index.php中写入要调用的类,当然要使用的命名空间不能少
访问这个类的时候可以看到它是有报错的
报错的原因是因为找不到Send这个类,可以结合之前讲过的类自动加载机制,进行一个更改
因为在自动加载备选目录的时候只有extend目录,我们只需要模仿写一个singwa的就可以,因为singwa是自定义的
还有一个情况就是在thinkphp->library中创建一个自定义目录然后创建一个自定义文件他的加载有两种方法
方法一:创建命名空间
Send.php的内容以及index.php的内容不动,改的是Loader.php的注册命名空间那
方法二:修改自动加载extend目录
还是跟刚才的自定义目录加载那一样只不过要调用的比较多,获取一个根目录->下一级目录->自定义目录

这个主要是针对通过composer来安装类库的自动加载
情况一:通过cpmposer安装
情况二:不想安装直接复制粘贴
下载过来之后
比如说使用time.php这个类库,调用Time类里的today方法,不光要在index.php里面设置,还要设置映射
