摘要:外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。將使用者與子系統從直接耦合,轉變成由外觀類提供統一的接口給使用者使用,以降低客戶端與子系統之間的耦合度。接下來將深入分析外觀服務的加載過程。引導程序將在處理請求是完成引導啟動。
本文首發于 深入淺出 Laravel 的 Facade 外觀系統,轉載請注明出處。
今天我們將學習 Laravel 核心架構中的另一個主題「Facade(外觀)」。
本文將從以下幾個方面出發,全面講解 Laravel 中 Facade 的運行原理,為了便于理解后續中所有 Facade 譯作「外觀」:
簡單介紹「外觀」設計模式;
Laravel「外觀」的加載原理;
Laravel「外觀」基本使用。
什么是「外觀」設計模式 外觀模式定義為子系統中的一組接口提供一個統一的入口。外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
外觀模式是一種使用頻率非常高的結構型設計模式,它通過引入一個外觀角色來簡化客戶端與子系統之間的交互,
為復雜的子系統調用提供一個統一的入口,降低子系統與客戶端的耦合度,且客戶端調用非常方便。 - 設計模式 Java 版
核心 就是在 客戶端(使用者) 與 子系統(接口或服務) 之間引入一個「外觀」角色。
將使用者與子系統從直接耦合,轉變成由「外觀」類提供統一的接口給使用者使用,以降低客戶端與子系統之間的耦合度。
結構示意圖:關于「外觀模式」可以閱讀 設計模式 Java 版 - 外觀模式
Laravel 外觀組件Laravel 中的「外觀」組件實際上是服務容器中底層類的「靜態代理」,它將 Laravel 內核中定義的「Contracts(在 Laravel 中又
稱為服務、契約或者通常我們所說的接口)」,以靜態可調用的方式封裝到各個「外觀」服務中供我們使用。
在講解如何使用外觀組件之前,我們依舊先去深入分析「外觀」組件是如何被 Laravel 加載到項目中的。這一步是
用好「外觀」組件的前提。
所有內置的外觀組件的配置數據,同 Laravel 其它服務一樣被定義在 config/app.php 文件中。讓我們來瀏覽一下 aliases 節點的配置數據吧:
... "aliases" => [ "App" => IlluminateSupportFacadesApp::class, "Artisan" => IlluminateSupportFacadesArtisan::class, ... ], ...
外觀配置定義格式遵循 「別名」:「外觀類」 的數據格式。當一個 HTTP 請求被接收時,將在處理請求階段將這些「外觀」組件加載到服務中。
接下來將深入分析外觀服務的加載過程。
加載外觀服務「外觀」服務的加載工作由定義在 IlluminateFoundationHttpKernel 內核中的 IlluminateFoundationBootstrapRegisterFacades::class 啟動程序完成。
引導啟動外觀服務如果你已經閱讀我的另一篇文章 深入剖析 Laravel 服務提供者實現原理,你應該對引導程序不會太陌生。
引導程序將在處理 HTTP 請求是完成引導啟動 bootstrap()。所以這里我們需要深入到 RegisterFacades 類的內部去了解更多細節上的處理。
make("config")->get("app.aliases", []), $app->make(PackageManifest::class)->aliases() ))->register(); } }
加載外觀服務有 AliasLoader 組件完成:
首先,會從配置文件 config/app.php 中讀取所有的「外觀」服務配置 aliases;
再從清單文件中讀取別名服務 $app->make(PackageManifest::class)->aliases();
將兩個配置數組合并后注入到 AliasLoader 完成 注冊(register)。
注冊外觀服務最后我們來瞧瞧 AliasLoader 加載器是如何將所有的「外觀」服務加載到系統中的。
getAliases(), $aliases); static::$instance->setAliases($aliases); return static::$instance; } /** * Set the registered aliases. 設置需注冊別名數據。 */ public function setAliases(array $aliases) { $this->aliases = $aliases; } /** * Register the loader on the auto-loader stack. 將加載器注冊到自動加載中。 */ public function register() { if (! $this->registered) { $this->prependToLoaderStack(); $this->registered = true; } } /** * Prepend the load method to the auto-loader stack. 設置自動加載方法。 */ protected function prependToLoaderStack() { // 將 AliasLoader 的 load 方法作為 __autoload 的實現 spl_autoload_register([$this, "load"], true, true); } /** * Load a class alias if it is registered.從注冊過的服務中加載這個「外觀」服務。 */ public function load($alias) { if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) { $this->loadFacade($alias); return true; } if (isset($this->aliases[$alias])) { return class_alias($this->aliases[$alias], $alias); } } }
注意 這里是知識點,在 AliasLoader->register() 完成「外服服務注冊」涉及 PHP 兩個知識的應用:
PHP 內置魔術方法 __autoload 的使用;
PHP 如何給類創建別名。
? 1. 外觀服務的動態引入
我們知道 __autoload 魔術方法的作用是嘗試加載未經定義的類,這樣當我們使用一個未經引入的類時,則會自動的給我們引入這個類。
更優的解決方案是通過 spl_autoload_register 函數,將自定義的類加載程序作為 __autoload 的實現,以替代默認 __autoload() 模式函數或方法的行為。
所有 prependToLoaderStack() 方法:
/** * Prepend the load method to the auto-loader stack. 設置自動加載方法。 */ protected function prependToLoaderStack() { // 將 AliasLoader 的 load 方法作為 __autoload 的實現 spl_autoload_register([$this, "load"], true, true); }
就是去完成這樣的作用,將 AliasLoader->load() 方法作為自動加載程序的實現,在使用「外觀」服務時動態引入這個類。
? 2. 支持外觀服務別名
我們已經了解到當「外觀」服務被使用時,由 AliasLoader->load() 去自動加載這個類。
與此同時,load 方法通過 class_alias($original, $alias) 函數完成別名注冊。
這樣,當我們使用 App 類時實際上就是在使用 IlluminateSupportFacadesApp 類。
很完美么,我們的「狗蛋」終于與「世界上最好的語言」畫上了等號。你就是我,我就是你。
到這里其實已經完成了「外觀」服務工作原理分析工作的 70%。
探秘 Facade最后我們將揭開 Facade 的神秘面紗,研究一下 Laravel 是如何實現 Facade 設計模式的。
我們拿 IlluminateSupportFacadesApp 外觀服務開刀,去解開類似 App::make() 靜態方法使用的奧秘。
深入 FacadesApp:
我們看到它的實現內部僅僅定義了一個 getFacadeAccessor 方法,該方法的功能是獲取已注冊組件的名稱 app;除此之外,一無所有。
看來在這里我們得不到什么有用的信息了。繼續調查基類 IlluminateSupportFacadesFacade。如果你有去通便瀏覽全部的源碼。
$method(...$args); } }你會發現這個 Facade 基類并沒有定義類似 make 的方法,那么這里能夠靜態調用 App::make() 看來是需要從 __callStatic 著手才行。
不過在這里我們需要再次厘清一個事實:「外觀」模式的功能是什么?
將使用者與子系統從直接耦合,轉變成由「外觀」類提供統一的接口給使用者使用,以降低客戶端與子系統之間的耦合度。這句話的意思就是我「外觀」啥也不提供,就是一層對服務(或者說組件或接口)的封裝,然后以統一的方式提供給你們外部調用。
好了現在我們來看看 Facade::__callStatic 是如何獲取實際的服務并調用響應的方法的吧。
首先,通過 getFacadeRoot 靜態方法獲取實際服務的實例對象;
然后,調用實例對象的相關方法并返回處理結果。
從 getFacadeRoot 解析對象的功能中我們可以看到:它會調用實現「外觀」的 getFacadeAccessor 方法獲取到組件(服務或者說接口)的名稱;然后從 Laravel 服務容器 static::$app[$name](app 是在 RegisterFacades 中注冊到「外觀」中) 中解析出相關服務。
到這里,我們就將「外觀」服務的基本工作原理給分析透徹了。
另外有關「外觀」組件的一些細枝末節,如:
在文檔「Facades Vs. 輔助函數」一節提到的測試驗證是如何實現的 Cache::shouldReceive("get");
什么是「實時 Facades」。
還是需要你自行深入到 Facade 基類去一探究竟。
掃盲 ArrayAccess 接口另外補充一個知識點就是關于 static::$app[$name] 這一句代碼。你不經要問,這有啥好補充的呢,不就是一個簡單獲取數據么。
獲取數據不假,簡單也不假。
不過你仔細看一下,你會發現 static::$app 靜態成員變量難道不是一個 IlluminateContractsFoundationApplication 實現實例么,怎么可以從對象中以數組的方式獲取值呢?
這是因為我們的服務容器 IlluminateContainerContainer 實現了 ArrayAccess 接口。
該接口的功能是提供像訪問數組一樣訪問對象的能力的接口,這樣就可以像數組一樣訪問對象訪問成員。
/** *@link https://github.com/laravel/framework/blob/5.6/src/Illuminate/Container/Container.php */ class Container implements ArrayAccess, ContainerContract { /** * Get the value at a given offset. 獲取一個偏移位置的值,實際上從容器中解析出服務。 */ public function offsetGet($key) { return $this->make($key); } }Laravel「外觀」基本使用外觀服務的一個典型使用場景是在定義路由時使用 Route::get("/", ...)。這樣一看似乎「Laravel 別名服務」也就不這么神秘了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/28846.html
摘要:本文來自原文鏈接歡迎作客我們的學習群該篇屬于底層核心技術實戰揭秘這一課程底層核心概念解析這一章的擴展閱讀。考慮到學員們的基礎差異,為了避免視頻當中過于詳細而連篇累牘,故將一些底層實現相關的知識點以文章形式呈現,供大家預習和隨時查閱。 本文來自pilishen.com----原文鏈接; 歡迎作客我們的php&Laravel學習群:109256050該篇屬于《Laravel底層核心技術實戰...
摘要:外觀模式的目的在于降低系統的復雜程度。在不引入抽象外觀類的情況下,增加新的子系統可能需要修改外觀類或客戶端的源代碼,違背了開閉原則。 外觀模式 外觀模式(Facade Pattern):外部與一個子系統的通信必須通過一個統一的外觀對象進行,為子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。外觀模式又稱為門面模式,它是一種對象結構型模...
摘要:使用現在,在任何一個控制器,或者路由的回調函數中,使用你會發現,已經可以好好工作了,參考文章設計模式九外觀模式結構型服務容器實例教程深入理解控制反轉和依賴注入服務提供者實例教程創建測試實例 我的博客原文: http://www.qinblog.net/Articl... 前言 laravel 提供了一個靈活的模式,那就是 facade 。框架內部的 DB、Auth、File 等功能也...
摘要:沒有任何意外,王小二的公司用來開發公司的主打產品。臃腫的著手開干吧小二打開熟悉的,找到提交訂單模塊的。要不再去請教下哥的煩惱小二找到哥,詳細的描述了他的問題。 流行的MVC架構模式 如今的Web開發,各種框架風起云涌,勢如破竹。 從國民第一的ThinkPhp到稱霸全球的Laravel,這些框架有一個共同特征,都采用了MVC的架構模式。 showImg(https://segmentfa...
摘要:可以為服務提供者的方法設置類型提示。方法將在所有其他服務提供者均已注冊之后調用。所有服務提供者都在配置文件中注冊。可以選擇推遲服務提供者的注冊,直到真正需要注冊綁定時,這樣可以提供應用程序的性能。 本文最早發布于 Rootrl的Blog 導言 Laravel是一款先進的現代化框架,里面有一些概念非常重要。在上手Laravel之前,我認為先弄懂這些概念是很有必要的。你甚至需要重溫下PHP...
閱讀 1273·2021-09-27 13:35
閱讀 2574·2021-09-06 15:12
閱讀 3389·2019-08-30 15:55
閱讀 2838·2019-08-30 15:43
閱讀 440·2019-08-29 16:42
閱讀 3451·2019-08-29 15:39
閱讀 3071·2019-08-29 12:28
閱讀 1248·2019-08-29 11:11