[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`類的一個對象)。