【Laravel入門】10分鐘搞懂Laravel請求的生命週期

【Laravel入門】10分鐘搞懂Laravel請求的生命週期

這篇文章將要給你一個清楚且從大局觀去了解到底Laravel框架是如何運作的。越是更好的了解一個框架,當你看到一些方便的效果出現時,就不會認為這是"魔法",同時在運用時也就更有自信

針對剛入門的朋友,假如下面寫的一些東西你不是太了解,別急著失去信心!

只要先對等會說的有個大概的理解即可,隨著你在學習過後續的章節之後,你將會越來越有概念的

旅程開始

Laravel 應用的所有請求入口為 public/index.php,也就是說任何的請求都會被網頁伺服器 (Apache/Nginx) 設定導向到這個檔案

這個 index.php 並沒有太多的程式內容,只不過是用來載入剩下的 Laravel 框架

index.php 檔案載入由 Composer 自動生成的類別定義,然後從 bootstrap/app.php 傳回一個 Laravel 應用的實例

而Laravel自身做的第一件事情就是建立一個應用或者說是服務容器的實例,接著是綁定實作,如以下程式碼:

\\bootstrap/app.php

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

這段程式碼總共做了兩件事情,分別是設定主要目錄以及綁定實作的類別

設定主要目錄是為了方便之後其他工作常需要找子目錄,其路徑自然都需要相對於主要目錄。而綁定實作的類別,之後可以根據需要,去透過 Application 建置需要的實例來運作

小知識

現代的網頁應用,除了提供網頁服務外,也會提供命令列或單元測試等功能;一般來說,都會希望不管是透過網頁瀏覽器又或者是透過命令列等不同情境都能呈現出相同的結果。 只要Application的初始化有一致,那麼不同情境也會跑相同的程式碼

第二站 HTTP / Console Kernels

接下來,請求將會被轉給 HTTP Kernel 又或者是 Console Kernel,端視請求的類型來決定。這兩個 Kernel 作為核心,所有的清求都會經過它們。現在,先聚焦在 HTTP kernel,位於 app/Http/Kernel.php

這個 HTTP Kernerl 繼承了 Illuminate\Foundation\Http\Kernel 類別,這裡定義了一個 bootstrappers 陣列,將會在請求被執行之前先運作。這些 bootstrapper 定義了錯誤處理.log紀錄.偵測應用環境.以及所有需要在請求被執行之前先行運作的工作。一般來說,這些類別會把 Laravel 的內部設定都搞定好,你不需要自己把手弄髒去做設定

HTTP Kernel也定義了一個 HTTP 中介層清單,使得所有請求需要在被應用處理之前先通過這些中介層,就像是濾網一般。這些中介層處理 HTTP Session 的讀寫,判斷應用是否處於維護模式,確認 CSRF token,還有更多等會會提到的..

HTTP Kernel的 handle() 很簡單: 它接收一個請求,並回傳一個回應。不妨把 HTTP Kernel 想成是一個很大的黑箱,代表整個應用,提供它 HTTP 請求就會傳出HTTP回應

裏頭可以預期的是做了這些事情:

  1. 載入.env檔案
  2. 載入config設定
  3. 設定錯誤處理
  4. 設定 Facade
  5. 註冊服務供應器(也就是 register 方法)
  6. 啟動服務供應器(也就是 boot 方法)

小重點

由此可知所有服務供應器的 register() 會比 boot() 先執行

第三站 服務供應器

在所有的 Kernel bootstrapping 行動當中最重要的,莫過於載入你應用的服務供應器。應用的所有服務供應器是被設定於 config/app.php 設定檔裡的 providers 陣列裏頭

Laravel 會逐一的遞迴這個列表並生成這些服務供應器實例。當完成初始化,這些供應器的 register() 將會被呼叫。當完成註冊後,這些供應器的 boot() 又會接著被呼叫

服務供應器負責引導框架的各種元件,比如資料庫 database ,排程 queue,驗證 validation 以及最常用的路由 Route 元件

可以說所有由 Laravel 框架所提供的所有主要特色都是由服務供應器來引導並進行設定,因此可說是在整個 Laravel 引導過程中最重要的部分

你可能會覺得很好奇為什麼服務供應器的 register() 需要優先於 boot() 來執行。其實很簡單,透過先執行 register() 能夠去完成該服務供應器的初始化,以確保接下來的 boot() 能夠正常執行

第四站 路由

在這些重要的服務供應器當中,其中的超級明星就是 App\Providers\RouteServiceProvider。這個服務供應器載入了存放專案的 routes 資料夾裏頭的路由檔案。再接下來,你可以打開看看 RouteServiceProvider.php ,看看它做了些甚麼事情

小發現

在這檔案你將會發現 $namespace 屬性被註解起來了,這也就是為什麼 8.x開始需要在路由檔寫上完整的控制器命名空間的原因

一旦應用開始引導並註冊了所有的服務供應器,請求將會被轉給路由去處理,再接下來路由又會根據規則來判斷要把這個請求分配給哪一個控制器的哪一個方法來處理,以及是否要執行哪一個中介層等等

中介層提供了一個便利的機制來過濾或者是驗證 HTTP 請求。舉例來說,Laravel 內建了一個中介層用來驗證使用者是否完成登入。假如使用者沒登入的話,中介層會把使用者導到登入頁。反之,如果使用者已經完成登入,中介層就會允許它通過到下一關,直到進入應用裏頭。有些中介層被設計來對應應用裡的所有路由,就像是那些被定義在 HTTP kernel 裏頭 $middleware 屬性內的,如果想了解更多關於中介層的知識,可以參考中介層那一個章節

假如請求通過了所有對應路由所派發的中介層,對應路由就會將之轉給控制器的方法來接手執行,並且接收控制器方法所回傳的回應,再接著將對應傳回也需要通過路由的中介層鏈

旅程終結

當路由或控制器方法回傳一個回應,回應將會傳回並經過路由的中介層,提供應用能有機會去修改或驗證回應

最後,當回應通過了所有中介層,HTTP 核心的處理方法就發出回應物件,而 index.php 檔案就會呼叫回應的 send(),這個 send() 發出回應內容到使用者網頁瀏覽器

太棒了,我們已經體驗過整個Laravel請求的生命週期囉

旅程的省思

請初入門者先專注於服務供應器

服務供應器真的是引導Laravel應用的關鍵。當應用實例被建立之後,服務供應器跟著被註冊,然後請求就一關關的被引導進應用裏頭。就這麼的簡單!

對 Laravel 應用是如何被建立,而且如何透過服務供應器來引導是很用的。你的應用裡的內建服務供應器都被存放在 app/Providers 資料夾內

預設情況下, AppServiceProvider.php 這支檔案裏頭是空的。這個供應器是個很好的地方讓你去加入自己的引導程式以及服務容器綁定。日後當你把應用越寫越大,你也許希望去建立多個服務供應器,每一個服務供應器負責特定的服務


分享這篇文章:

關聯文章:

訂閱電子報,索取 Laravel 學習手冊

價值超過 3000 元,包含常用 Laravel 語法與指令!

Laravel 百萬年薪特訓營

從最基礎的 PHP 語法開始,包含所有你該知道的網頁基礎知識,連同 Laravel 從零開始一直到實戰,最後還將告訴你如何找好工作,讓你及早擁有百萬年薪