Laravel 生命週期介紹

iffor發表於2018-07-08

本文嘗試在程式碼的角度對 Laravel 的生命週期進行說明。

概括來說 Laravel 的生命週期由以下幾個階段組成:

  • Application 初始化;
  • Kernel 和 Exception Handler 註冊到Application容器物件上,Kernel 分為 HttpKernel 和 ConsoleKernel;
  • 使用 Application 物件建立 Kernel;
  • 建立 Request 物件來包裝客戶端相關的請求資料;
  • 把 Request 物件交予 Kernel 的 handle 方法處理,生成 Response 物件;
  • 使用 Response 的 send 方法,把相關的輸出返回給客戶端;
  • 呼叫 Kernel 的 terminate 方法進行相關的清理操作,至此整個請求結束。

上面在巨集觀的層面介紹了 Laravel 的生命週期,每個生命週期都實現了特定的功能,關於這些功能的細節將在下面的進行初步介紹。

Application 初始化

Application 是 Laravel 中最為核心的物件,如果沒有它 Laravel 中的一切物件就都將不存在。

Application 物件是一個 應用 容器 物件。換句話說它即是一個容器也是一個應用程式物件。作為一個表示「應用」的物件,提供了應用的基本資訊,例如:路徑相關,當前執行環境等相關基礎資訊;作為一個「容器」物件,提供了相關物件註冊,建立,儲存等相關的功能,在 Laravel 中各個元件中都可以非常方便的獲取當前的 Application 物件,所以可以簡單簡單粗暴的把 Application 理解為一個全域性的單例物件,所有需要全域性共享的資料都可以儲存到 Application 上。

Application 物件的初始化過程如下:

定義專案的目錄結構

設定基礎路徑 $basePath ,以 $basePath 路徑為基礎, 定義了專案目錄結構 ,並且把相關的目錄結構儲存到 Application 物件上。下文以./ 表示基礎路徑 $basePath 具體的目錄結構如下:

  • path,對應的目錄為 ./app,裡面包含了專案相關的核心程式碼,例如ControlerMiddleware 都在這一層;
  • path.base,目錄為 ./ ,專案的基礎目錄;
  • path.resources ,目錄為 ./resources, 模版檔案和沒有被編譯過的css和js原始碼,圖片等相關資原始檔放這個這個目錄,如果應用程式設計到多語音,那麼多語言檔案也在這個目錄;
  • path.lang ,目錄為 ./resources/lang ,多語言配置檔案所在的目錄;
  • path.public,目錄為 ./public, web server 的根目錄,web server 只能訪問到這個目錄,主要包含了專案的入口檔案 index.php,編譯好的 css 和 js 等相關資原始檔都要拷貝的這個目錄。
  • path.config,目錄為 ./config ,配置檔案所在的目錄,
  • path.storage 目錄為 ./storage,這個目錄需要寫入的許可權,主要包含了3類在專案執行的過程中會產生的檔案:快取檔案,session 檔案,日誌檔案。
  • path.database,目錄為 ./database,這個目錄存放了資料庫相關的檔案,主要分為3類:資料庫結構生成器檔案,種子資料檔案,建立種子資料工廠類檔案。
  • path.bootstrap,目錄為 ./bootstrap,這個目錄包含了最外層的引導檔案app.php,這個檔案建立了 Application 物件,該目錄的 ./bootstrap/cache 還生成了快取 ServiceProvder配置檔案

儲存基礎例項物件到 Application 上

設定了容器的單例物件,然後儲存了兩個例項物件到 Application 。

  • static::setInstance($this); 把當前 Application 物件作為容器單例儲存到容器
  • $this->instance(Container::class, $this); 把當前 Application 物件儲存到容器
  • 建立 PackageManifest 物件,並把該資料儲存到 Application 物件,這個物件的主要作用是分析 ./vendor/installed.json 檔案,把 laravel 相關的配置讀取出來,例如 ServiceProvider等

註冊基礎的服務提供者(Service Provider)

註冊了 3 個基礎的服務提供者。

  • EventServiceProvider,事件模組,事件模組是 Laravel 實現可擴充套件性的一個重要的工具;
  • LogServiceProvider,日誌模組;
  • RoutingServiceProvider,路由模組,該模組的主要作用是把請求轉發到具體的處理器上;

定義核心容器的相關別名

本質來說就是把別名和真正的類之間做了一層對映關係,我覺得這個別名系統的缺點大於優點,優點無非就是所謂的寫起來“簡潔”,“簡潔”的同時會帶來閱讀上的不直觀,缺點首先是需要迴圈去定義對映關係,其實是這個對映關係(陣列)需要使用額外的記憶體是儲存。

Kernel 的建立

這裡只介紹 HttpKernel 相關的功能和初始化的過程。

Kernel 是位於 Application 上層的物件,相關的初始化操作和請求的處理都是由 Kernel 來處理的。

初始化的過程

Kernel 是使用 Application 容器物件建立的。

  • Kernel 的建構函式定義了 Application 和 Router 型別的引數,這兩個引數在 Application 都在已經在 Application 上註冊或者建立好了;
  • $middlewarePriority 賦值給 Router 物件,這類中介軟體由優先順序的設定;
  • $routeMiddleware 定義的分組中介軟體賦值給 Router 物件;
  • $routeMiddleware 別名儲存到 Router 物件上。

根據上面幾個步驟來看,中介軟體的執行都是在 Router 上實現的,Kernel 只是負責定義而已。

Kernel 的 Handle 方法執行的流程

Handle 方法的引數需求是需要一個 Request 物件,這個 Request 物件在 index.php 已經建立。

具體的流程如下:

  • 設定 Request 物件的 http 方法是可以通過 _method 引數覆蓋的,目前我還發現這個功能的使用實際的使用場景;

  • Request 物件儲存的 Application 容器裡;

  • 執行 Kernel 上定義的引導器列表,相關的自定義的初始化操作都是通過這個步驟來實現的, 比如:環境變數和配置檔案的載入;服務提供者的註冊和引導等;

  • 建立 Pipeline 物件,把 Request 物件交給 Pipeline 物件處理;處理順序先由 $middleware 屬性定義的中介軟體列表處理完之後交給 Router 物件處理,處理完成之後會生成一個 Response 物件;

  • 異常的處理,新版 PHP 所有可以丟擲/捕獲的異常都實現了 Throwable 介面,首先對 Exception 異常進行捕獲,然後交給 renderException 方法去處理返回, 對於非 Exception 的可丟擲/捕獲的異常(實際就是Error)會把它包裝為 FatalThrowableError 物件,然後再給 renderException 方法處理返回;

  • 最後派發 RequestHandled 事件, 該事件表明對 Request的相關處理已經完成, 事件系統已經在 Application 物件初始化的時候已經建立,所以這裡可以直接用 Application 取出來使用。

在上面介紹的步驟中其中一個叫做「執行 Kernel 上定義的 $bootstrapers 列表」。下面對相關的 bootstrapers 列表進行說明。

$bootstrappers(引導器) 列表

引導器具體的執行者是 Application 應用程式物件,為避免多次執行,Application 會在執行完成之後進行標記為已經引導。具體的引導過程如下:

  • 標記 $hasBeenBootstrapped 屬性為 true
  • 遍歷引導器列表,每個引導器在之後之前會派發bootstrapping:引導器名稱的事件;
  • 然後使用 Application 容器物件建立引導器物件,然後呼叫引導器物件的 bootstrap 方法;
  • 最後派發 bootstrapped:引導器名稱的事件。

透過事件系統,來實現可擴充套件性在這裡就能體現出來。

具體定義的引導器列表為:

  • LoadEnvironmentVariables, 載入「.env」 檔案中定義的環境變數;
  • LoadConfiguration,載入配置檔案;
  • HandleExceptions,設定相關係統級別的異常處理;
  • RegisterFacades,註冊 Facades 列表;
  • RegisterProviders,載入註冊服務提供者(Service Provider);
  • BootProviders,執行已經註冊的服務提供者的boot方法;

至此相關的引導器已經執行完成,在後面的文章中會對每個引導器詳細的介紹。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章