分析 AliasLoader
昨天了解 Facade 基本原理後,可能會覺得奇妙的關鍵不過就是 Magic Method 而已,但其實 Laravel 還有更神奇的。
下面這兩個呼叫的結果是一樣的:
\Illuminate\Support\Facades\Request::ip(); |
正因為有這個設計,所以預設 routes
下的設定可以這樣寫:
Route::get('/', function () { |
這功能是由 AliasLoader 實作的,我們來看看它如何被使用:
AliasLoader::getInstance(array_merge( |
這裡會把 config/app.php
的 aliases 設定與 PackageManifest 所取得的 aliases 合併,然後當做參數傳給 getInstance()
,接著再註冊。
這裡注意到,PackageManifest 是會有機會覆蓋原有 config 的設定。
以上面的 Request 與 Route 為例,aliases 設定如下:
[ |
再來看 getInstance()
是如何處理這份設定的:
public static function getInstance(array $aliases = []) |
它實作了單例模式,以及把設定更新。會更新實例的設定理由很明顯,正是為了測試。
只是一樣是產實例,為何它要特別使用單例,而不是被 Container 管控?接著看下去就會了解了。
產生實例後,會呼叫 register()
方法:
public function register() |
prependToLoaderStack()
方法:
protected function prependToLoaderStack() |
spl_autoload_register()
實現了自動載入機制。Composer 正是使用這個函式實作了自動載入,可以參考它的 AutoloadGenerator 是如何實作的。
PHP 的自動載入機制,是使用一個 list,裡面每個元素都是實作自動載入的方法。當要使用某個類別,可是它還沒被載入時,就會拿出這個 list 嘗試載入。載入的結果會有成功或失敗,當載入成功後,後面的方法就不需要再執行了;失敗才需要換下一個。
某種程度蠻像 Routing 的設計:RouteCollection = list、Route = 自動載入方法、Request = 未載入的類別、而 spl_autoload_register() 的任務就類似 Router。
spl_autoload_register()
的參數有三個,第一個是 callable,也就是自動載入的方法;第二個是註冊失敗要不要丟例外;第三個是要不要把自動載入的順序往前移。
從原始碼可以知道,這個載入方法會提前到第一個順位,並且使用 load()
當作自動載入方法:
public function load($alias) |
回到一開始的設定範例:
[ |
使用 class_alias()
之後,就能讓對 Request
的靜態操作,可以跟操作 Illuminate\Support\Facades\Request
完全一模一樣。
接著看更神奇的 Real-Time Facades,今天才發現有這個更 magic 的東西。Facade 雖然好用,但很麻煩的是,因為它是靜態操作,意謂著需要做一些靜態的前置作業。說更簡單一點就是,要事前準備好程式才能用。Real-Time Facades 正是解決這個麻煩事,它可以動態產生 Facade。
5.4 版之後開始支援 Real-Time Facades
說明這麼多,不如直接看範例,下面這四段程式碼回傳的結果是一樣的:
\Illuminate\Support\Facades\Request::ip(); |
前兩個範例在前面已經說明了。\Illuminate\Http\Request::capture()->ip()
這個為何能正常執行,可以參考一開始分析 bootstrap 流程是如何產生 request 實例的。
最後一個就是神奇的 Real-Time Facades 使用方法。回到剛剛的 load()
程式片段:
// static::$facadeNamespace 預設是 `Facades\\`,只要開頭是這串字的就會執行 |
從上面的程式碼看,可以猜想得到 ensureFacadeExists()
會產生檔案,並回傳檔案路徑:
protected function ensureFacadeExists($alias) |
以 Facades\Illuminate\Http\Request
為例,三個會被置換的字串如下:
- DummyNamespace - Facades\Illuminate\Http
- DummyClass - Request
- DummyTarget - Illuminate\Http\Request
程式碼很單純,換來看一下 stub 裡面長什麼樣:
<?php |
getFacadeAccessor()
最後回傳的會是 Illuminate\Http\Request
,再來就如同 Facade 的分析--會去 Container 取得實例並呼叫。
整個分析下來後,其實可以發現 Facade 是一個活用 PHP 特性的好範例,Real-Time Facades 也相當有趣,非常值得大家參考。