laravel 的路由相比其餘PHP框架很是靈活和優雅,它也能作的在url不變的狀況下改變調用的控制器和方法。php
那麼這到底在 laravel 裏是怎麼完成的呢html
這沒什麼神祕的,回憶一下咱們寫一個單頁過程化 PHP 腳本時咱們是如何接收 HTML 頁面傳輸的參數的?laravel
是的,也許你想起來了咱們會使用 PHP 的超全局變量 $_SERVER
、$_GET
、$_POST
等等,是的框架的底層一樣是使用它們的,只是框架進行了更詳盡的封裝。bootstrap
laravel 對 Symfony 框架提供的 HttpFoundation 組件,這個組件對 HTTP 進行了面向對象封裝,laravel在其基礎上又進行了封裝,以適合 laravel 框架自身的需求。app
HttpFoundation 組件將 $_GET
、$_POST
、$_FILES
、$_COOKIE
等一些超全局變量進行封裝,不只將超全局變量調用轉變爲面向對象的方式,並且也簡化了操做。好比 Symfony\Component\HttpFoundation\Request::createFromGlobals()
的返回值就是全部超全局組成的一個集合。composer
若是你對這個組件感興趣能夠查看其文檔 HttpFoundation框架
路由啓動咱們要從原點找起,那麼原點就是 laravel 框架的入口文件 public/index.php
。函數
index.php 裏首先把 bootstrap/autoload.php 引入,這個文件引入的是 composer 的 autoload 文件,將全部的包加載到腳本里,這個腳本文件還定義了一個常量 LARAVEL_START 記錄了框架啓動的微秒時間戳。this
而後引入了 bootstrap/app.php 腳本,這個腳本加載了 laravl 框架的核心文件,返回了一個應用實例。url
隨後就是後續步驟
// 獲得應用核心實例
$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 驗證等等都在主流程的各個步驟中附加分發調用了。