ThinkPHP框架执行流程源码解析(2)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: ThinkPHP框架执行流程源码解析

此时就会有一个问题,这个init方法明显是被调用了俩次的,那么另一次调用的地方是在哪里呢!


如果在不知道新的技巧之前,就会进行一系列的断点打印,看在哪里进行了执行,比如在这个init的上层去打印。


也就是在initialize那个方法里边去打印做断点,但是这样很是麻烦的,而且很有可能浪费了大量的时间还是找不到正确的地方。


小技巧之debug_backtrace()


这个方法会产生一条回溯追踪,会显示出一个方法所有的调用位置。


使用方式就是如下图,只需要把debug_backtrace这个方法打印出来即可。


image.png

image.png

image.png




根据得到的数据信息,就可以非常快的进行定位。


第一次就是在app类的215行。


image.png


第二次是在thinkphp/library/think/route/dispatch/Module.php类的60行


image.png


可以在这里做一个打印,看一下这个module是否为index


image.png

image.png


所以说有了这个方法就可以非常快速地定位调用位置。

三、框架执行流程之初始化应用init分析

上文给大家提供了一个小技巧debug_backtrace实战演示了如何查看一个方法都在哪里执行的。


并且案例也是使用的init这个方法来演示的,因为接下来就是要对init这个方法进行深入的了解。


在init方法里边主要做的事情在上边的脑图已经描述的很清楚了。


从一开始就对模块的定位,就是在第二节中的对init方法的调用,会传入对应的模块

加载app目录下的tags文件,在tags文件里边就是对行为扩展定义的文件。在之前门面的文章中定义钩子执行就在这个文件中设置的。

加载common文件,也就是公共文件,所以说公共文件就是在这里进行加载的。

加载助手函数文件helper,在助手函数里边有一个大家特别熟悉的一个方法,那就是dump。这就是为什么在有的地方使用dump会报错的原因。

加载中间件文件,这里的直接给出的是直接加载app目录下的中间件文件,但是在框架中我们需要在定义一个目录为http,在这个目录下定义中间件文件。

注册服务的容器对象实例,这里注册就使用的是容器类中的bindTo方法进行绑定注册的。

读取配置文件,这段在配置文件加载那一节中已经进行深入的说明了, 这里就不提了。配置文件会读取俩个地方一个是第一步模块下的config文件,另一个就是config目录下的配置文件。

设置模块路径,会把第一步获取到的模块进行env环境变量配置里边

最后一步就是对容器中的对象实例进行配置更新,具体更新了什么在后文中给大家详细说来。

    /**
     * 初始化应用或模块
     * @access public
     * @param  string $module 模块名
     * @return void
     */
    public function init($module = '')
    {
        // 定位模块目录
        $module = $module ? $module . DIRECTORY_SEPARATOR : '';
        /**
         * 第一次:D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\
         * 第二次:D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\index\
         */
        $path   = $this->appPath . $module;
        // 加载初始化文件
        if (is_file($path . 'init.php')) {
            include $path . 'init.php';
        } elseif (is_file($this->runtimePath . $module . 'init.php')) {
            include $this->runtimePath . $module . 'init.php';
        } else {
            // 加载行为扩展文件
            if (is_file($path . 'tags.php')) {
                $tags = include $path . 'tags.php';
                if (is_array($tags)) {
                    $this->hook->import($tags);
                }
            }
            // 加载公共文件
            if (is_file($path . 'common.php')) {
                include_once $path . 'common.php';
            }
            if ('' == $module) {
                // 加载系统助手函数
                include $this->thinkPath . 'helper.php';
            }
            // 加载中间件
            if (is_file($path . 'middleware.php')) {
                $middleware = include $path . 'middleware.php';
                if (is_array($middleware)) {
                    $this->middleware->import($middleware);
                }
            }
            // 注册服务的容器对象实例
            if (is_file($path . 'provider.php')) {
                $provider = include $path . 'provider.php';
                if (is_array($provider)) {
                    $this->bindTo($provider);
                }
            }
            /**
             * $path : "D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\"
             *          "D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\index\"
             */
            // 自动读取配置文件
            if (is_dir($path . 'config')) {
                $dir = $path . 'config' . DIRECTORY_SEPARATOR;
            } elseif (is_dir($this->configPath . $module)) {
                // D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\config\
                $dir = $this->configPath . $module;
            }
            // scandir:以升序的方式读取目录中的文件
            // 返回就是config目录中的所有文件
            $files = isset($dir) ? scandir($dir) : [];
            foreach ($files as $file) {
                /**
                 * $this->configExt:配置文件的后缀
                 * pathinfo返回的是文件后缀,关于pathinfo共有三个可选的参数PATHINFO_DIRNAME、PATHINFO_BASENAME、PATHINFO_EXTENSION,分别为只返回文件名,文件目录名,文件扩展
                 */
                if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
                    /**
                     * 俩个参数分别为
                     * 1.目录+config目录下的文件
                     * 2.config目录下文件名
                     */
                    $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
                }
            }
        }
        $this->setModulePath($path);
        if ($module) {
            // 对容器中的对象实例进行配置更新
            $this->containerConfigUpdate($module);
        }
    }

这里附带上一份代码,可以对着代码看上边的执行流程,对每一步都做了简单的说明。

咔咔个人见解对源码进行优化


在设置模块的这步代码咔咔感觉不是很是严谨,因为init方法会在俩个地方进行执行。


第一次的模块为空,这块代码执行是没有任何意义的。


下面在对容器的对象实例进行配置更新时进行了一次判断,判断模块的这个参数是否为空,如果不为空才会执行。


那么同样的道理,咔咔感觉在设置模块路径这块也应该在这个判断里边。


虽说第二次执行会把第一次的结果覆盖掉,但是咔咔感觉下图这样使用才会更好。

image.png


相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
14 2
|
26天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
35 3
|
3天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
16天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
36 3
|
10天前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
11 0
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
66 0
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
52 0
|
1月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
59 0
|
1月前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
80 0
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
53 5

推荐镜像

更多