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运行的过程,源码解析