TP中的Facade是什麼呢,目前看是一個用來方便實現原有類(未定義靜態方法)的靜態方式調用對應的方法的類。
具體Facade的實現如下:
首先看核心文件:\thinkphp\base.php
`// 註冊核心類的靜態代理
Facade::bind([
facade\App::class => App::class,
facade\Build::class => Build::class,
facade\Cache::class => Cache::class,
facade\Config::class => Config::class,
facade\Cookie::class => Cookie::class,
facade\Debug::class => Debug::class,
facade\Env::class => Env::class,
facade\Hook::class => Hook::class,
facade\Lang::class => Lang::class,
facade\Log::class => Log::class,
facade\Request::class => Request::class,
facade\Response::class => Response::class,
facade\Route::class => Route::class,
facade\Session::class => Session::class,
facade\Url::class => Url::class,
facade\Validate::class => Validate::class,
facade\View::class => View::class,]);
可以看到類似容器中的綁定方式,找到對應的綁定方法如下:
` /**
* 綁定類的靜態代理
* @static
* @access public
* @param string|array $name 類標識
* @param string $class 類名
* @return object
*/
public static function bind($name, $class = null)
{
if (__CLASS__ != static::class) {
return self::__callStatic('bind', func_get_args());
}
if (is_array($name)) {
self::$bind = array_merge(self::$bind, $name);
} else {
self::$bind[$name] = $class;
}
}`
可以看到同樣是放到了一個數組$bind中。
這裏注意一下下面的在thinkphpbase.php文件中的註冊類別名的方法,可以實現類別名直接訪問類(這裏自己看)
`// 註冊類庫別名
Loader::addClassAlias([
'App' => facade\App::class,
'Build' => facade\Build::class,
'Cache' => facade\Cache::class,
'Config' => facade\Config::class,
'Cookie' => facade\Cookie::class,
'Db' => Db::class,
'Debug' => facade\Debug::class,
'Env' => facade\Env::class,
'Facade' => Facade::class,
'Hook' => facade\Hook::class,
'Lang' => facade\Lang::class,
'Log' => facade\Log::class,
'Request' => facade\Request::class,
'Response' => facade\Response::class,
'Route' => facade\Route::class,
'Session' => facade\Session::class,
'Url' => facade\Url::class,
'Validate' => facade\Validate::class,
'View' => facade\View::class,]);
那麼具體如何調用的靜態方法呢,比如
\Cache::set('name','value');
這個方法,具體時間如何調用到set方法呢,首先先用上面的類的別名機制直接找到對應的facadeCache::class這個類,也就是實際上是調用的這個類。然後我們看這個類的內容:
namespace think\facade;
use think\Facade;
class Cache extends Facade
{
}
可以看到沒有內容,那set方法哪來的呢,這裏利用了一個PHP的靜態方法調用失敗會調用的方法,在Facade類中有:
// 調用實際類的方法
public static function __callStatic($method, $params)
{
return call_user_func_array([static::createFacade(), $method], $params);
}
//在靜態上下文中調用一個不可訪問方法時,__callStatic() 會被調用。
也就是當調用不到set方法時,這個方法會被調用,也就是實際上調用了
call_user_func_array([static::createFacade(), $method], $params)
實際上就是:
/**
* 創建Facade實例
* @static
* @access protected
* @param string $class 類名或標識
* @param array $args 變量
* @param bool $newInstance 是否每次創建新的實例
* @return object
*/
protected static function createFacade($class = '', $args = [], $newInstance = false)
{
$class = $class ?: static::class;
$facadeClass = static::getFacadeClass();
if ($facadeClass) {
$class = $facadeClass;
} elseif (isset(self::$bind[$class])) {
$class = self::$bind[$class];
}
if (static::$alwaysNewInstance) {
$newInstance = true;
}
return Container::getInstance()->make($class, $args, $newInstance);
}
可以看到最後一句,實際上也就是是調用了Cache::class的實例(通過容器獲取)的set方法。