ThinkPHP6 源码分析之请求处理
请求处理
return $this->app->route->dispatch($request, $withRoute);
这里就是处理整个业务的核心,之前的事件注册,路由注册,中间件注册,都会在这里执行。可能会涉及很多细节,但在这里不会细说,主要看一下整个分发的流程。看一下 dispatch 方法的代码
public function dispatch(Request $request, $withRoute = null)
{
$this->request = $request;
// 1
$this->host = $this->request->host(true);
// 2
$this->init();
// 3
if ($withRoute) {
$checkCallback = function () use ($request, $withRoute) {
$withRoute();
return $this->check();
};
if ($this->config['route_check_cache']) {
$dispatch = $this->cache
->tag('route_cache')
->remember($this->getRouteCacheKey($request), $checkCallback);
} else {
$dispatch = $checkCallback();
}
} else {
$dispatch = $this->url($this->path());
}
// 4
$dispatch->init($this->app);
$this->app->middleware->add(function () use ($dispatch) {
try {
$response = $dispatch->run();
} catch (HttpResponseException $exception) {
$response = $exception->getResponse();
}
return $response;
});
return $this->app->middleware->dispatch($request);
}
按照1 ~ 4 步骤进行说明
- 获取域名不含端口号
- 初始化
- route的初始化
- 加载配置文件 route.php
- 路由是否延迟解析
- 设置路由缓存
- 如果生成了路由缓存文件就加载路由缓存文件 => runtime 目录下的 route.php
- 是否开启路由
- 开启路由缓存 ,加载路由文件并且检测 URL,设置路由缓存 key 的还会将对应路由缓存起来。
注意的是,remember 方法会执行闭包,所以存储的并不是闭包,而是 dispatch\url 对象
获取路由缓存的 key,这个是在配置文件设置的,还必须是设置闭包。- 如果没有开启路由 直接解析 URL
- dispatch 初始化
- 因为 $dispatch instanceof dispatch\Controller 所以主要初始化工作在 Controller 里面
- 将调度追加到中间件 middleware queue 队列中,所以这一步最近才会执行
- 中间件调度执行
未启用路由
当前所在 think\Route
类中,再往下讲之前,先来看一下 url 这个方法。
public function url(string $url): UrlDispatch
{
return new UrlDispatch($this->request, $this->group, $url);
}
这段代码很重要,因为请求在没有启用路由的情况是由它处理。$this->group
属性呢就是在当前类实例化的时候设置的 think\route\Domain
域名路由类。$url
是从 pathinfo
信息中获取的。了解了这些信息之后,直接看到步骤 4,因为上面的步骤最终目的都是获得这个 dispatch
对象。
启用路由的情况
启动路由的情况会产生另外一个 Dispatch 对象 think\route\dispatch\Callback
,为什么会产生两个不同对象,具体细节在之后的路由解析中会详细介绍,先跳过细节,看看整个过程是如何处理的。下面会分别介绍两个对象的处理。
DisPatch 的初始化
上述两种情况下,获取控制和模块的方法是完全不一样。
前者是直接解析 URL 来获取,就是框架以前传统的方式 module/controller/index,然后解析出来。
后者是经过路由解析之后,才会获取相应模块的控制器,这部分有一定性能消耗。
所以对比来看,未启用路由性能比较高,但是还是推荐启用路由,这样比较易于管理。
Dispatch 执行
因为 Dispatch run 作为闭包被加入到中间的队列中之后,由中间件 Dispatch。关于中间执行可以参照上篇,这里需要知道的是 Dispatch 是被加到中间件队列末尾,是在最后执行的就可以了。
主要来看 $dispatch->run()
,上文说的两个对象都是继承 Dispatch 对象的,所以到里面看看 run
方法。
public function run(): Response
{
if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
$rules = $this->rule->getRouter()->getRule($this->rule->getRule());
$allow = [];
foreach ($rules as $item) {
$allow[] = strtoupper($item->getMethod());
}
return Response::create('', '', 204)->header(['Allow' => implode(', ', $allow)]);
}
$option = $this->rule->getOption();
// 数据自动验证
if (isset($option['validate'])) {
$this->autoValidate($option['validate']);
}
$data = $this->exec();
return $this->autoResponse($data);
}
启用路由和未启用路由完全是两种过程,启用理由设计到路由解析的过程,暂且搁置,后面详细说明。下面的过程是指的未启用路由的过程。
- 通过 URL 获取访问路径,例如 index/index
- 对于这样的 URL 默认会访问配置文件 route.php 的
controller_layer
所设置的目录下的类文件。框架默认设置的是controller
。创建框架的时候应该就可以看到了。 - 对于控制器而言可以设置控制器的中间件,对于控制器中间件作用范围应该是执行方法之前,控制器初始化后。
- 设置空控制器,默认是
Error
,这个似乎和以前的版本是一样的。
这两种方式的区别,路由访问带来了一定的自由目录组织的能力,当然性能会有所损耗,Url 访问可能限制相对而言不是那么自由,比如对于初始化框架你只能限定在 controller 目录下创建类使用。当然这个是可以改变的,使用框架在初始化的先后循序上做一下改变,以应对框架的在 URL 解析上的规则,这个将会在下一篇事件机制上作出解答。
原文转载于thinkphp6源码分析之请求处理
本作品采用《CC 协议》,转载必须注明作者和本文链接