Facade 布局是在面向?qū)ο缶幊讨薪?jīng)常使用的一種軟件設(shè)計(jì)布局方式。Facade 實(shí)際上是一種包括復(fù)雜函數(shù)庫的類野瘦,提供了更加簡潔易讀的接口。Facade 布局還能為一組結(jié)構(gòu)復(fù)雜飒泻、設(shè)計(jì)簡陋的 API 提供統(tǒng)一鞭光、設(shè)計(jì)周到的 API。
Laravel 框架與該布局的特點(diǎn)相似惰许,也稱為 Facades。在本教程中史辙,我們會(huì)學(xué)習(xí)如何在其他框架應(yīng)用 Laravel 的 “Facades”汹买。在繼續(xù)學(xué)習(xí)之前佩伤,讓我們簡單了解一下 Ioc 容器。
首先卦睹,我們了解 Laravel 的 facades 內(nèi)部工作結(jié)構(gòu)畦戒。之后再討論如何將之改造并用于其他環(huán)境。
Laravel 中的 Facades
Laravel facade 是一種為容器內(nèi)部服務(wù)提供類似靜態(tài)接口的類结序。據(jù)其文檔描述障斋,F(xiàn)acades 是可觸及容器服務(wù)底層實(shí)現(xiàn)方式的代理。
不過徐鹤,在 PHP 社區(qū)垃环,有關(guān)其名稱的爭論一直不斷。一些人堅(jiān)持修改此名稱以避免開發(fā)者的困惑返敬,因?yàn)槠洳⑽赐耆珜?shí)現(xiàn) Facade 布局遂庄。如果你也受此名稱困擾,大可以為其取個(gè)別名劲赠。但是涛目,請注意,下文將會(huì)用到的 Laravel 框架基類(base class)將會(huì)稱為 Facade凛澎。
How Facades Are implemented in Laravel
Facades 在 Laravel 中如何實(shí)現(xiàn)
你可能也知道霹肝,容器內(nèi)的每個(gè)服務(wù)都有個(gè)唯一名稱。在 laravel 應(yīng)用中塑煎,可使用 App::make()
方法或 app()
輔助函數(shù)從容器中直接獲取服務(wù)沫换。
<?php
App::make('some_service')->methodName();
前面已經(jīng)提過,Laravel 使用 facade 類的好處是讓開發(fā)者使用服務(wù)時(shí)更加便捷最铁。使用 facade 類之后讯赏,下面的代碼就能達(dá)到相同的效果:
// ...
someService::methodName();
// ...
在 Laravel 中,所有服務(wù)都包含一個(gè) facade 類冷尉。這些 facade 類繼承自 Illuminate/Support
包中的 Facade 基類漱挎。它們只需實(shí)現(xiàn) getFacadeAccessor
方法即可,后者會(huì)返回容器內(nèi)的服務(wù)名雀哨。
在上面的示例中识樱,someService
代表 facade 類。methodName
其實(shí)是容器內(nèi)原服務(wù)的一個(gè)方法震束。如果跳出 Laravel 的語境查看上面的示例,則表示一個(gè)名為 someService
的類引出名為 methodName()
的靜態(tài)方法当犯。但 Laravel 并不是這樣實(shí)現(xiàn)接口的垢村。在下一節(jié),我們將介紹 Laravel 的 Facade 基類在幕后的運(yùn)作方式嚎卫。
Base Facade
Facade 類包含一個(gè)名為 $app
的私有屬性嘉栓,其值為服務(wù)容器的引用宏榕。如果要在 Laravel 之外使用 facades,必須使容器明確使用 setFacadeApplication()
方法侵佃。
在 facade 基類內(nèi)部麻昼,__callStatic
魔術(shù)方法用于處理實(shí)際并不存在的靜態(tài)方法的調(diào)用。如果調(diào)用 Laravel facade 類的靜態(tài)方法馋辈, __callStatic
方法便會(huì)激活抚芦,因?yàn)?facade 類并未實(shí)現(xiàn)該方法。因此叉抡,__callStatic
會(huì)從容器獲取各自的服務(wù),進(jìn)而調(diào)用之答毫。
以下是 facade 基類中 __callStatic
方法的實(shí)現(xiàn)方式:
<?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()
會(huì)從容器獲取服務(wù)。
Facade 類解析
每個(gè) facade 類均繼承自基類洗搂。我們只需實(shí)現(xiàn) getFacadeAccessor()
方法消返,該方法用于返回容器中的服務(wù)名。
<?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 類耘拇,在使用之前我們得導(dǎo)入它們撵颊。PHP 支持命名空間與自動(dòng)導(dǎo)入,因此只要調(diào)用全限定名驼鞭,即可自動(dòng)載入這些類秦驯。PHP 還支持使用 use
指令給類取別名:
use App\Facades\SomeServiceFacade
SomeServiceFacade:SomeMethod();
然而,在需要某個(gè)特定的 facade 類時(shí)挣棕,我們必須在每個(gè)腳本文件都寫一遍上面的代碼译隘。Laravel 在處理 facade 別名時(shí)有其獨(dú)特的方法——?jiǎng)e名載入器(alias loader)。
Laravel 如何給 Facades 加別名
所有的別名都保存在 app.php
配置文件的 aliases
數(shù)組中洛心,該文件保存在 /config
目錄下固耘。
查看該數(shù)組,會(huì)發(fā)現(xiàn)每個(gè)別名都與一個(gè)全限定類名對應(yīng)词身。這意味著我們可以給 facade 類選定任意的名字厅目。
// ..
'aliases' => [
// ...
'FancyName' => 'App\Facades\SomeServiceFacade',
],
現(xiàn)在,讓我們看看 Laravel 如何使用該數(shù)組給 facade 類取別名法严。在引導(dǎo)階段损敷,Laravel 會(huì)使用來自 Illuminate\Foundation
包的 AliasLoader
服務(wù)。AliasLoader
以該別名數(shù)組為參數(shù)深啤,遍歷其所有元素拗馒,使用 PHP 的 spl_autoload_register 創(chuàng)建一個(gè) __autoload
函數(shù)隊(duì)列。各個(gè) __autoload
函數(shù)會(huì)用 PHP 的 class_alias 函數(shù)為各個(gè) facade 類創(chuàng)建別名溯街。
因此诱桂,我們無需像使用 use
指令時(shí)那樣在使用類前導(dǎo)入之并為其創(chuàng)建別名洋丐。當(dāng)我們試圖使用一個(gè)不存在的類時(shí),PHP 會(huì)檢查 __autoload
隊(duì)列以得到合適的 autoloader挥等。這時(shí)友绝,AliasLoader
已經(jīng)記下所有的 __autoload
函數(shù)。各個(gè) autoloader 會(huì)選定一個(gè)類名并根據(jù)別名數(shù)組推導(dǎo)出對應(yīng)的初始類名肝劲。最后迁客,它會(huì)為其創(chuàng)建別名。請參考下面的方法調(diào)用:
<?php
// FancyName is resolved to App\Facades\SomeServiceFacade according to the aliases array
FancyName::someMethod()
在幕后涡相,FancyName
會(huì)對應(yīng)至 App\Facades\SomeServiceFacade
哲泊。
在其他框架使用 Facades
現(xiàn)在,我們已經(jīng)了解 Laravel 如何處理 facades 與別名催蝗,我們可以將 Laravel 的 facade 方法運(yùn)用到其他環(huán)境中切威。接下來,我們會(huì)在 Silex 框架使用 facades丙号。然而先朦,只要遵循同樣的理念,你也可以將之用在別的框架犬缨。
Silex 擁有繼承自 Pimple
的容器喳魏。使用 $app
對象即可調(diào)用容器內(nèi)的服務(wù):
<?php
$app['some.service']->someMethod()
有了 facade 類,我們可以為 Silex 服務(wù)提供一個(gè)類似靜態(tài)的接口怀薛。此外刺彩,我們也可以使用 AliasLoader
服務(wù)為這些 facades 創(chuàng)建有意義的別名。因此枝恋,我們可以重組上面的代碼:
<?php
SomeService::someMethod();
必備條件
為了使用 facade 基類创倔,我們要使用 composer
指令安裝 Illuminate\Support
包:
composer require illuminate\support
此包還包含其他服務(wù)。但目前我們只需要 facade 基類焚碌。
創(chuàng)建 Facades
只需繼承 Facade 基類并實(shí)現(xiàn) getFacadeAccessor
方法畦攘,即可為服務(wù)創(chuàng)建 facade。
在本文中十电,所有 facades 都會(huì)保存在 src/Facades
路徑下知押。例如:名為 some.service
的服務(wù),其 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
命名空間下台盯。
現(xiàn)在只剩下設(shè)定 facade 類的應(yīng)用容器静盅。如前所述,在靜態(tài)語境下調(diào)用 facade 類的方法象踊,會(huì)觸發(fā) __callStatic
方法。該方法會(huì)用 getFacadeAccessor()
返回的數(shù)據(jù)識別容器內(nèi)的服務(wù)杯矩,并試圖獲取之。在 Laravel 之外使用 facade 基類時(shí)史隆,容器對象并不是自動(dòng)設(shè)定的,需要手動(dòng)設(shè)定泌射。
為此粘姜,使用 facade 基類的 setFacadeApplication
方法,可以設(shè)定 facade 類的應(yīng)用容器孤紧。
在 app.php
文件,添加以下代碼:
<?php
Illumiante\Support\Facade::setFacadeApplication($app);
這會(huì)給繼承自 facade 基類的所有 facades 設(shè)定容器拒秘。
現(xiàn)在号显,無需直接從容器獲取服務(wù),我們可以使用剛剛創(chuàng)建的 facade 類來獲取躺酒,該類還允許我們調(diào)用靜態(tài)語境下的所有方法押蚤。
實(shí)現(xiàn)別名
為了給 facade 類創(chuàng)建別名,我們將使用之前介紹過的 AliasLoader
羹应。AliasLoader
類由 illuminate\foundation
包提供揽碘,可以下載整個(gè)包,也可以拷貝部分代碼保持為文件园匹。
如果你想拷貝源文件雳刺,建議將其保存在 src/Facades
目錄下。你可以根據(jù)項(xiàng)目的結(jié)構(gòu)為 AliasLoader
類創(chuàng)建命名空間偎肃。
在本例中煞烫,我們將拷貝代碼并將其保存在 app/facades
命名空間下。
創(chuàng)建別名數(shù)組
在 config
目錄下創(chuàng)建 aliases.php
文件累颂,并填入 alias-facade 綁定:
<?php
return [
'FancyName' => 'App\Facades\SomeService',
];
FancyName
是我們給 App\Facades\SomeService
建立的別名滞详。
注冊別名
AliasLoader
是一種單例服務(wù)。要?jiǎng)?chuàng)建或得到別名載入器(alias loader)的實(shí)例紊馏,需調(diào)用 getInstance
方法并以別名數(shù)組為參數(shù)料饥。最后,為了注冊這些別名朱监,需調(diào)用其 register
方法岸啡。
再次打開 app.php
文件,加入以下代碼:
<?php
// ...
$aliases = require __DIR__ . '/../../config/aliases.php';
App\Facades\AliasLoader::getInstance($aliases)->register();
現(xiàn)在赫编,大功告成了巡蘸!我們可以這樣使用該服務(wù):
<?php
FancyName::methodName();
進(jìn)行包裝
一個(gè) Facade 類只需實(shí)現(xiàn) getFacadeAccessor
方法即可奋隶,后者會(huì)返回容器內(nèi)的服務(wù)名。若要在 Laravel 環(huán)境外使用 facade悦荒,必須使用 setFacadeApplication()
方法明確設(shè)定服務(wù)容器唯欣。
要引用 facade 類,我們可以使用全限定類名或使用 PHP 的 use
指令導(dǎo)入之搬味【城猓或者,遵循 Laravel 給 facades 創(chuàng)建別名的方法碰纬,使用 alias loader萍聊。
原文鏈接:http://www.sitepoint.com/how-laravel-facades-work-and-how-to-use-them-elsewhere/ (作者:Reza Lavaryan)本文系 OneAPM 工程師編譯整理。
OneAPM for PHP 能夠深入到所有 PHP 應(yīng)用內(nèi)部完成應(yīng)用性能管理 能夠深入到所有 PHP 應(yīng)用內(nèi)部完成應(yīng)用性能管理和監(jiān)控悦析,包括代碼級別性能問題的可見性寿桨、性能瓶頸的快速識別與追溯、真實(shí)用戶體驗(yàn)監(jiān)控牛隅、服務(wù)器監(jiān)控和端到端的應(yīng)用性能管理酌泰。想閱讀更多技術(shù)文章陵刹,請?jiān)L問 OneAPM 官方技術(shù)博客。
本文轉(zhuǎn)自 OneAPM 官方博客