🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] * * * * * ## 1 本节是应用启动文件(/thinkphp/library/think/App.php)的其他静态方法分析 **self::initModule()** ~~~ private static function initModule($module, $config) { $module = (COMMON_MODULE == $module || !APP_MULTI_MODULE) ? '' : $module . DS; if (is_file(APP_PATH . $module . 'init' . EXT)) { include APP_PATH . $module . 'init' . EXT; } else { $path = APP_PATH . $module; $config = Config::load(APP_PATH . $module . 'config' . EXT); if ($config['app_status']) { $config = Config::load(APP_PATH . $module . $config['app_status'] . EXT); } if ($config['extra_config_list']) { foreach ($config['extra_config_list'] as $name => $file) { $file = strpos($file, '.') ? $file : $path . $file . EXT; Config::load($file, is_string($name) ? $name : pathinfo($file, PATHINFO_FILENAME)); } } if (is_file($path . 'alias' . EXT)) { Loader::addMap(include $path . 'alias' . EXT); } if (APP_HOOK && is_file($path . 'tags' . EXT)) { Hook::import(include $path . 'tags' . EXT); } if (is_file($path . 'common' . EXT)) { include $path . 'common' . EXT; } if ($config['lang_switch_on'] && $module) { Lang::load($path . 'lang' . DS . LANG_SET . EXT); } } } ~~~ **self::initModule()源码分析** `$module = (COMMON_MODULE == $module || !APP_MULTI_MODULE) ? '' : $module . DS;` 模块目录名称获取 ~~~ if (is_file(APP_PATH . $module . 'init' . EXT)) { include APP_PATH . $module . 'init' . EXT; } ~~~ 检查是否存在模块对应的初始化入口文件init.php,见使用范例 模块开发 ~~~ $path = APP_PATH . $module; $config = Config::load(APP_PATH . $module . 'config' . EXT); ~~~ 加载模块的配置文件 见使用范例 模块开发 ~~~ if ($config['app_status']) { $config = Config::load(APP_PATH . $module . $config['app_status'] . EXT); } ~~~ 加载模块状态配置文件 见使用范例 模块开发 ~~~ if ($config['extra_config_list']) { foreach ($config['extra_config_list'] as $name => $file) { $file = strpos($file, '.') ? $file : $path . $file . EXT; Config::load($file, is_string($name) ? $name : pathinfo($file, PATHINFO_FILENAME)); } } ~~~ 加载模块扩展配置文件 见使用范例模块开发 ~~~ if (is_file($path . 'alias' . EXT)) { Loader::addMap(include $path . 'alias' . EXT); } ~~~ 加载模块别名配置文件 见使用范例模块开发 ~~~ if (APP_HOOK && is_file($path . 'tags' . EXT)) { Hook::import(include $path . 'tags' . EXT); } ~~~ 加载模块行为扩展文件 见使用范例模块开发 ~~~ if (is_file($path . 'common' . EXT)) { include $path . 'common' . EXT; } ~~~ 加载模块公共函数文件 见使用范例模块开发 ~~~ if ($config['lang_switch_on'] && $module) { Lang::load($path . 'lang' . DS . LANG_SET . EXT); } ~~~ 加载模块语言包文件 见使用范例模块开发 * * * * * * * * * * **2 self::parsePathinfo()源代码** ~~~ private static function parsePathinfo(array $config) { if (isset($_GET[$config['var_pathinfo']])) { $_SERVER['PATH_INFO'] = $_GET[$config['var_pathinfo']]; unset($_GET[$config['var_pathinfo']]); } elseif (IS_CLI) { // CLI模式下 index.php module/controller/action/params/... $_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; } APP_HOOK && Hook::listen('path_info'); if (!isset($_SERVER['PATH_INFO'])) { foreach ($config['pathinfo_fetch'] as $type) { if (!empty($_SERVER[$type])) { $_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ? substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type]; break; } } } } ~~~ **self::parsePathinfo()源代码分析** ~~~ if (isset($_GET[$config['var_pathinfo']])) { $_SERVER['PATH_INFO'] = $_GET[$config['var_pathinfo']]; unset($_GET[$config['var_pathinfo']]); } elseif (IS_CLI) { // CLI模式下 index.php module/controller/action/params/... $_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; } ~~~ 检查$_GET参数是否包含$config配置的兼容模式参数, 兼容模式参数见附:全局配置文件 如果有 设置$_SERVER['PATH_INFO']为$_GET中兼容模式参数对应的参数的值 并删除$_GET兼容模式参数 如果没有检查模式检查 检查是否是CLI模式 然后设置$_SERVER['PATH_INFO']为CLI的$_SERVER['argv'][1]参数 这里的PATH_INFO其实就是调用的文件名称与参数 ` APP_HOOK && Hook::listen('path_info');` path_info分析回调 ~~~ if (!isset($_SERVER['PATH_INFO'])) { foreach ($config['pathinfo_fetch'] as $type) { if (!empty($_SERVER[$type])) { $_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ? substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type]; break; } } } ~~~ 如果上面获取PATH_INFO失败 那么从配置信息$config['pathinfp_fetch']中依次读取PATH_INFP信息 经过上面调用获取了全局变量$_SERVER['PATH_INFO']的值,这个值在下面的self::route()中会用到 * * * * * * * * * * **3 self::route()源代码** ~~~ public static function route(array $config) { self::parsePathinfo($config); if (empty($_SERVER['PATH_INFO'])) { $_SERVER['PATH_INFO'] = ''; define('__INFO__', ''); define('__EXT__', ''); } else { $_SERVER['PATH_INFO'] = trim($_SERVER['PATH_INFO'], '/'); define('__INFO__', $_SERVER['PATH_INFO']); define('__EXT__', strtolower(pathinfo($_SERVER['PATH_INFO'], PATHINFO_EXTENSION))); if ($config['url_deny_suffix'] && preg_match('/\.(' . $config['url_deny_suffix'] . ')$/i', __INFO__)) { throw new Exception('url suffix deny'); } $_SERVER['PATH_INFO'] = preg_replace($config['url_html_suffix'] ? '/\.(' . trim($config['url_html_suffix'], '.') . ')$/i' : '/\.' . __EXT__ . '$/i', '', __INFO__); } $depr = $config['pathinfo_depr']; $result = false; if (APP_ROUTE_ON && !empty($config['url_route_on'])) { if (!empty($config['route'])) { Route::register($config['route']); } $result = Route::check($_SERVER['PATH_INFO'], $depr, !IS_CLI ? $config['url_domain_deploy'] : false); if (APP_ROUTE_MUST && false === $result && $config['url_route_must']) { throw new Exception('route not define '); } } if (false === $result) { $result = Route::parseUrl($_SERVER['PATH_INFO'], $depr); } self::dispatch($result); } ~~~ **self::route()源代码分析** `self::parsePathinfo($config);` 调用上面的self::parsePathinfo()获取$_SERVER['PATH_INFO']的值 ~~~ $_SERVER['PATH_INFO'] = ''; define('__INFO__', ''); define('__EXT__', ''); ~~~ 如果$_SERVER['PATH_INFO']为空 设置`__INFO__ __EXT__`全局变量为空 ~~~ $_SERVER['PATH_INFO'] = trim($_SERVER['PATH_INFO'], '/'); define('__INFO__', $_SERVER['PATH_INFO']); define('__EXT__', strtolower(pathinfo($_SERVER['PATH_INFO'], PATHINFO_EXTENSION))); if ($config['url_deny_suffix'] && preg_match('/\.(' . $config['url_deny_suffix'] . ')$/i', __INFO__)) { throw new Exception('url suffix deny'); } $_SERVER['PATH_INFO'] = preg_replace($config['url_html_suffix'] ? '/\.(' . trim($config['url_html_suffix'], '.') . ')$/i' : '/\.' . __EXT__ . '$/i', '', __INFO__); ~~~ 如果$_SERVER['PATH_INFO']为空 设置`__INFO__ __EXT__`为$_SERVER['PATH_INFO']的相应值。 并检查是否禁用相应的后缀请求 见使用范例 禁用URL后缀 接着去除正常的Url后缀???待分析 `$depr = $config['pathinfo_depr'];` 获取配置的pathinfo_depr, ~~~ if (APP_ROUTE_ON && !empty($config['url_route_on'])) { } ~~~ 检查全局变量APP_ROUTE_ON 是否开启路由检测 全局变量设置见 [附:全局变量文件](http://www.kancloud.cn/zmwtp/tp5/119430) 如果开启 ~~~ if (!empty($config['route'])) { Route::register($config['route']); } ~~~ 将配置中的路由添加到全局路由定义 全局路由定义见 [附:全局路由文件](http://www.kancloud.cn/zmwtp/tp5/119438) `$result = Route::check($_SERVER['PATH_INFO'], $depr, !IS_CLI ? $config['url_domain_deploy'] : false);` 检查路由 ???待分析 ~~~ if (APP_ROUTE_MUST && false === $result && $config['url_route_must']) { throw new Exception('route not define '); } ~~~ 路由检测结果分析 ~~~ if (false === $result) { $result = Route::parseUrl($_SERVER['PATH_INFO'], $depr); } ~~~ 路由url分析 见 [附:全局路由文件](http://www.kancloud.cn/zmwtp/tp5/119438) `self::dispatch($result);` 注册路由分析结果到app调度类型$dispatch 调度类型的使用见 [应用调度分析](http://www.kancloud.cn/zmwtp/tp5/119428) ## 3 总结 **initModule() 模块的初始化实现** 在系统主流程App::run()中有两处调用, 一处是App::run()刚开始总的公共模块初始化 self::initModule(COMMON_MODULE,Config::get()) 一处是App::module()中的应用模块初始化 self::initModule(MODULE_NAME, $config); **parsePathinfo() 获取请求信息** **route() 根据请求信息进行路由分派** 其他的函数包括 run() 应用启动流程 见 [应用启动文件](http://www.kancloud.cn/zmwtp/tp5/119426) invokeFunction() 调度回调函数 见 [应用调度分析](http://www.kancloud.cn/zmwtp/tp5/119428) invokeMethod() 调度回调方法 见 [应用调度分析](http://www.kancloud.cn/zmwtp/tp5/119428) bindParams() 调度参数合成 见 [应用调度分析](http://www.kancloud.cn/zmwtp/tp5/119428) module() 调度回调模块 见 [应用调度分析](http://www.kancloud.cn/zmwtp/tp5/119428) dispathc() 调度注册见[应用调度分析](http://www.kancloud.cn/zmwtp/tp5/119428)