1. 程式人生 > >illuminate/routing 原始碼分析之註冊路由

illuminate/routing 原始碼分析之註冊路由

我們知道,在 Laravel 世界裡,外界傳進來一個 Request 時,會被 Kernel 處理並返回給外界一個 Response。Kernel 在處理 Request 時,會呼叫 illuminate/routing 包提供的路由功能,來根據當前的 Request,轉發到對應的執行邏輯(執行邏輯的形式可以為 Closure 或 [email protected])。同時,在進入執行邏輯之前和之後,還會依次進入 Middlewares 的前置和後置處理。所以,一個 Request 由 Kernel 處理為一個 Response 的一個生命週期圖如下:

根據上面的流程,理解路由系統的內部工作原理是非常重要的!當然,它也是非常複雜的。想要深入理解一個工具的使用,學習它的內部設計原理才是畫龍點睛

。在理解 illuminate/routing 如何工作之前,先設想如何去設計一個路由系統呢?一起想個三分鐘吧。

  • 註冊路由 :想想一個 Request 進入程式時,攜帶的請求資訊類似為 GET https://localhost/api/v2/peop...,所以我們需要定義一個 Route 物件來表示這個資訊,同時還得定義 RouteCollection(Route 的集合)來新增、獲取和匹配出一個 Route。程式啟動時,開發者定義的所有路由(Route)列表都會被註冊到 RouteCollection 內。
  • 查詢路由 :有了整個程式的路由列表,這樣當一個 Request 進來時,再根據當前 Request 的資訊匹配出一個合適的 Route,所以可以設計一個類似 RouterManager 物件,作用類似開發經理 Manager 的總體統籌,來呼叫 $router->findRoute(\$request): Route
    匹配出合適的 Route。
  • 執行路由 :既然匹配出了對應 Route,那可以呼叫 RouterManager->runRoute($route): Response 得到對應的 Response 返回給外界。

所以,如果自己去設計一個路由系統,就可以按照上面三步去做,思路也很好理解。實際上,Laravel 的路由模組 illuminate/routing 也是按照這三個步驟來設計的。本文將會原始碼分析下 Laravel 是如何把開發者在 routes/*.php 中寫的路由列表註冊到 RouteCollection 物件內的。

我們知道,Laravel 在啟動時第一步會去例項化 \Illuminate\Foundation\Application

物件,這個容器物件會去呼叫 \Illuminate\Routing\RoutingServiceProvider::register() 往容器物件的 $bindings 陣列屬性key-value 形式註冊進來,註冊的物件主要包括 Router(就是上文的 RouterManager 角色,把它比作為開發小組的開發經理角色)等。

然後會去呼叫 \App\Providers\RouteServiceProvider::boot() 方法預設載入 routes/api.php 和 routes/web.php 檔案中註冊的路由列表,並且以 Facade 模式去註冊路由列表:


Route::prefix($prefix)->middleware($middleware)->group('xxx/web.php');

實際上就是呼叫 \Illuminate\Routing\RouteRegistrar 類裡的 attribute(key, value) 方法以 key-value 形式註冊到 attributes 陣列屬性裡。最主要的 group(string) 方法呼叫的是 Router::group() 方法,然後呼叫 loadRoutes(routes) 去執行在 routes/api.php 和 routes/web.php 檔案中定義的路由。對於每一種方法(如 GET、POST 等等方法)的路由,Router 物件內都有對應的方法來新增 Route 註冊到 RouteCollection 中,比如常見的 get(uri, action) 方法,就是呼叫的 RouteCollection::add(route) 方法把 Route 註冊到 RouteCollection 中。而 route 的建立,呼叫的是 Router::createRoute(methods, uri, action),其中由於 $action 可能是 Closure 或者 [email protected],如果是 [email protected] 形式,則需要把字串切割為陣列形式,再傳入 Route 類的建構函式裡。

總結下注冊路由所需要用到的物件:使用 Route 來表示路由資訊,使用 RouteCollection 來表示路由集合列表,並且提供了新增刪除方法來把 Route 註冊到 RouteCollection 內,而 Router 才是縱覽全域性的角色,註冊路由是通過該物件發起的,它會呼叫 RouteCollection 去註冊路由,路由的元資料資訊如路由名稱等是用 RouteRegistrar 物件表示。從上文可知道,所有物件中,Router 才是畫龍點睛的物件。

通過以上的分析,就能對 illuminate/routing 路由系統的基本設計越來越清晰。一個 Request 進來後,Application 首先開始啟動並按照以上邏輯開始註冊路由列表,然後就是根據當前 Request 資訊查詢對應的 Route 物件。

那如何根據當前 Request 資訊查找出對應的 Route 的呢?見本系列第二篇文章。

原文地址:https://segmentfault.com/a/1190000015862083