Laravel 中 Facade 門面的實現

寫PHP的老王發表於2019-08-02

Route::get('/', function () {
    return view('welcome');
});

在laravel中的路由檔案routes/web.php有這麼一段程式碼,用於配置路由。這裡Route就是用Facade實現類方法get的靜態呼叫。

Laravel中的Facade解決類什麼問題?

在php中,很多情況都需要使用一個容器獲取到所有的物件,然後再呼叫改物件的方法,這樣在編寫程式碼的時候就會看到很長的一個呼叫鏈。例如:
在Yii2中,幾乎所有的系統類都是在app容器當中,對這些系統類進行操作都需要執行Yii::$app->route獲取到類例項,然後在執行方法Yii::$app->route->get()。但是如果用Facade實現之後的呼叫就是Route::get()。這樣的寫法是的程式碼更加簡潔。

Laravel中Facade是怎麼實現的?

思路是透過__callStatic魔術方法將方法呼叫代理到實際的物件方法中去。


abstract class Facade
{
    ...
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }
    ...
    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        return static::$resolvedInstance[$name] = static::$app[$name];
    }
    ...
    public static function getFacadeAccessor(){
        //子類返回類名
    }
    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);
    }
}

class Route extends Facade{
    public static function getFacadeAccessor(){
        return 'router';
    }
}

根據每個Facade中提供的getFacadeAccessor返回實際的物件類名,獲取類物件。每個類物件一旦建立,就放在一個靜態陣列中,因此在一次請求中最多隻會被建立一次。

有沒有其他的實現方式?

從上面的程式碼可以看到,其實核心就是一個靜態代理的功能。那麼有沒有其他的實現方式了呢?

class Facade{
    public static $instance;
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }
    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$instance)) {
            return static::$instance;
        }

        return static::$instance = static::$app[$name];
    }
    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);
    } 
}

class Route extends Facade{

}

可以看出,上面的方式也能夠實現靜態代理,類似於Facade的功能。都是透過魔術方法實現。作用上,都簡化了程式碼編寫。

兩種不同實現方式的區別

第二種實現方式有一個很大的缺點,那就是必須繼承Facade類。PHP本身只能繼承一個類,所以第二種實現方式對於一些需要繼承其他類的物件是不適合的。

Laravel的實現方式,對類本身沒有束縛,任何類物件都能夠透過建立一個Facade物件實現靜態代理。有很大的靈活性。

轉載自 公眾號【寫PHP的老王】

本作品採用《CC 協議》,轉載必須註明作者和本文連結
寫PHP的老王

相關文章