1. Loading Middleware
A previously written articleanalyzed the application Initialization, that is, the expansion and analysis of $this->initialize(), the first line of code of the runWithRequest () method called in the run() method of the Http class. Let’s take another look at the first few lines of the runWithRequest () method:
protected function runWithRequest(Request $request) { $this->initialize(); // 加载全局中间件 $this->loadMiddleware(); . . .
After the application is initialized, the next step is to process the middleware.
Initialization of middleware class
loadMiddleware method:
protected function loadMiddleware(): void { if (is_file($this->app->getBasePath() . 'middleware.php')) { $this->app->middleware->import(include $this->app->getBasePath() . 'middleware.php'); } }
is still a common routine, instantiate the middleware through $this->app->middleware file and obtain its instance.
Import middleware
After getting the instance of the Middleware class through $this->app->middleware, the program then calls the import method and passes in the "middleware" from the "app" directory. .php" file. The original content of the file is as follows (all comments were originally commented out):
return [ // 全局请求缓存 // \think\middleware\CheckRequestCache::class, // 多语言加载 \think\middleware\LoadLangPack::class, // Session初始化 // \think\middleware\SessionInit::class, // 页面Trace调试 \think\middleware\TraceDebug::class, ];
In order to study how the middleware is loaded, first remove the two comments, that is, add two middlewares. Next, look at the import method:
public function import(array $middlewares = [], string $type = 'global'): void { foreach ($middlewares as $middleware) { $this->add($middleware, $type); } }
This method passes in an array of middleware and a middleware type. The default is global. The key is the add method inside. Jump to the add method:
public function add($middleware, string $type = 'route'): void { if (is_null($middleware)) { return; } $middleware = $this->buildMiddleware($middleware, $type); if ($middleware) { $this->queue[$type][] = $middleware; // 去除重复 $this->queue[$type] = array_unique($this->queue[$type], SORT_REGULAR); } }
In fact, the real work is the buildMiddleware method, go directly to:
protected function buildMiddleware($middleware, string $type): array { // 是否是数组 if (is_array($middleware)) { // 列出中间件及其参数 // 这里说明我们可以给中间件传入参数,且形式为 [中间件, 参数] list($middleware, $param) = $middleware; } // 是否是一个闭包 // 说明中间件可以是一个闭包 if ($middleware instanceof \Closure) { //返回闭包和参数 return [$middleware, $param ?? null]; } // 排除了上面几种类型,且不是字符串,抛出错误 if (!is_string($middleware)) { throw new InvalidArgumentException('The middleware is invalid'); } //中间件别名检查 $alias = $this->app->config->get('middleware.alias', []); if (isset($alias[$middleware])) { $middleware = $alias[$middleware]; } //如果中间件有包含中间件(说明中间件可以嵌套) //再走一遍「import」递归解析 if (is_array($middleware)) { $this->import($middleware, $type); return []; } //返回解析结果 return [[$middleware, 'handle'], $param ?? null]; }
For detailed analysis, see the above code comments. The final returned result, in the add method, executes $ this->queue\[$type][] = $middleware; and adds it to a queue. The final parsing result is probably like this (app/middleware.php removes some middleware comments):
At this point, the global middleware is loaded.
2. Multi-application parsing
After loading the middleware, the next step is multi-application parsing (ThinkPHP 6 starts to support multi-application mode).
if ($this->multi) { $this->parseMultiApp(); }
Notice that the constructor of the Http class:
public function __construct(App $app) { $this->app = $app; //多应用解析,通过判断「app」目录下有无「controller」目录,没有就是多应用模式 $this->multi = is_dir($this->app->getBasePath() . 'controller') ? false : true; }
As you can see, the program determines whether it is a multi-application mode by judging whether there is a "controller" directory in the "app" directory. .
Then look at the main method parseMultiApp:
protected function parseMultiApp(): void { // 虽然在「Http」的构造函数自动判断过是否开启多应用 //如果没有controller目录,$this->multi为true,就会来到本方法 // 接着还要看配置文件是否有配置 if ($this->app->config->get('app.auto_multi_app', false)) { // 自动多应用识别 $this->bindDomain = false; // 获取域名绑定 $bind = $this->app->config->get('app.domain_bind', []); // 如果有域名绑定 if (!empty($bind)) { // 获取当前子域名 $subDomain = $this->app->request->subDomain(); $domain = $this->app->request->host(true); //完整域名绑定 if (isset($bind[$domain])) { $appName = $bind[$domain]; $this->bindDomain = true; //子域名绑定 } elseif (isset($bind[$subDomain])) { $appName = $bind[$subDomain]; $this->bindDomain = true; //二级泛域名绑定 } elseif (isset($bind['*'])) { $appName = $bind['*']; $this->bindDomain = true; } } //如果没有域名绑定 if (!$this->bindDomain) { //获取别名映射 $map = $this->app->config->get('app.app_map', []); //获取禁止URL访问目录 $deny = $this->app->config->get('app.deny_app_list', []); //获取当前请求URL的pathinfo信息(含URL后缀) // 比如 index/index/index $path = $this->app->request->pathinfo(); // 比如,从index/index/index获取得index $name = current(explode('/', $path)); //解析别名映射 if (isset($map[$name])) { //如果这个别名映射到的是一个闭包 //这样不知有啥用 if ($map[$name] instanceof Closure) { $result = call_user_func_array($map[$name], [$this]); $appName = $result ?: $name; //直接取得应用名 } else { $appName = $map[$name]; } //$name不为空且$name在$map数组中作为KEY,或者$name是禁止URL方位的目录 } elseif ($name && (false !== array_search($name, $map) || in_array($name, $deny))) { throw new HttpException(404, 'app not exists:' . $name); } elseif ($name && isset($map['*'])) { $appName = $map['*']; } else { $appName = $name; } if ($name) { $this->app->request->setRoot('/' . $name); $this->app->request->setPathinfo(strpos($path, '/') ? ltrim(strstr($path, '/'), '/') : ''); } } } else { $appName = $this->name ?: $this->getScriptName(); } $this->loadApp($appName ?: $this->app->config->get('app.default_app', 'index')); }
You can see that the first section of the "pathinfo" information will be parsed into the application name, such as index in index/index/index/. At the end of the method, the loadApp method is also called. The operation performed is similar to the initialization of the previous application, except that the files loaded are all in the directory of the application.
Compared with the previous version, ThinkPHP 6 seems to have transformed the original module into a multi-application, because in the case of multi-application, the application name and the previous module name are parsed from the first section of pathinfo. The content of the module is not visible in the new document.