ThinkPHP6 原始碼分析之請求處理

JaguarJack發表於2019-07-05

請求處理

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

相關文章