首頁 >php框架 >Laravel >laravel為什麼要用門面

laravel為什麼要用門面

WBOY
WBOY原創
2022-06-06 16:02:102121瀏覽

在laravel中,因為門面能夠為應用的服務容器提供一個「靜態」接口,相比於傳統的靜態方法,門面提供的「靜態」接口相當於是服務容器底層類別中的一個靜態代表,能夠提供更靈活和易於測試的語法,所以在laravel中要用到門面。

laravel為什麼要用門面

本文操作環境:Windows10系統、Laravel6版、Dell G3電腦。

laravel為什麼用門面

 Laravel 中的門面總體上還是遵循著門面模式的基本思想的。 Laravel 中的門面是為應用的服務容器提供一個【靜態】接口,相當於是服務容器底層類別中的一個【靜態代表】,能夠提供更靈活、易於測試、優雅的語法。

對於 Laravel 中的門面來說,我們會經常使用到,比如說緩存。

Cache::get('key');

再例如我們之前常用的資料庫和 Redis 。

DB::connection('mysql2')->table('db_test')->get()->toArray();
 
Redis::connection('default')->client()->get('test')

發現沒有,門面全是用的靜態方法。但是你點過去,會發現這個門面類裡面什麼東西都沒有呀!

class Cache extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'cache';
    }
}

在 Facade 類別中,別的方法函數我們先不用看,直接拉到最底下,你會發現一個魔術方法,__callStatic() 。

public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();
 
    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }
 
    return $instance->$method(...$args);
}

__callStatic() 的意思是透過靜態呼叫時如果沒有定義對應的方法,就進入到__callStatic() 方法中,例如我們呼叫的Cache::get() 這個方法,實際上目前的Cache 門面類別以及它的父類別Facade 都沒有定義這個方法,那就直接進入了__callStatic() 中。接著,它就透過 getFacadeRoot() 取得我們目前門面的實例對象,然後呼叫實例物件中的 get() 方法。

好了,到此為止,其實如果面試的時候有面試官問你Laravel 中的門面模式是如何實現的時候,你就可以自信地說核心就是這個__callStatic() 魔術方法了。那麼這個具體的實例物件又是從哪裡來的呢?我們繼續往下看。

實例物件

接下來我們來看看 Facade 中的具體實例物件是怎麼取得的。這裡我們又要回到服務容器。不過還是先從門面入口來看看。

在 __callStatic() 方法中,我們會看到呼叫了一個 static::getFacadeRoot() 方法來獲得特定的實例物件。

public static function getFacadeRoot()
{
    return static::resolveFacadeInstance(static::getFacadeAccessor());
}

這個方法的內容很簡單,就是呼叫了另外兩個方法,注意 getFacadeAccessor() 是我們的各個門面子類別中實現的,例如例子中就是在 Cache 這個類別中實現的。它只是傳回一個實例的別名,還記得這個別名是在哪裡定義的嗎?我們在服務容器中看過,就是 vendor/laravel/framework/src/Illuminate/Foundation/Application.php 中 registerCoreContainerAliases() 方法裡面定義的那些。

接下來,我們主要看的就是 static::resolveFacadeInstance() 這個方法。從名字我們可以出,它的意思是 解決門面實例 ,這貨要是不回傳一個實例物件那還真對起它的名字了。

protected static function resolveFacadeInstance($name)
{
    if (is_object($name)) {
        return $name;
    }
 
    if (isset(static::$resolvedInstance[$name])) {
        return static::$resolvedInstance[$name];
    }
 
    if (static::$app) {
        return static::$resolvedInstance[$name] = static::$app[$name];
    }
}

第一個判斷,如果傳遞進來的是一個對象,直接返回。第二個判斷,如果目前實例數組中已經有了,就不再創建了,類似於一個 享元模式 的效果。注意,靜態的成員數組哦!什麼意思呢?靜態的全域共享的,也就是說,你這個實例物件創建之後,其他地方都可以使用,完全的單例狀態。最後一個判斷,app 也就是我們的服務容器存在的話,進行服務容器的操作。

我們先來看下這個 app 屬性是什麼時候賦值的。在講服務提供者時,Kernel 中有一個 bootstrappers 屬性數組,其中有一個 RegisterFacades 提供者。很明顯,它是用於註冊門面的一個服務提供者,在這個服務提供者中,我們會看到這樣的代碼。

public function bootstrap(Application $app)
{
    Facade::clearResolvedInstances();
 
    Facade::setFacadeApplication($app);
 
    AliasLoader::getInstance(array_merge(
        $app->make('config')->get('app.aliases', []),
        $app->make(PackageManifest::class)->aliases()
    ))->register();
}

其中的 Facade::setFacadeApplication() 就是將 服務容器 的 Application 物件注入到了門面類別的靜態成員變數 app 中。注意,同樣是靜態的,全域存在的。

然後我們繼續回到 resolveFacadeInstance() 方法。

protected static function resolveFacadeInstance($name)
{
    // …………
    // …………
    if (static::$app) {
        return static::$resolvedInstance[$name] = static::$app[$name];
    }
}

這裡怎麼回事,怎麼就透過 static::$app[$name] 就能得到一個實例物件了呢?別激動,別急,想想怎麼讓一個物件可以進行這樣的陣列操作?我們之前學過的哦!

就是這個 ArrayAccess 接口,它必須實現的那幾個方法可以讓物件像數組一樣去使用。

OK,知道原理了,我們來看看是不是這樣,找到 Application 的父類別 vendor/laravel/framework/src/Illuminate/Container/Container.php 。

class Container implements ArrayAccess, ContainerContract
{
    // …………
    // …………
    public function offsetGet($key)
    {
        return $this->make($key);
    }
    // …………
    // …………
}

真像大白了吧?不再需要我繼續多解釋了吧?關於 make() 方法在先前的服務容器中已經講解過了哦。

好了,剩下的內容交給你了,請根據vendor/laravel/framework/src/Illuminate/Foundation/Application.php 中registerCoreContainerAliases() 方法中的別名找到Cache 的具體實作類,然後分析它的get()、set()、forget() 等方法的實現,看看它們是怎麼根據我們的設定檔來使用不同的快取儲存方案的。

【相關推薦:laravel影片教學

以上是laravel為什麼要用門面的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn