ThinkPhp源码分析(自动加载)

in 默认分类技术分享 / 0 评论 / 275阅读

起始准备

安装

composer config -g repo.packagist composer https://packagist.phpcomposer.com
composer  create-project  topthink/think:5.1.20

先来到public/index.php文件

自动加载文件路径
thinkphp/library/think/Loader.php

1.png
可以先引入了,thinkphp/base.php文件,刚进去查看

2.png
此文件引入了thinkphp/library/think/Loader.php,并且调用了自动加载函数,然后就可以分析自动加载类了

自动加载

register静态函数

进入自动加载文件后,找到register静态方法进行分析(一个功能一个功能的分析)

3.png
这里使用了spl_autoload_register这个函数,这个函数就是用来自动加载的,当我们访问一个不存在的类的时候,就会调用这个函数
spl_autoload_register参数介绍

  1. 第一个参数写的是函数名,当访问不存在的类时,spl_autoload_register就会调用这个参数
  2. 第二个参数就是第一个参数指定的函数无法成功注册时, spl_autoload_register()是否抛出异常。
  3. 第三个参数把此函数放到队列的首位,说人话就是,让他优先执行

所以这里的spl_autoload_register函数就是在访问到不存在的类是会调用think\\Loader::autoload这个方法或者是我们指定的方法,因为他第一个参数是个三元运算符,在我们没有指定的自动加载的方法时会使用默认的方法。
剩下的一行是用来获取当前项目的路径,就是你tp项目的路径

4.png

第一行是用来获取Composer的路径的
然后判断是否有thinkphp/vendor/composer/目录,然后没有的话这个功能点就结束了
然后继续判断是否存在autoload_staic.php文件,如果不存在,就会调用registerComposerLoader,此方法会判断存在哪些文件,然后加载对应的文件,如果是空目录的话此功能点结束(具体可以进去此函数分析)
然后回去包含autoload_static.php此文件,进去瞅瞅

5.png
这里有2个静态成员很重要$prefixLengthsPsr4$prefixDirsPsr4后面分析有用,继续回到Loader.php
包含autoload_static.php此文件后,他会执行get_declared_classes函数,用来获取当前所有的类(包括自定义的)返回值是数组,从$declaredClass这个数组里面弹出最后一个元素并且赋值给$composerClass(最后一个元素就是刚刚包含的autoload_staic.php这个类)

6.png

7.png
输出的就是autoload_static.php文件里面的类,然后他会用一个foreach循环数组,然后用property_exists函数来判断该类是否有该成员,如果有的话就创建一个静态成员,并且值是该类成员的值,刚刚看到的autoload_staic.php这个文件只存在数组的前面2个成员,我们可以输出看一下,创建静态变量的值

8.png

9.png

所以他就是那2个类成员的值。
下一个功能

10.png
这里调用了一个静态函数addNamespace此函数用来注册命名空间,传递了1个数组,进去此函数查看

11.png
这里判断他是不是数组,是数组的话就用foreach循环里面的内容,不是数组就直接调用了,因为刚刚都说了传递的参数是数组,然后他就会执行foreach循环,把数组的key变成$perfix,value变成$paths然后把$prefix/字符进行拼接,又调用了一个静态函数addPsr4,在进去查看addPsr4函数

12.png
这里调用的是一个if语句,一般的流程是会进入第二个elseif这里,使用我分析的也是这里,他会先判断,我们的静态成员$prefixDirsPsr4是否存在传入的$prefix(这里拿think\那条数据举例)。$prefixDirsPsr4不存在think\返回False因为前面有个!所以返回True,继续执行。
然后计算$prefix的长度
在判断$prefix的最后一个字符是否是\,是的话向下执行,不是的话抛出异常
然后把think\的长度加入到$prefixLengthsPsr4数组中,加入过程,
例如think\\就变成这样$prefixLengthsPsr4['t']['think\\']=$length
然后在把$paths加入到$prefixDirsPsr4数组中。
加入完成后此功能也执行完毕,我们看看最后$prefixLengthsPsr4$prefixDirsPsr4这2个数组的值

13.png
14.png

下一个功能

15.png
这里他判断是否有runtime/classmap.php这个文件,有的话就包含,没有的话此功能结束,默认是没有的,不过我们可以用php think optimize:autoload生成

16.png
然后看看runtime/classmap.php这个文件

17.png
这里面其实就是一个数组,用来映射类,回到Loader.php
此功能会包含此文件,然后在传入addClassMap静态函数,进去此函数查看作用

18.png
看到他把2个数组合并然后放到静态变量$classMap

最后一个功能

19.png
调运了个静态函数addAutoLoadDir,把extend路径传入,此函数代码就一条

// 注册自动加载类库目录
public static function addAutoLoadDir($path)
{
self::$fallbackDirsPsr4[] = $path;
}

很简单,把$path加入到$fallbackDirsPsr4静态数组中
这就是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;
        }
    }

先会判断$class是否是类的别名,如果是类的别名就返回true继续执行,类的别名在base.php有定义。
然后调用,findFile来寻找对应的类的文件,进入findFile
此方法也一个功能点一个功能点的分析


if (!empty(self::$classMap[$class])) {
    // 类库映射
    return self::$classMap[$class];
}

这里就是看看$classMap是否有映射,如果有映射的话直接访问,所以生成的runtime/classmap.php可以提升点性能。

 // 查找 PSR-4
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';

        $first = $class[0];
        if (isset(self::$prefixLengthsPsr4[$first])) {
            foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
                if (0 === strpos($class, $prefix)) {
                    foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
                        if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
                            return $file;
                        }
                    }
                }
            }
        }

此功能首先进行替换,然后拼接'.php'
获取第一个$class的第一个字母
判断prefixLengthsPsr4是否有这个元素首字母对应的值,没有退出执行下一个功能
然后循环获取对应的keyvalue
然后判断我们的$class的字符串首次在$prefix的字符串首次出现的位置,这里基本上都是返回0,也就是会往下执行
然后在$prefixDirsPsr4寻找对应的目录,判断是否存在此目录,然后返回此文件名
下一个功能

        // 查找 PSR-4 fallback dirs
        foreach (self::$fallbackDirsPsr4 as $dir) {
            if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
                return $file;
            }
        }

这里的$fallback就是extend那个目录的路径,这里直接进行拼接,然后判断是否有该文件,例如我传入的类是test类,那么拼接的路径就是thinkphp\extend\test.php(返回的是绝对路径,我这里用的是相对路径)
其他功能可以自己去查看源码

回复 取消回复