TP5源码分析-执行应用【initialize方法-之init方法分析】

文章目录

          • 回顾
          • 分析
          • 细节
          • 展望

回顾

上一节分析了initialize方法里面的具体流程,对里面一些地方都做了解释,像init()、数据库配置初始化、路由初始化这些都是我们接下来分析的重点

分析
  • 今天我们来分析initialize方法里的init方法

  • 上源码 看注解 有var_dump的后面我都会把打印结果贴在后面

 /**
     * 初始化应用或模块
     * @access public
     * @param  string $module 模块名
     * @return void
     */
public function init($module = '')
    {
       // var_dump($module);  index
        // 定位模块目录
        $module = $module ? $module . DIRECTORY_SEPARATOR : ''; // index
        $path   = $this->appPath . $module;
       // var_dump($path);//初始化应用D:\www\ThinkPHP_V5.1.39\application\   初始化模块 D:\www\ThinkPHP_V5.1.39\application\index\
        // 加载初始化文件
        if (is_file($path . 'init.php')) {
            include $path . 'init.php';
        } elseif (is_file($this->runtimePath . $module . 'init.php')) {

            // 加载公共文件
            if (is_file($path . 'common.php')) {
                include_once $path . 'common.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);//import 分析
            }
        }


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

            // 注册服务的容器对象实例 例如在provider.php中设置
            /*return [
                      'route'      => \think\Route::class,
                      'session'    => \think\Session::class,
                      'url'        => \think\Url::class,
                     ];
            */
            if (is_file($path . 'provider.php')) {
                $provider = include $path . 'provider.php';
                if (is_array($provider)) {
                    $this->bindTo($provider);
                }
            }

            // 自动读取配置文件 1、先读取应用配置 或者 模块配置 2、根目录里面的配置(这个配里面也可以分模块)
            if (is_dir($path . 'config')) {
                $dir = $path . 'config' . DIRECTORY_SEPARATOR;

            } elseif (is_dir($this->configPath . $module)) { //$this->configPath ==> D:\www\ThinkPHP_V5.1.39\config\
                $dir = $this->configPath . $module;
            }

            $files = isset($dir) ? scandir($dir) : []; //遍历配置所有文件

            foreach ($files as $file) {
                if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
                   // var_dump($dir . $file); ==>D:\www\ThinkPHP_V5.1.39\config\app.php
                   // var_dump(pathinfo($file, PATHINFO_FILENAME)); ===>app
                    $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
                }
            }
        }

        $this->setModulePath($path);

        if ($module) {
            // 对容器中的对象实例进行配置更新
            $this->containerConfigUpdate($module);
        }
    }
  • 这个方法主要是对应用或模块进行初始化
  • 【加载行为扩展文件】 和 【加载中间件】将会作专题介绍
  • 【注册服务的容器对象实例】
    实际上通过配置动态绑定容器
  if (is_file($path . 'provider.php')) {
        $provider = include $path . 'provider.php';
        if (is_array($provider)) {
            $this->bindTo($provider);
        }
   }

在应用或模块下有个provider.php 可以这样设置
return [
‘route’ => \think\Route::class,
‘session’ => \think\Session::class,
‘url’ => \think\Url::class,
];

  • 【自动读取配置文件】
    1、先读取应用配置 或者 模块配置
    2、根目录里面的配置(这个配里面也可以分模块)
        // 自动读取配置文件 1、先读取应用配置 或者 模块配置 2、根目录里面的配置(这个配里面也可以分模块)
            if (is_dir($path . 'config')) {
                $dir = $path . 'config' . DIRECTORY_SEPARATOR;

            } elseif (is_dir($this->configPath . $module)) { //$this->configPath ==> D:\www\ThinkPHP_V5.1.39\config\
                $dir = $this->configPath . $module;
            }

            $files = isset($dir) ? scandir($dir) : []; //遍历配置所有文件

            foreach ($files as $file) {
                if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
                   // var_dump($dir . $file); ==>D:\www\ThinkPHP_V5.1.39\config\app.php
                   // var_dump(pathinfo($file, PATHINFO_FILENAME)); ===>app
                    $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
                }
            }
  • 代码的严谨性分析

setModulePath 作用两次 第一次是应用路径 第二次是模块路径 模块路径覆盖前面的路径 实际上如果我们没有模块的时候 就没有必要再去setModulePath

 $this->setModulePath($path);
if ($module) {
     // 对容器中的对象实例进行配置更新
     $this->containerConfigUpdate($module);
 }

应该改为

if ($module) {
    $this->setModulePath($path);
     // 对容器中的对象实例进行配置更新
     $this->containerConfigUpdate($module);
 }
  • 【对容器中的对象实例进行配置更新】
    把模块对应的配置放入容器统一管理 学习这种思想
$this->containerConfigUpdate($module);
protected function containerConfigUpdate($module)
    {
        $config = $this->config->get();

        // 注册异常处理类
        if ($config['app']['exception_handle']) {
            Error::setExceptionHandler($config['app']['exception_handle']);
        }

        Db::init($config['database']);
        $this->middleware->setConfig($config['middleware']);
        $this->route->setConfig($config['app']);
        $this->request->init($config['app']);
        $this->cookie->init($config['cookie']);
        $this->view->init($config['template']);
        $this->log->init($config['log']);
        $this->session->setConfig($config['session']);
        $this->debug->setConfig($config['trace']);
        $this->cache->init($config['cache'], true);

        // 加载当前模块语言包
        $this->lang->load($this->appPath . $module . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php');

        // 模块请求缓存检查
        $this->checkRequestCache(
            $config['app']['request_cache'],
            $config['app']['request_cache_expire'],
            $config['app']['request_cache_except']
        );
    }
细节

public function init($module = ''){}
大家可能注意到了,这个函数加载了两次,第一次是加载应用, 第二次加载模块 。要问的是第二次是怎么加载的?变量$module又是从哪传过来的?厉害啊 这是关键之所在啊!!!
请看:

/**
 * 执行应用程
*/
    public function run()
    {
        try {
            // 初始化应用
            // 监听app_init
            // 监听app_dispatch
            if (empty($dispatch)) {
                // 路由检测
               var_dump($this->routeCheck());
                $dispatch = $this->routeCheck()->init();
            }     
    }

上面的代码我就留摘出了几行 这就是关键,在路由的检测的时候 我打印
$this->routeCheck();结果是object(think\route\dispatch\Module)# 对象
然后你看 $this->routeCheck()->init(); 又调用了init(); 那么我们在对象think\route\dispatch\Module 里面的init 一探究竟

  public function init()
    {
        parent::init();
        $result = $this->dispatch;
        if (is_string($result)) {
            $result = explode('/', $result);
        }
        if ($this->rule->getConfig('app_multi_module')) {
            // 多模块部署
            $module    = strip_tags(strtolower($result[0] ?: $this->rule->getConfig('default_module')));
            $bind      = $this->rule->getRouter()->getBind();
            $available = false;
            if ($bind && preg_match('/^[a-z]/is', $bind)) {
                // 绑定模块
                list($bindModule) = explode('/', $bind);
                if (empty($result[0])) {
                    $module = $bindModule;
                }
                $available = true;
            } elseif (!in_array($module, $this->rule->getConfig('deny_module_list')) && is_dir($this->app->getAppPath() . $module)) {
                $available = true;
            } elseif ($this->rule->getConfig('empty_module')) {
                $module    = $this->rule->getConfig('empty_module');
                $available = true;
            }

            // 模块初始化
            if ($module && $available) {
                // 初始化模块
                $this->request->setModule($module);
                $this->app->init($module);
            } else {
                throw new HttpException(404, 'module not exists:' . $module);
            }
        }

答案揭晓 :

$this->app->init($module);

以上的函数就是我跟你说的第二次要加载的地方,OK,完美解决

展望
  • 接下来计划要重点搞定的是:
    1、配置文件
    2、路由
    3、行为扩展
    4、中间件

你可能感兴趣的:(TP5,源码分析,php)