上篇:Laravel源碼入門-啓動引導過程(八)HandleExceptions
上文介紹了 HandleExceptions,在 《Laravel源碼入門-啓動引導過程(五)$kernel->handle($request)》中第四個要載入的是 RegisterFacades,也就是 Foundation\Http\Kernel::bootstrapers[] 的第四個
\Illuminate\Foundation\Bootstrap\RegisterFacades::class, 如下:
// Illuminate\Foundation\Http\Kernel.php 片段
/**
* The bootstrap classes for the application.
* 引導類,起引導作用的類
*
* @var array
*/
protected $bootstrappers = [
// 載入服務器環境變量(.env 文件)
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
// 載入配置信息(config 目錄)
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
// 配置如何處理異常
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
// 註冊 Facades
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
// 註冊 Providers
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
// 啓動 Providers
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
我們再直接貼出 RegisterFacades 類的代碼,進行分析,非常直觀,如下
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Contracts\Foundation\Application;
class RegisterFacades
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
Facade::clearResolvedInstances();
Facade::setFacadeApplication($app);
AliasLoader::getInstance($app->make('config')->get('app.aliases', []))->register();
}
}
代碼雖然直觀,但是對於Laravel的初學者,有個問題是難以理解它的一些概念,Facade就是其一。少於程序語言能夠做這樣的概念的重構,有點做博士論文的味道,以至於我們所說的Laravel的學習曲線長,主要就是因爲學術味道過於濃重,無法上手就用。
來看看Facade,collins詞典的解釋是:The facade of a building, especially a large one, is its front wall or the wall that faces the street.用來說明建築,那麼建築的facade是什麼呢,是他的前牆面,所謂”前牆面“實際是臨街的一面,很清楚了,通俗說建築的外立面。
谷歌出來的Facade的圖片大多是這裏圖片呈現的樣子,可以看到,建築的外立面是需要重點裝飾的,以至於我們可以通過說某個建築外面立的樣子來明顯定位他。下面具體說。我們來看看大家舉例子時,對於facade的經典例程,如下:
Cache::get('akey');
先說這句代碼的意思,表面看是我們一般理解的,Cache類的一個靜態方法get(),返回的是通過get()獲取鍵值爲‘akey’的值。但是實際上,這裏的Cache不是緩存類,get()也不是Cache類的靜態方法,甚至不是Cache類的方法。那到底是什麼呢?
我們接着外立面的概念,虛擬一個類吧,Illuminate\Foundation\Building,建築物類,假如是 laravel 的 Illuminate的基礎類中的一個類。再虛擬一個類,Illumniate\Supports\Facades\Building,這個不叫建築物類了,我們把它叫做建築物外立面類。它位於 Illuminiate支持中的 Facades中。如果說我們要寫得更清晰的話,可以使用這樣寫:
Illuminate\Foundation\BuildingFoundation;
Illumniate\Supports\Facades\BuildingFacade;
當然因爲有 namespace 的使用,我們就不用謝後綴的同義反復了。粗淺理解,我們本身有BuildingFoundation,再給建築外面加個殼子,就成爲BuildingFacade,當都建成以後,我們再去稱呼他們時,往往會用 他的外殼,或者說外觀的樣子來做代詞指代,比如 鳥巢和水立方,實際上他們是國家體育場(http://www.n-s.cn/)和國家游泳中心(www.water-cube.com/cn)的Facade。順理成章了,當我們以 BuildFacade::get()使用時,實際上BuildFacade沒有這個方法,而是調用的 BuildFoundation的get()方法。
這樣做有什麼好處呢,一般認爲是便於可測試性與降低耦合性,說得直白一些,就像說鳥巢和水立方,比說國家體育場和國家游泳中心更方便,和更具有好的辨認性一樣。更進一步,在複雜的應用容器中,對象如星空浩繁,數量龐大,大項目更是如此,如何那麼容易就能提取出、表達出某個對象呢,靠傳參數、傳指針的方式,已經讓我們搞得暈頭轉向,到不如,給他做個殼子(Facade)讓他更好提取出來,所有耦合性也就降低了。實際上,真正的耦合性沒有降低,或者說,根本不是降低了真正的業務邏輯上的耦合性,而是laravel找到一個簡便有效的辦法可以訪問需要訪問的功能或對象而已。
總結一句,BuildingFacade 更容易地解析(make())出需要的對象 BuildingFoundation。
=======
瞭解了 Facade,讓我們再次回到具體 的 RegisterFacades 代碼清單:
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Contracts\Foundation\Application;
class RegisterFacades
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
// 第一步:清空所有已經解析的實例,源代碼就一句:
// static::$resolvedInstance = [];見 Illuminate\Support\Facades\Facade.php
Facade::clearResolvedInstances();
// 第二步:設置 $app,源代碼也是一句:
// static::$app = $app;
Facade::setFacadeApplication($app);
// 第三步:獲取應用配置信息->獲取Facade類別名->獲取別名下的實例->註冊實例
// 下面是源代碼,我們格式化一下,便於閱讀和查看:
// AliasLoader::getInstance($app->make('config')->get('app.aliases', []))->register();
// 3.1 獲取配置信息庫(Illuminate\Cache\Repository.php)
$config = $app->make('config');
dump($config);
// 3.2 獲取配置庫中的 app.aliases 所有Facade別名
$aliases = $config->get('app.aliases', []);
dump($aliases);
// 3.3 有Facade別名獲取別名實例(如果沒有就創建:new static())
$instance = AliasLoader::getInstance($aliases);
// 查看註冊前的 $instance。
dump($instance);
// 3.4 註冊實例(看源碼使用了spl_autoload_register([$this, 'load'], true, true);)
$instances->register();
// 查看註冊後的 $instance。
dump($instance);
}
}
dump($config)結果:
dump($aliases)結果:所有的Facades(建築的外立面)
註冊前後dump($instance)結果:
附錄:Facade的官方介紹:https://laravel.com/docs/5.4/facades
反思:說到Facade之於我們來說,就是穴位之於中醫。中醫不像西醫,不知道也不想知道身體內的具體影像、或很細微的結構的東西,他就通過經脈,通過把脈知道病情,通過按摩具體穴位實現治癒功能。除此,Facade之於我們,也更像武林高手,點穴即可!點!點!Facade::xxx()!
反思:例子拓展:Facade都位於 Illuminate\Support\Facades\
仿造:https://laravel.com/docs/5.4/facades#facade-class-reference
Facade | Class | Service Container Binding | 備註 |
---|---|---|---|
NationalStadium | Illuminate\Foundation\NationalStadium | birdsnest | http://www.n-s.cn/ |
NationalAquaticsCenter | Illuminate\Foundation\NationalAquaticsCenter | watercube | http://www.water-cube.com/cn/ |