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

简介: 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


相关文章
|
2天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
10天前
yolo-world 源码解析(六)(2)
yolo-world 源码解析(六)
19 0
|
10天前
yolo-world 源码解析(六)(1)
yolo-world 源码解析(六)
13 0
|
10天前
yolo-world 源码解析(五)(4)
yolo-world 源码解析(五)
22 0
|
24天前
|
XML Java Android开发
Android实现自定义进度条(源码+解析)
Android实现自定义进度条(源码+解析)
52 1
|
28天前
|
存储 NoSQL 算法
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)(二)
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)
45 0
|
10天前
yolo-world 源码解析(五)(1)
yolo-world 源码解析(五)
31 0
|
10天前
yolo-world 源码解析(二)(2)
yolo-world 源码解析(二)
21 0
|
10天前
Marker 源码解析(二)(3)
Marker 源码解析(二)
16 0

推荐镜像

更多