laravel中静态代理Facades使用

Facades是一个可以从容器中访问对象的类

所有Facades类都是继承Illuminate\Support\Facades\Facade类

当Facades类调用任何静态方法时,laravel就会解析出其绑定的请求方法,绑定到容器中的实例

return Cache::get('name');    //使用Facades
return cache('name');        //使用辅助函数

这两种获取缓存的方法最终都是通过容器中实例的方法调用

下面通过源码分析两种方法的调用

1.facade调用原理

通过点击Cache::get()静态调用跳转到Cache类

这里要记住getFacadeAccessor()静态方法返回了cache,以及继承了Facade抽象类

点击Facade抽象类,可以看到其中有__callStatic类魔术方法

/**
     * Handle dynamic, static calls to the object.
     *
     * @param  string  $method
     * @param  array  $args
     * @return mixed
     *
     * @throws \RuntimeException
     */
    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);
    }

其解释:它的工作方式类似于 __call() 魔术方法,__callStatic() 是为了处理静态方法调用,PHP5.3.0以上版本有效,PHP 确实加强了对 __callStatic() 方法的定义;它必须是公共的,并且必须被声明为静态的。同样,__call() 魔术方法必须被定义为公共的,所有其他魔术方法都必须如此。

触发时机:只有一种情况,那就是在类外部使用类名调用不存在的静态方法

所以当调用Cache::get()时,首先会去Cache类中找get静态方法,找不到,去抽象类中找,也找不到,那么就触发了__callStatic()魔术方法。

首先执行了这句 $instance = static::getFacadeRoot();,也就是调用自身的getFacadeRoot()静态方法,方法如下:

/**
     * Get the root object behind the facade.
     *
     * @return mixed
     */
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

里面调用了resolveFacadeInstance方法,并传入了static::getFacadeAccessor()的返回结果

getFacadeAccessor()方法如下:

/**
     * Get the registered name of the component.
     *
     * @return string
     *
     * @throws \RuntimeException
     */
    protected static function getFacadeAccessor()
    {
        throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
    }

由于Cache类重写了该方法,在上面,所以这里返回的是'Cache'字符串

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

所以上面实际调用了 return static::resolveFacadeInstance('cache');

/**
     * Resolve the facade root instance from the container.
     *
     * @param  object|string  $name
     * @return mixed
     */
    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {    //这里$name = 'cache'是字符串所以不返回
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {    //这里实现的是一个单例,如果存在实例就直接返回
            return static::$resolvedInstance[$name];
        }

        if (static::$app) {    //第一次执行,不存在实例,就执行到这一步
            return static::$resolvedInstance[$name] = static::$app[$name];    //这里把app里的cache实例赋给resolvedInstance类变量里
        }
    }

这里发现$app是由外部注入传参的

/**
     * Set the application instance.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public static function setFacadeApplication($app)
    {
        static::$app = $app;
    }

点击方法,找到RegisterFacades.php文件

class RegisterFacades
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        Facade::clearResolvedInstances();

        Facade::setFacadeApplication($app);    //看到了$app是这里注入的

        AliasLoader::getInstance(array_merge(
            $app->make('config')->get('app.aliases', []),
            $app->make(PackageManifest::class)->aliases()
        ))->register();
    }
}

全局搜索RegisterFacades类发现在laravel\framework\src\Illuminate\Foundation\Http\Kernel.php中

/**
     * The bootstrap classes for the application.
     *
     * @var array
     */
    protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];

这里就要另外写一篇了,这里框架做了初始化的工作,通过facade类去加载cache实例,其实最终还是去找最里层的容器,只不过facade类实例做了缓存,保存在了自己的$resolvedInstance变量容器中

这里框架在Illuminate\Foundation\Http\Kernel类的sendRequestThroughRouter方法里绑定了上面这个数组的所有类实例,有兴趣可以看laravel8源码剖析index.php运行过程这篇文章

2.辅助函数cache()

/**
     * Get / set the specified cache value.
     *
     * If an array is passed, we'll assume you want to put to the cache.
     *
     * @param  dynamic  key|key,default|data,expiration|null
     * @return mixed|\Illuminate\Cache\CacheManager
     *
     * @throws \Exception
     */
    function cache()
    {
        $arguments = func_get_args();

        if (empty($arguments)) {
            return app('cache');
        }

        if (is_string($arguments[0])) {
            return app('cache')->get(...$arguments);
        }

        if (! is_array($arguments[0])) {
            throw new Exception(
                'When setting a value in the cache, you must pass an array of key / value pairs.'
            );
        }

        return app('cache')->put(key($arguments[0]), reset($arguments[0]), $arguments[1] ?? null);
    }

app()辅助函数返回实例对象

/**
     * Get the available container instance.
     *
     * @param  string|null  $abstract
     * @param  array  $parameters
     * @return mixed|\Illuminate\Contracts\Foundation\Application
     */
    function app($abstract = null, array $parameters = [])
    {
        if (is_null($abstract)) {
            return Container::getInstance();
        }

        return Container::getInstance()->make($abstract, $parameters);
    }

后面就都是初始化的工作

下一篇剖析一下laravel从index.php运行的过程,源码解析

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

end for time

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值