Laravel Facade 实现揭秘

Facade(门面),为应用服务容器中的绑定类提供了一个“静态”接口,相比于传统静态方法,在维护时能够提供更加易于测试、更加灵活、简明优雅的语法。

Laravel 内置了很多facade,都定义在Illuminate\Support\Facades命名空间下,如App,Artisan,Auth,Cache,Config,Cookie,DB,Event,File,Log,Mail,Queue,Redis,Redirect,Request,Response,Route,Validator,URL等。

门面类只需要实现一个方法getFacadeAccessor。该方法定义了从容器中解析什么,然后 Facade 基类使用魔术方法 __callStatic() 从门面中调用解析对象。例如cache类。

/**
 * @see \Illuminate\Cache\CacheManager
 * @see \Illuminate\Cache\Repository
 */
class Cache extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'cache';
    }
}

上述代码说明会从容器中解析出cache对象,那么cache对象到底是什么,就要看ServiceProvider对cache注册的是什么,在config/app.php中看到注册的cache服务提供者。

'providers' => [
    //...
    Illuminate\Cache\CacheServiceProvider::class,
    //...
],

从CacheServiceProvider类中可以看出,它使用的是CacheManager来管理具体使用的是哪种类型的cache。

class CacheServiceProvider extends ServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = true;

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('cache', function ($app) {
            return new CacheManager($app);
        });
        //....
    }
}

再看CacheManager函数代码

public function getDefaultDriver()
{
    return $this->app['config']['cache.default'];
}

/**
 * Set the default cache driver name.
 *
 * @param  string  $name
 * @return void
*/
public function setDefaultDriver($name)
{
    $this->app['config']['cache.default'] = $name;
}

再看配置文件config/cache.php,最终使用的是还是在env中配置的驱动类型,默认是file,其他的还有redis,memcache,database等,最终facace得到的env配置的类。

'default' => env('CACHE_DRIVER', 'file'),

那么在解析出Cache类之后,Cache::get()方法是调用的呢。Cache本身是没有get方法的,那么就要从Cache的抽象基类Facade说起。
该类中实现了__callStatic方法,也就是说当调用不存在或无法调用的静态方法时,执行它。因此Cache::get()的get方法最终会走到这个方法里,通过最后一行的return实现。

public static function getFacadeRoot()
{
    return static::resolveFacadeInstance(static::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); /*...是一种语法糖,将数组和可遍历对象展开为函数参数*/
}

你可能感兴趣的:(PHP,lavavel5,源码分析,PHP技术分享)