如何使用 Laravel Facades ?

Facade 布局是在面向?qū)ο缶幊讨薪?jīng)常使用的一種軟件設(shè)計(jì)布局方式。Facade 實(shí)際上是一種包括復(fù)雜函數(shù)庫的類野瘦,提供了更加簡潔易讀的接口。Facade 布局還能為一組結(jié)構(gòu)復(fù)雜飒泻、設(shè)計(jì)簡陋的 API 提供統(tǒng)一鞭光、設(shè)計(jì)周到的 API。

如何使用 Laravel Facades 泞遗?

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 官方博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末也糊,一起剝皮案震驚了整個(gè)濱河市狸剃,隨后出現(xiàn)的幾起案子钞馁,更是在濱河造成了極大的恐慌僧凰,老刑警劉巖熟丸,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異怀大,居然都是意外死亡呀闻,警方通過查閱死者的電腦和手機(jī)总珠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門局服,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淫奔,“玉大人堤结,你說我怎么就攤上這事√圃穑” “怎么了瘾带?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵朴恳,是天一觀的道長允蚣。 經(jīng)常有香客問我,道長森渐,這世上最難降的妖魔是什么章母? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任翩剪,我火速辦了婚禮,結(jié)果婚禮上秫逝,老公的妹妹穿的比我還像新娘违帆。我一直安慰自己,他們只是感情好刷后,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布尝胆。 她就那樣靜靜地躺著含衔,像睡著了一般二庵。 火紅的嫁衣襯著肌膚如雪催享。 梳的紋絲不亂的頭發(fā)上因妙,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天兰迫,我揣著相機(jī)與錄音,去河邊找鬼涡拘。 笑死鳄乏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的橱野。 我是一名探鬼主播水援,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼蜗元,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了薪鹦?” 一聲冷哼從身側(cè)響起惯豆,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤框仔,失蹤者是張志新(化名)和其女友劉穎拄养,沒想到半個(gè)月后瘪匿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棋弥,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡顽染,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年粉寞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唧垦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片液样。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坊秸,死狀恐怖褒搔,靈堂內(nèi)的尸體忽然破棺而出站超,到底是詐尸還是另有隱情,我是刑警寧澤死相,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布算撮,位于F島的核電站肮柜,受9級特大地震影響审洞,放射性物質(zhì)發(fā)生泄漏莱睁。R本人自食惡果不足惜仰剿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一南吮、第九天 我趴在偏房一處隱蔽的房頂上張望部凑。 院中可真熱鬧涂邀,春花似錦必孤、人聲如沸瑞躺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至践樱,卻和暖如春厂画,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拷邢。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工袱院, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞭稼。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓忽洛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親环肘。 傳聞我的和親對象是個(gè)殘疾皇子欲虚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容