上一节分析了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、根目录里面的配置(这个配里面也可以分模块)
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,完美解决