12. Facade 机制
Opened this issue · 0 comments
总结: Facade 就是一个标记便捷的调用容器盛放对象的方法的设计。
一般的,Facade
类都继承于 Illuminate\Support\Facades\Facade
。
比如 Illuminate\Support\Facades\Gate
这个门面:
<?php
namespace Illuminate\Support\Facades;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
class Gate extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return GateContract::class;
}
}
代码中我们只需要留意 getFacadeAccessor
方法是在 Illuminate\Support\Facades\Gate
中定义的就好了。
我们来分析 Illuminate\Support\Facades\Facade
:
魔术方法 part 1
首先我们来看末尾的 __callStatic
方法:
laravel/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
Lines 206 to 224 in ca57c28
还记得 Facade 类我们是怎么调用的吗?
静态调用!比如 Gate::allows()
。
因为在 Facade 类中定义了 __callStatic
魔术方法,所以只要静态调用了里面没有提供的方法,都会走到魔术方法里面。
这个魔术方法内部有一个取出实例的逻辑:
$instance = static::getFacadeRoot();
代码位于
laravel/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
Lines 123 to 131 in ca57c28
前面,我们留意了 static::getFacadeAccessor()
是位于 Illuminate\Support\Facades\Gate
中的,而 static::getFacadeAccessor()
运行后作为参数给了 static::resolveFacadeInstance()
,后者实现为
laravel/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
Lines 145 to 162 in ca57c28
而 static::$app
就是容器对象
这句
尝试从容器对象
数组取
操作,就是调用了 make
laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php
Lines 1208 to 1211 in ca57c28
就是从容器取出对象。
总结 getFacadeRoot
的逻辑大概为
- 判断
static::getFacadeAccessor()
返回是否对象,如果对象则直接使用。 - 在
Facade::$resolvedInstance
数组中尝试根据$name
取出对象,避免多次调用时,每次都取。 - 从
Illuminate\Foundation\Application
容器取出对象。
魔术方法 part 2
拿到 $instance
后,先验证 $instance
是否为空。如果是就报错 A facade root has not been set.
。
laravel/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
Lines 219 to 221 in ca57c28
所以如果遇到
A facade root has not been set.
这个错误,一般都是 a. 相应的服务提供者未注册; b. 对象绑定到容器的别名跟static::getFacadeAccessor()
返回不一致。
最后,直接把方法和参数穿透到背后的 $instance
去执行。
Laravel 有很多 facade 门面,背后穿透的不只一个类(譬如 DB
、 View
、Session
),这是怎么回事呢?
请看 15. Laravel 神奇的 Manager 类