說明
上一篇的例子中,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\Facadet
類。檢視think\Facadet
類,注意到其定義了一個魔術方法:
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
類的一個物件)。