Hello,我是Rocket
这是我参与更文挑战的第4天,活动详情查看:更文挑战
引言
- 知识是自己的,不要畏惧去看源码,因为它比你想象的更简单
- 今天带着大家来揭开Laravel Facade神秘的面纱,从他启动注册,到解析加载一点点
- 学完估计你会想我曹原来这么简单,其实是你想复杂了
- 动态facede感觉有点多余,有那功夫写个命令生成Facade
1、Facade 是什么
Laravel
的Facade
是典型的静态代理模式,他可以让你以静态的方式来调用存放在容器中任何对象的任何方法
2、Facade 启动和注册
Facade 的启动引导是在 Illuminate\Foundation\Bootstrap\RegisterFacades 中注册的
public function bootstrap(Application $app)
{
Facade::clearResolvedInstances();
Facade::setFacadeApplication($app);
AliasLoader::getInstance(array_merge(
$app->make('config')->get('app.aliases', []),
$app->make(PackageManifest::class)->aliases()
))->register();
}
复制代码
默认的别名配置是从 app 配置文件下的 aliases 读取的,PackageManifest 是 laravel 5.5 新增的 包自动发现 规则,这里我们暂时不考虑 PackageManifest 包提供的别名。
public function register()
{
if (! $this->registered) {
$this->prependToLoaderStack();
$this->registered = true;
}
}
protected function prependToLoaderStack()
{
spl_autoload_register([$this, 'load'], true, true);
}
复制代码
通过Illuminate\Foundation\AliasLoader
把所有的facade
注册进自动加载,基于php的spl_autoload_register
注册完成后,后续所有 use 的类都将通过 load 函数来完成类的自动加载。并且优先走load方法
3、如何定义Facade
需要继承Illuminate\Support\Facades\Facade
类,并实现getFacadeAccessor
静态方法
use Illuminate\Support\Facades\Facade;
class Cache extends Facade
{
/**
* 获取组件注册名称
*
* @return string
*/
protected static function getFacadeAccessor() {
return 'cache';
}
}
复制代码
4、Facade 是如何实现的
我们平时调用facade总会发现这个类其实是没有对应的方法的,那么facade是怎么找到具体的实现类
<?php
use Illuminate\Support\Facades\Config;
class Test
{
public function index()
{
Config::get('app.name');
}
}
复制代码
所有的Facade
都是基于Illuminate\Support\Facades\Facade
类,在该基类中定义了一个 __callStatic 方法,已至于我们能够轻松地使用 Facade(不用实列化)。
abstract class Facade
{
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
//static::getFacadeAccessor() 被子类重写
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
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];
}
//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);
}
}
复制代码
static::$app[$name]
大家可能好奇怎么能把对象当数组取值呢?实际上app继承了Illuminate\Container\Container
IOC容器,同时容器还实现了PHP的ArrayAccess
,static::$app[$name]
这种方式等同于app->make($name)
5、动态Facade实现的原理
public function load($alias)
{
//检测到命名空间包含 Facades\\
if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
//这块是实现动态Facade的关键
$this->loadFacade($alias);
return true;
}
if (isset($this->aliases[$alias])) {
//注册类别名
return class_alias($this->aliases[$alias], $alias);
}
}
protected function loadFacade($alias)
{
require $this->ensureFacadeExists($alias);
}
protected function ensureFacadeExists($alias)
{
if (file_exists($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) {
return $path;
}
//实际上的处理会写一个缓存文件
file_put_contents($path, $this->formatFacadeStub(
$alias, file_get_contents(__DIR__.'/stubs/facade.stub')
));
return $path;
}
复制代码
6、结尾
与诸君共勉之,希望对您有所帮助
复制代码