請求處理
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 協議》,轉載必須註明作者和本文連結