概述
laravel 的路由相比其他PHP框架非常靈活和優雅,它也能做的在url不變的情況下改變呼叫的控制器和方法。
那麼這到底在 laravel 裡是怎麼完成的呢
路由到底是如何獲取的?
這沒什麼神祕的,回憶一下我們寫一個單頁過程化 PHP 指令碼時我們是如何接收 HTML 頁面傳輸的引數的?
是的,也許你想起來了我們會使用 PHP 的超全域性變數 $_SERVER
、$_GET
、$_POST
等等,是的框架的底層同樣是使用它們的,只是框架進行了更詳盡的封裝。
laravel 對 Symfony 框架提供的 HttpFoundation 元件,這個元件對 HTTP 進行了物件導向封裝,laravel在其基礎上又進行了封裝,以適合 laravel 框架自身的需求。
HttpFoundation 元件將 $_GET
、$_POST
、$_FILES
、$_COOKIE
等一些超全域性變數進行封裝,不僅將超全域性變數呼叫轉變為物件導向的方式,而且也簡化了操作。比如 Symfony\Component\HttpFoundation\Request::createFromGlobals()
的返回值就是所有超全域性組成的一個集合。
如果你對這個元件感興趣可以檢視其文件 HttpFoundation
路由是如何啟動的?
路由啟動我們要從原點找起,那麼原點就是 laravel 框架的入口檔案 public/index.php
。
index.php 裡首先把 bootstrap/autoload.php 引入,這個檔案引入的是 composer 的 autoload 檔案,將所有的包載入到指令碼里,這個指令碼檔案還定義了一個常量 LARAVEL_START 記錄了框架啟動的微秒時間戳。
然後引入了 bootstrap/app.php 指令碼,這個指令碼載入了 laravl 框架的核心檔案,返回了一個應用例項。
隨後就是後續步驟
// 得到應用核心例項
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
// 對請求處理後獲得響應
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture() // 獲得請求引數
);
// 傳送響應
$response->send();
// 終止
$kernel->terminate($request, $response);
我們想知道路由在哪分配呼叫就在 $kernel->handle()
這裡,其中呼叫了 $this-sendRequestThroughRouter()
,這裡就將請求傳送給了中介軟體和路由。
$this->dispatchToRouter()
中 $this->router->dispatch($request)
就將請求傳入到 Router 類中了。
然後再 Router 類的 dispatch()
的方法中呼叫了 Router 類的 dispatchToRoute()
方法將請求向下傳遞。
在dispatchToRoute()
中你會發現 $route = $this->findRoute($request);
的呼叫。findRoute()
中呼叫 RouteCollection 類中的 match()
方法去匹配在初始化框架時讀取到記憶體中的開發者在路由定義檔案裡定義的路由,如果未匹配到就丟擲 NotFoundHttpException
異常。如匹配到就返回了 Route 類的例項。
然後這個例項在 Router 類的 dispatchToRoute()
方法中例項有被傳入到 runRouteWithinStack()
方法。這個方法中又呼叫多個方法將匹配到的開發者定義的 URL 的對映的控制器例項化,去呼叫控制器的 callAction()
方法,callAction()
寫在控制器的基類中,就是我們寫一個控制器都要繼承的那個 Conreoller
類中。
方法很簡單,就是講傳入的方法名和引數用 PHP 函式 call_user_func_array()
進行呼叫。
以上是我們的最佳實踐。但是在你初始化 laravel 框架的時候他的首頁是以下這樣呼叫的。
Route::get('/', function () {
return view('welcome');
});
路由中使用匿名函式,在呼叫控制器的例項前框架先會判斷是否是一個控制器動作,如何不是就獲取路由中的匿名函式進行呼叫。判斷的依據就是開發者定義的路由的第二個引數是否是一個字串。
這是 laravel 路由的主流程,其他中介軟體呼叫,Request 驗證等等都在主流程的各個步驟中附加分發呼叫了。