【laravel 实现杂谈】facade - (sunznx) 振翅飞翔
04 September 2019

Facade 是一个比较好听的名字,在 laravel 代码里面,还有另外一个名字叫 alias. alias 在 config/app.php 的 aliases 配置里面

'aliases' => [
    'App' => Illuminate\Support\Facades\App::class,
    'Arr' => Illuminate\Support\Arr::class,

    ...
    'Redis' => Illuminate\Support\Facades\Redis::class,
    ...
]

laravel 框架在运行的时候会读取这个配置,然后才会知道哪些 class 需要进行 facade 操作

实现 facade 的第一步只是简单的调用了 class_alias('Redis', Illuminate\Support\Facades\Redis::class) 。在 class_alias 之后,执行 Redis::get('key') 也就执行了 Illuminate\Support\Facades\Redis::get('key')

如果我们打开 Illuminate\Support\Facades\Redis::class 来看,会发现代码只有寥寥几行

class Redis extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'redis';
    }
}

执行 Redis::get('key') 发现 get 方法不存在就会调用 __callStatic 方法,__callStatic 在 Illuminate/Support/Facades/Facade.php 中定义

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);
}

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];
}

以 Facade/Redis 为例,上面的代码最后寻找 $app['redis']

再看看 Illuminate/Redis/RedisServiceProvider.php,是不是都通了

class RedisServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register()
    {
        $this->app->singleton('redis', function ($app) {
            $config = $app->make('config')->get('database.redis', []);

            return new RedisManager($app, Arr::pull($config, 'client', 'phpredis'), $config);
        });

        $this->app->bind('redis.connection', function ($app) {
            return $app['redis']->connection();
        });
    }

    public function provides()
    {
        return ['redis', 'redis.connection'];
    }
}

通过上面的分析,我们也可以看出,facade 一般是 单例singleton ,否则每一次调用都会 new 一个 object