[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`类的一个对象)。