分析ThinkPHP5的源码(1) : 类的自动加载

来源:csdn博客 分类: 文章浏览史 发布时间:2023-03-24 22:44:03 最后更新:2023-03-24 浏览:159
转载声明:
本文为摘录自“csdn博客”,版权归原作者所有。
温馨提示:
为了更好的体验,请点击原文链接进行浏览
摘录时间:
2023-03-24 22:44:03

前文

Composer 下载ThinkPHP5.1的源码,每个框架它都必须都有一个“类的自动加载”机制 ,我们都知道PHP引入文件是需要requireinclude 才能使用别的类文件中的方法。

比如我需要写一个公共文件 model.php

include "model.php"
include "model2.php"

class User {
 //code..
}

但是!如何当公共类库文件很多的时候,每次都需要手动引入,就显得非常麻烦,不利于/不方便维护管理。
所以PHP引入了一个spl_autoload_register() 的类自动加载,TP框架就是借助了 spl_autoload_register() 来完成类的自动加载。

TP5框架入口加载机制

学习框架源码的第一步,先找到入口文件 public/index.php ,然后一步步跟进流程,看下代码执行的过程。
在这里插入图片描述

  • 第一行:定义命名空间就不赘述了。。
  • 第二行:去加载基础文件 base.php ,是位于上一层目录的 think目录下

打开Base.php ,第16行就会去加载 Loader.php 文件 (就是TP5自动加载的类库) ,Loader.php 是TP5封装的底层基础类库。
在这里插入图片描述
再引用了核心文件 Loader.php

调用类库的 Loader::register() 的方法

在这里插入图片描述

发现都用了spl_autoload_register()的系统函数,其他框架也是同理。
都是会在框架的第一步“入门文件”就进行类的自动加载机制,针对底层进行封装。

TP5中的Loader::register() 其实做了2件事:

  • 内部自定义一个autoload() 去进行框架的底层深度封装
  • 为了支持 Composer 自动加载 安装第三方的类库插件(Vendor),都是遵循PSR-4的风格统一,那么加载composer类的 autoload_static.php 后,再去加载对应的插件类库。

分析Loader::register的执行流程

如下截图:
在这里插入图片描述

  1. 分析TP5执行 Loader::register() 的注册自动加载方法 ,( 是调用不存在的类的时候才会执行spl_autoload_register()

  2. 运用3元运算符,如果有参数就执行其他自定义类,没就 加载本类的autoload() 方法

  3. 调用 self::gerRootPath() 获取项目的根目录

  4. 这里就是走 Composer的加载机制,然后组织一个Vendor的决定路径 ,self::$composerPath

  5. 判断是否有Vendor的目录,再判断是否有auto_static的文件 ,有就加载composer目录下的auto_static.php

分析Composer自动加载——类文件

在逻辑往下走,判断是否有Vendor的目录,再判断是否有auto_static的文件 ,有就加载composer目录下的auto_static.php

auto_static.php

发现定义了2个属性分别是: $prefixLengthsPsr4$prefixDirsPsr4 ,这是什么意思呢?

在这里插入图片描述
定义数组,有个key和value,比如 $prefixLengthsPsr4 (PSR4的长度)

  • t key代表一个命名空间 ,代表think\composer\ 命名空间 ,首字母代表类,把某些类放进去来,15 代表字符的长度。
  • a key也是代表一个命名空间,代表:app ,用首字母代表,把某些类放进去, 4是这个字符的长度。

2个斜线是转义字符,比如 think\\composer\\ 等同于 think\composer\

$prefixDirsPsr4 把每个命名空间的类对应的目录列出来

  • 比如think\composer 这个命名空间 在src下
  • app\\ 这个命名空间就在根目录的application
    在这里插入图片描述
    比如通过 Compsoe r安装 swoole、workermam、phpexcel 就把相应的规则追加数组上 填进来。

总结: composer的统一加载机制的地方,每次composer新类库的时候,都会往这个属性追加 命名空间 以及类库的目录路径

分析Composer自动加载——属性赋值

回到Loader::register()方法。

// 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);
            }
        }

        // 注册命名空间定义
        self::addNamespace([
            'think'  => __DIR__,
            'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
        ]);

再返回 Loader::register() 方法接着放下分析:

如果存在Vendor目录,再判断是否有 composer_static.php ,有就执行如下:
在这里插入图片描述
执行了 get_declared_classes 这个系统函数,返回由当前脚本中已定义类的名字组成的数组。
在这里插入图片描述
我们打印这个get_declared_classes
在这里插入图片描述
得到目前所有加载的类,最后require加载的是 composer类

所以,我们上面的 array_pop 弹出最后一个元素,赋值后的 $composerClass ,就是composer的类。
在这里插入图片描述
在这里插入图片描述
这个命令空间那么长的其实就是 composer目录下的autoload_static.php

再继续往下执行
在这里插入图片描述

property_exists : 检查对象或类是否具有该属性

我们知道autoload_static.php 存在有

  • prefixLengthsPsr4
  • $prefixDirsPsr4
  • $classMap

等一众的方法,composer的 stacis.php这些属性以及值 ,再赋值作为Loader类的某个属性再处理。

上面的写法等同 :

self::prefixLengthsPsr4
self::prefixDirsPsr4

打印这些属性返回:
在这里插入图片描述
为什么这样去做?后续要集合多个属性再Map,加载文件的时候用这些属性。

总结: 把composere下的auto_static的PSR4的属性以及值赋值到Loader的PSR4属性中

分析Composer自动加载——注册命名空间

  1. 会调用当前Loader::addNamespace() 方法 ,把当前thinktraits 核心注册进行。
 // 注册命名空间定义
self::addNamespace([
     'think'  => __DIR__,
     'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
]);
  1. 进去 addNamespace() 方法,我们打印这个 $namespace 参数。
// 注册命名空间
    public static function addNamespace($namespace, $path = '')
    {

        echo "<pre>";
        print_r($namespace);exit;

        if (is_array($namespace)) {
            foreach ($namespace as $prefix => $paths) {
                self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true);
            }
        } else {
            self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true);
        }
    }

在这里插入图片描述
3. 再接下来,无论是不是数组,都再把命名空间拼接组装好,再调用self::addPsr4()
4. 查看 addPsr4() 方法
在这里插入图片描述
注册的时候走的是“296” 行 。

因为之前 self::$prefixDirsPsr4 里的属性只是composer的autp_static.php 上的,只有 think\ 跟 app\ ,并没有thinktraits 这2个属性。

 public static $prefixDirsPsr4 = array (
        'think\\composer\\' => 
        array (
            0 => __DIR__ . '/..' . '/topthink/think-installer/src',
        ),
        'app\\' => 
        array (
            0 => __DIR__ . '/../..' . '/application',
        ),
    );

在这里插入图片描述
6. 这样就完成 think、traits下的命名空间注册到Loader类的PSR4属性中。

加载类库映射文件

再往下执行:
在这里插入图片描述

  1. 我们在根目录的runtime 并没有classmap.php的文件 ,我们可以通过命令生成这个文件
php think optimize:autoload
  1. 这时候runtime下就有classmap.php ,就是存放一些命名空间映射的文件
<?php
/**
 * 类库映射
 */

return [
    'app\\index\\controller\\Index' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/application/' . 'index/controller/Index.php',
    'think\\App' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/App.php',
    'think\\Build' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Build.php',
    'think\\Cache' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Cache.php',
    'think\\Collection' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Collection.php',
    'think\\Config' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Config.php',
    'think\\Console' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Console.php',
    'think\\Container' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Container.php',
   //code.....
]
  1. 进入这个 self::addClassMap() 方法,看到把当前classmap.php的数组内容赋值到一个 Loader类的**$classMap** 属性中。

总结: 把tp5的核心的 think traits 命名空间注册到PSR4里,方便后续调用,实在属性多个数组统一自动加载。

自动加载extend目录

再往下执行
在这里插入图片描述
进去 self::addAutoLoadDir() 方法:

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

把当前extend的目录也加载到 Loader类 $fallbackDirsPsr4 的属性中。

总结

  • prefixDirsPsr4 (对应类的目录)
  • prefixLengthsPsr4 (对应类的命名长度)
  • fallbackDirsPsr4 (对应extend的目录)

我们拿到很多变量成员、命令空间、目录路径等都赋值到 Loader类的多个PSR4的属性中。

后续再拿这些属性做自动加载配置。

php技术微信