前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >thinkphp6.0 底层源码分析 - 类的自动加载、配置文件初始化

thinkphp6.0 底层源码分析 - 类的自动加载、配置文件初始化

原创
作者头像
stark张宇
发布2023-10-21 07:22:25
3270
发布2023-10-21 07:22:25
举报
文章被收录于专栏:stark张宇stark张宇

概述

因为工作的需要,深入研究了一下thinkphp的源码,也算是对php知识的一个回归,工作这么多年,我一直坚信php是最好的Web编程语言,它可以做到成本和效率的一个平衡,知其然,更要知其所以然才是高手修炼之道

类的自动加载

不管是tp,yaf 还是yii ,所有的php框架都是从自动加载类库文件开始的,如果你不知道如何下手,就打开入口文件,从分析类的自动加载开始。

thinkphp6使用了composer去加载类库,整个composer的实现原理是:首先将各个使用了不同psr规范的类或映射类,以某种形式存储,然后当类找不到的时候,通过与存储的数据匹配,找到类所在的路径,然后去加载。

实际上composer总共有四种规范的文件需要加载,分别是:psr0、psr4、类映射、公共函数文件。

1.使用了单例模式,原理:简化后,psr0,psr4,classmap每个分类对应一个数组,类名在这三个数组进行检索,检索完成后,include

代码语言:txt
复制
public static function getLoader()
{
    if (null !== self::$loader) {
        return self::$loader;
    }
}

2.此处先注册自动加载未定义类,紧跟着注销,是因为只加载并实例化classLoader类,其他类的加载,使用composer提供的方法,而不是自定义的。

代码语言:txt
复制
spl_autoload_register(array('ComposerAutoloaderInit1283bda52466502421173f3a3bffb31b', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit1283bda52466502421173f3a3bffb31b', 'loadClassLoader'));

3.php版本大于5.6且未使用hhvm且没有启用zenGaurd加密扩展,即可使用静态加载,composer install 后,从各个vendor库的composer.json中读取autoload属性。

代码语言:txt
复制
$useStaticLoader = PHP_VERSION_ID >= 50600 &&
!defined('HHVM_VERSION') &&
(!function_exists('zend_loader_file_encoded') ||
!zend_loader_file_encoded());

这里使用到了一个技巧,若对象类的成员属性是private,同时已经实现了set方法,现在需要实现同样的功能,直接复制给private成员属性。若是常用方法是将private属性变成public,或者修改set方法,或添加新的方法。但这里使用了系统类Closure的属性,可以通过bind方法,使用到了目标对象的private属性。

代码语言:txt
复制
public static function getInitializer(ClassLoader $loader)
{
    return \Closure::bind(function () use ($loader) {
        $loader->prefixLengthsPsr4 = ComposerStaticInit1283bda52466502421173f3a3bffb31b::$prefixLengthsPsr4;
        $loader->prefixDirsPsr4 = ComposerStaticInit1283bda52466502421173f3a3bffb31b::$prefixDirsPsr4;
        $loader->fallbackDirsPsr0 = ComposerStaticInit1283bda52466502421173f3a3bffb31b::$fallbackDirsPsr0;
        $loader->classMap = ComposerStaticInit1283bda52466502421173f3a3bffb31b::$classMap;

    }, null, ClassLoader::class);
}

框架初始化执行流程

thinkphp6.0 应用的初始化做了大量的操作,其主要的操作有:加载环境变量、加载配置文件,加载语言包、监听 AppInit、initializers 数组包含的类的初始化。

代码语言:txt
复制
public function run(Request $request = null): Response
{
    //初始化
    $this->initialize();

    //自动创建request对象
    $request = $request ?? $this->app->make('request', [], true);
    $this->app->instance('request', $request);

    try {
        $response = $this->runWithRequest($request);
    } catch (Throwable $e) {
        $this->reportException($e);

        $response = $this->renderException($request, $e);
    }
    return $response;
}

1.加载环境变量

重点强调一下在初始化加载initialize中,和底下的$this->lang->load$this->config->load都是一样的,都是加载对应文件中的数组。

代码语言:txt
复制
// 加载环境变量
if (is_file($this->rootPath . '.env')) {
    $this->env->load($this->rootPath . '.env');
}


object(think\Env)#8 (1) {
  ["data":protected]=>
  array(14) {
    ["APP_DEBUG"]=>
    string(4) "true"
    ["DATABASE_CHARSET"]=>
    string(7) "utf8mb4"
    ["PROJECT_WS_DOMAIN"]=>
    string(15) "wss://127.0.0.1"
  }
}

2.调试模式设置

$this->debugModeInit() 运行原理详见注释,需要注意的是,这里不知道是不是源码中的Bug,!$this->appDebug 恒为true (有时间在做ob缓存和依赖注入的知识点)。

代码语言:txt
复制
protected function debugModeInit(): void
{
    // 应用调试模式
    if (!$this->appDebug) {
        $this->appDebug = $this->env->get('app_debug') ? true : false;
        // 关闭错误显示
        ini_set('display_errors', 'Off');
    }
    // 如果不是命令行模式
    if (!$this->runningInConsole()) {
        // 重新申请一块比较大的buffer
        if (ob_get_level() > 0) {
            // 相当于ob_get_contents() 和 ob_clean()
            // 获取缓冲区内容并删除缓冲区内容
            $output = ob_get_clean();
        }
        // 开启新的缓冲区控制
        ob_start();
        if (!empty($output)) {
            // 由于开启了新的缓冲区控制,
            // 这里不会直接输出$output
            // 而是等到依次执行了ob_flush()和flash()之后才将内容输出到浏览器
            echo $output;
        }
    }
}
  1. 加载应用文件和配置等操作

在加载全局初始化文件的时候,加载是有顺序的,首先加载app目录下的common.php文件和系统下的helper.php文件,然后加载config目录下的所有php文件,最后加载event事件和service服务文件。

代码语言:txt
复制
protected function load(): void
{
    $appPath = $this->getAppPath();
    # 首先加载app目录下的common.php文件和系统下的helper.php文件
    if (is_file($appPath . 'common.php')) {
        include_once $appPath . 'common.php';
    }

    include_once $this->thinkPath . 'helper.php';

    $configPath = $this->getConfigPath();

    $files = [];

    if (is_dir($configPath)) {
        $files = glob($configPath . '*' . $this->configExt);
    }

    foreach ($files as $file) {
        $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
    }
    # 然后加载config目录下的所有php文件
    # 最后加载event事件和service服务文件
    if (is_file($appPath . 'event.php')) {
        $this->loadEvent(include $appPath . 'event.php');
    }

    if (is_file($appPath . 'service.php')) {
        $services = include $appPath . 'service.php';
        foreach ($services as $service) {
            $this->register($service);
        }
    }

}

4.初始化错误和异常处理、注册系统服务和初始化系统服务

最后,初始化错误和异常处理、注册系统服务和初始化系统服务,这几行代码做了比较多的操作:分别实例化包含在里面的类,然后调用其init方法。initializers 数组的值如下:

代码语言:txt
复制
 // 初始化
foreach ($this->initializers as $initializer) {
    $this->make($initializer)->init($this);
}
代码语言:txt
复制
protected $initializers = [
    Error::class,
    RegisterService::class,
    BootService::class,
];

结尾

thinkphp6.0的类的自动加载和初始化就介绍到这里了,知其然,更要知其所以然才是高手修炼之道。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
    • 类的自动加载
      • 框架初始化执行流程
      • 结尾
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
      http://www.vxiaotou.com