ThinkPHP6 原始碼閱讀(十一):Facade

tsin發表於2019-09-01

說明

上一篇的例子中,User控制器引入了名稱空間think\facade\Event,接著這樣呼叫think\facade\Event::subscribe(),可開啟think\facade\Event類的檔案一看:

class Event extends Facade
{
    /**
     * 獲取當前Facade對應類名(或者已經繫結的容器物件標識)
     * @access protected
     * @return string
     */
    protected static function getFacadeClass()
    {
        return 'event';
    }
}

該類中只定義了一個getFacadeClass方法,是不存在subscribe方法的。程式實際呼叫的是think\Event類的subscribe方法,而且該方法也不是靜態方法,在這裡卻是靜態呼叫。這是如何做到的呢?

Facade的原理

Facade模式,中文一般翻譯為「門面」模式,是指為一堆零散的風格不一的子系統設計一個統一的呼叫介面,這個介面就像是一個「門面」。Facade模式在這裡的用處是把動態類方法轉換為可靜態呼叫的方法。接著,我們看看具體的實現過程。

think\facade\Event類繼承了think\Facadet類。檢視think\Facadet類,注意到其定義了一個魔術方法:

public static function __callStatic($method, $params)
{
    return call_user_func_array([static::createFacade(), $method], $params);
}

在PHP中,當一個類靜態呼叫一個不存在的方法,將會觸發該魔術方法,所以think\facade\Event::subscribe()這樣呼叫的時候,首先是執行該__callStatic方法。
在這個例子中,該方法$method引數傳入的值是subscribe$params傳入的值是app\subscribe\User
先看看這其中的static::createFacade()方法:

protected static function createFacade(string $class = '', array $args = [], bool $newInstance = false)
{
    // 如果$class為空,那麼$class就等於當前的類(think\facade\Event)
    $class = $class ?: static::class;
    // 解析出該Facade類實際要代理的類
    $facadeClass = static::getFacadeClass();

    if ($facadeClass) {
        $class = $facadeClass;
    }
    // 是否要一直新建例項(否則就是單例模式)
    if (static::$alwaysNewInstance) {
        $newInstance = true;
    }
    // 使用PHP的反射類例項化該類(比如,例項化think\Event)
    return Container::getInstance()->make($class, $args, $newInstance);
}

具體分析見註釋。最後一個$newInstance引數可以設定是否使用單例模式。該方法最後返回一個類的物件(Facade類所代理的類),在本例子中是think\Event類的物件。

最後,程式執行call_user_func_array([static::createFacade(), $method], $params),在這個例子中,就等於執行:$obj->subscribe('app\subscribe\User')(其中,$objthink\Event類的一個物件)。

Was mich nicht umbringt, macht mich stärker

相關文章