Skip to content

Facade门面模式

参考: Laravel中文社区8.5文档

环境

php
# php artisan --version
bootLaravel Framework 8.83.27

# php -v
PHP 7.4.27 (cli) (built: Dec 16 2021 22:42:18) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.27, Copyright (c), by Zend Technologies
    with Xdebug v2.9.2, Copyright (c) 2002-2020, by Derick Rethans

简介

Facades 为应用程序的 服务容器 中可用的类提供了 『静态代理』。 Laravel 的所有 Facades 都在 Illuminate\Support\facades命名空间中定义

什么是静态代理

参考官方手册:

php静态代理是一种在运行时动态绑定方法的技术。 “后期绑定”的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为“静态绑定”,因为它可以用于(但不限于)静态方法的调用。

定义路由

导入Cache Facade对象

php
Route::get('/', function () {
   return \Illuminate\Support\Facades\Cache::get('/');
});

查看源码

php中,调用类中不存在的方法,会调用类的__callStatic魔术方法。 查看\Illuminate\Support\Facades\Cache类,没有找到get方法,代码会自动调用__callStatic魔术方法,查看该魔术方法,

php
public static function __callStatic($method, $args)
{
    // 查看方法
    $instance = static::getFacadeRoot();
    // 如果没有设置实例,则抛出异常
    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }
    // 这里调用实例的$method方法传入对应的参数
    return $instance->$method(...$args);
}

public static function getFacadeRoot()
{
    // static::绑定的实际运行的类,即是Cache类,调用getFacadeAccessor,该方法返回的是别名字符串
    // cache中并没有resolveFacadeInstance方法,所以还是调用了Facade类的resolveFacadeInstance方法
    return static::resolveFacadeInstance(static::getFacadeAccessor());
}

验证一下,Cache类下新增resolveFacadeInstance方法

php
public static function resolveFacadeInstance($name)
{
    dd('resolveFacadeInstance');
}

访问首页,跟我们猜测一致

查看Facade类的resolveFacadeInstance方法

php
// $name值为Cache方法中返回的字符串
protected static function resolveFacadeInstance($name)
{
    // 如果$name是对象,直接返回该对象
    if (is_object($name)) {
        return $name;
    }
    // 如果$name在静态属性$resolvedInstance中存在,直接返回该对象
    if (isset(static::$resolvedInstance[$name])) {
        return static::$resolvedInstance[$name];
    }
    // 如果容器对象存在,从容器中解析出别名为$name的对象,并将该对象放入静态属性$resolvedInstance中,下次从属性中直接返回该对象,然后返回解析出的对象
    if (static::$app) {
        return static::$resolvedInstance[$name] = static::$app[$name];
    }
}

resolveFacadeInstance该方法中,如果容器对象中也不存在$name对应的类,则会抛出异常。

总结

Facades为应用程序的 服务容器 中可用的类提供了 『静态代理』。 Laravel 的所有 Facades 都在 Illuminate\Support\facades命名空间中定义。 Facades 使得我们可以更方便地使用框架提供的各种服务,而不需要在每个类中都进行手动绑定。 Facades 也使得我们可以更方便地在测试中使用框架提供的服务。