[TOC]
# 说明
上一篇的例子中,`User`控制器引入了命名空间`think\facade\Event`,接着这样调用`think\facade\Event::subscribe()`,可打开`think\facade\Event`类的文件一看:
```
class Event extends Facade
{
/**
* 获取当前Facade对应类名(或者已经绑定的容器对象标识)
* @access protected
* @return string
*/
protected static function getFacadeClass()
{
return 'event';
}
}
```
该类中只定义了一个`getFacadeClass`方法,是不存在`subscribe`方法的。程序实际调用的是`think\Event`类的`subscribe`方法,而且该方法也不是静态方法,在这里却是静态调用。这是如何做到的呢?
# Facade的原理
Facade模式,中文一般翻译为「门面」模式,是指为一堆零散的风格不一的子系统设计一个统一的调用接口,这个接口就像是一个「门面」。Facade模式在这里的用处是把动态类方法转换为可静态调用的方法。接着,我们看看具体的实现过程。
`think\facade\Event`类继承了`think\Facade`类。查看`think\Facade`类,注意到其定义了一个魔术方法:
```
public static function __callStatic($method, $params)
{
return call_user_func_array([static::createFacade(), $method], $params);
}
```
在PHP中,当一个类静态调用一个不存在的方法,将会触发该魔术方法,所以**`think\facade\Event::subscribe()`**这样调用的时候,首先是执行该`__callStatic`方法。
在这个例子中,该方法`$method`参数传入的值是`subscribe`,`$params`传入的值是`app\subscribe\User`。
先看看这其中的`static::createFacade()`方法:
```
protected static function createFacade(string $class = '', array $args = [], bool $newInstance = false)
{
// 如果$class为空,那么$class就等于当前的类(think\facade\Event)
$class = $class ?: static::class;
// 解析出该Facade类实际要代理的类
$facadeClass = static::getFacadeClass();
if ($facadeClass) {
$class = $facadeClass;
}
// 是否要一直新建实例(否则就是单例模式)
if (static::$alwaysNewInstance) {
$newInstance = true;
}
// 使用PHP的反射类实例化该类(比如,实例化think\Event)
return Container::getInstance()->make($class, $args, $newInstance);
}
```
具体分析见注释。最后一个`$newInstance`参数可以设置是否使用单例模式。该方法最后返回一个类的对象(Facade类所代理的类),在本例子中是`think\Event`类的对象。
最后,程序执行`call_user_func_array([static::createFacade(), $method], $params)`,在这个例子中,就等于执行:`$obj->subscribe('app\subscribe\User')`(其中,`$obj`是`think\Event`类的一个对象)。