Facade 佈局是在面向對象編程中常用的一種軟件設計佈局方式。Facade 其實是一種包括複雜函數庫的類,提供了更加簡潔易讀的接口。Facade 佈局還能爲一組結構複雜、設計簡陋的 API 提供統1、設計周到的 API。php
Laravel 框架與該佈局的特色類似,也稱爲 Facades。在本教程中,咱們會學習如何在其餘框架應用 Laravel 的 「Facades」。在繼續學習以前,讓咱們簡單瞭解一下 Ioc 容器。html
首先,咱們瞭解 Laravel 的 facades 內部工做結構。以後再討論如何將之改造並用於其餘環境。laravel
Laravel facade 是一種爲容器內部服務提供相似靜態接口的類。據其文檔描述,Facades 是可觸及容器服務底層實現方式的代理。編程
不過,在 PHP 社區,有關其名稱的爭論一直不斷。一些人堅持修改此名稱以免開發者的困惑,由於其並未徹底實現 Facade 佈局。若是你也受此名稱困擾,大能夠爲其取個別名。可是,請注意,下文將會用到的 Laravel 框架基類(base class)將會稱爲 Facade。數組
你可能也知道,容器內的每一個服務都有個惟一名稱。在 laravel 應用中,可以使用 App::make()
方法或 app()
輔助函數從容器中直接獲取服務。服務器
<?php App::make('some_service')->methodName();
前面已經提過,Laravel 使用 facade 類的好處是讓開發者使用服務時更加便捷。使用 facade 類以後,下面的代碼就能達到相同的效果:app
// ... someService::methodName(); // ...
在 Laravel 中,全部服務都包含一個 facade 類。這些 facade 類繼承自 Illuminate/Support
包中的 Facade 基類。它們只需實現 getFacadeAccessor
方法便可,後者會返回容器內的服務名。composer
在上面的示例中,someService
表明 facade 類。methodName
實際上是容器內原服務的一個方法。若是跳出 Laravel 的語境查看上面的示例,則表示一個名爲 someService
的類引出名爲 methodName()
的靜態方法。但 Laravel 並非這樣實現接口的。在下一節,咱們將介紹 Laravel 的 Facade 基類在幕後的運做方式。框架
Facade 類包含一個名爲 $app
的私有屬性,其值爲服務容器的引用。若是要在 Laravel 以外使用 facades,必須使容器明確使用 setFacadeApplication()
方法。函數
在 facade 基類內部,__callStatic
魔術方法用於處理實際並不存在的靜態方法的調用。若是調用 Laravel facade 類的靜態方法, __callStatic
方法便會激活,由於 facade 類並未實現該方法。所以,__callStatic
會從容器獲取各自的服務,進而調用之。
如下是 facade 基類中 __callStatic
方法的實現方式:
<?php // ... /** * Handle dynamic, static calls to the object. * * @param string $method * @param array $args * @return mixed */ public static function __callStatic($method, $args) { $instance = static::getFacadeRoot(); switch (count($args)) { case 0: return $instance->$method(); case 1: return $instance->$method($args[0]); case 2: return $instance->$method($args[0], $args[1]); case 3: return $instance->$method($args[0], $args[1], $args[2]); case 4: return $instance->$method($args[0], $args[1], $args[2], $args[3]); default: return call_user_func_array([$instance, $method], $args); } }
在上面的方法中,getFacadeRoot()
會從容器獲取服務。
每一個 facade 類均繼承自基類。咱們只需實現 getFacadeAccessor()
方法,該方法用於返回容器中的服務名。
<?php namespace App\Facades; use Illuminate\Support\Facades\Facade as BaseFacade; class SomeServiceFacade extends BaseFacade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'some.service'; } }
因爲 Laravel facades 是 PHP 類,在使用以前咱們得導入它們。PHP 支持命名空間與自動導入,所以只要調用全限定名,便可自動載入這些類。PHP 還支持使用 use
指令給類取別名:
use App\Facades\SomeServiceFacade SomeServiceFacade:SomeMethod();
然而,在須要某個特定的 facade 類時,咱們必須在每一個腳本文件都寫一遍上面的代碼。Laravel 在處理 facade 別名時有其獨特的方法——別名載入器(alias loader)。
全部的別名都保存在 app.php
配置文件的 aliases
數組中,該文件保存在 /config
目錄下。
查看該數組,會發現每一個別名都與一個全限定類名對應。這意味着咱們能夠給 facade 類選定任意的名字。
// .. 'aliases' => [ // ... 'FancyName' => 'App\Facades\SomeServiceFacade', ],
如今,讓咱們看看 Laravel 如何使用該數組給 facade 類取別名。在引導階段,Laravel 會使用來自 Illuminate\Foundation
包的 AliasLoader
服務。AliasLoader
以該別名數組爲參數,遍歷其全部元素,使用 PHP 的 spl_autoload_register 建立一個 __autoload
函數隊列。各個 __autoload
函數會用 PHP 的 class_alias 函數爲各個 facade 類建立別名。
所以,咱們無需像使用 use
指令時那樣在使用類前導入之併爲其建立別名。當咱們試圖使用一個不存在的類時,PHP 會檢查 __autoload
隊列以獲得合適的 autoloader。這時,AliasLoader
已經記下全部的 __autoload
函數。各個 autoloader 會選定一個類名並根據別名數組推導出對應的初始類名。最後,它會爲其建立別名。請參考下面的方法調用:
<?php // FancyName is resolved to App\Facades\SomeServiceFacade according to the aliases array FancyName::someMethod()
在幕後,FancyName
會對應至 App\Facades\SomeServiceFacade
。
如今,咱們已經瞭解 Laravel 如何處理 facades 與別名,咱們能夠將 Laravel 的 facade 方法運用到其餘環境中。接下來,咱們會在 Silex 框架使用 facades。然而,只要遵循一樣的理念,你也能夠將之用在別的框架。
Silex 擁有繼承自 Pimple
的容器。使用 $app
對象便可調用容器內的服務:
<?php $app['some.service']->someMethod()
有了 facade 類,咱們能夠爲 Silex 服務提供一個相似靜態的接口。此外,咱們也可使用 AliasLoader
服務爲這些 facades 建立有意義的別名。所以,咱們能夠重組上面的代碼:
<?php SomeService::someMethod();
爲了使用 facade 基類,咱們要使用 composer
指令安裝 Illuminate\Support
包:
composer require illuminate\support
此包還包含其餘服務。但目前咱們只須要 facade 基類。
只需繼承 Facade 基類並實現 getFacadeAccessor
方法,便可爲服務建立 facade。
在本文中,全部 facades 都會保存在 src/Facades
路徑下。例如:名爲 some.service
的服務,其 facade 類以下:
<?php namespace App\Facades use Illuminate\Support\Facades\Facade; class SomeServiceFacade extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'some.service'; } }
請注意,此類位於 app\facades
命名空間下。
如今只剩下設定 facade 類的應用容器。如前所述,在靜態語境下調用 facade 類的方法,會觸發 __callStatic
方法。該方法會用 getFacadeAccessor()
返回的數據識別容器內的服務,並試圖獲取之。在 Laravel 以外使用 facade 基類時,容器對象並非自動設定的,須要手動設定。
爲此,使用 facade 基類的 setFacadeApplication
方法,能夠設定 facade 類的應用容器。
在 app.php
文件,添加如下代碼:
<?php Illumiante\Support\Facade::setFacadeApplication($app);
這會給繼承自 facade 基類的全部 facades 設定容器。
如今,無需直接從容器獲取服務,咱們可使用剛剛建立的 facade 類來獲取,該類還容許咱們調用靜態語境下的全部方法。
爲了給 facade 類建立別名,咱們將使用以前介紹過的 AliasLoader
。AliasLoader
類由 illuminate\foundation
包提供,能夠下載整個包,也能夠拷貝部分代碼保持爲文件。
若是你想拷貝源文件,建議將其保存在 src/Facades
目錄下。你能夠根據項目的結構爲 AliasLoader
類建立命名空間。
在本例中,咱們將拷貝代碼並將其保存在 app/facades
命名空間下。
在 config
目錄下建立 aliases.php
文件,並填入 alias-facade 綁定:
<?php return [ 'FancyName' => 'App\Facades\SomeService', ];
FancyName
是咱們給 App\Facades\SomeService
創建的別名。
AliasLoader
是一種單例服務。要建立或獲得別名載入器(alias loader)的實例,需調用 getInstance
方法並以別名數組爲參數。最後,爲了註冊這些別名,需調用其 register
方法。
再次打開 app.php
文件,加入如下代碼:
<?php // ... $aliases = require __DIR__ . '/../../config/aliases.php'; App\Facades\AliasLoader::getInstance($aliases)->register();
如今,大功告成了!咱們能夠這樣使用該服務:
<?php FancyName::methodName();
一個 Facade 類只需實現 getFacadeAccessor
方法便可,後者會返回容器內的服務名。若要在 Laravel 環境外使用 facade,必須使用 setFacadeApplication()
方法明確設定服務容器。
要引用 facade 類,咱們可使用全限定類名或使用 PHP 的 use
指令導入之。或者,遵循 Laravel 給 facades 建立別名的方法,使用 alias loader。
原文連接:http://www.sitepoint.com/how-laravel-facades-work-and-how-to-use-them-elsewhere/ (做者:Reza Lavaryan)本文系 OneAPM 工程師編譯整理。
OneAPM for PHP 可以深刻到全部 PHP 應用內部完成應用性能管理 可以深刻到全部 PHP 應用內部完成應用性能管理和監控,包括代碼級別性能問題的可見性、性能瓶頸的快速識別與追溯、真實用戶體驗監控、服務器監控和端到端的應用性能管理。想閱讀更多技術文章,請訪問 OneAPM 官方技術博客。
本文轉自 OneAPM 官方博客