Laravel 中 Facade 门面的实现


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的老王
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!