作用
門面爲容器中的類提供了一個靜態調用接口,相比傳統了靜態方式調用,帶來了更好的可測試性和擴展性。
代碼使用 (這樣寫可以直接調用)兩段代碼執行的結果一樣,實際執行的是在 \think\Facade\Config
$apps = \Config::get('app.');
halt($apps);
$apps = \think\Facade\Config::get('app.');
halt($apps);
爲什麼可以使用 \Config 就可以調用呢?在 base.php中註冊了類的別名,所以就可以使用 Config 來調用
// 註冊類庫別名
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,
]);
看來分析代碼
1、可以看到在 thinkphp/library/think/facade/Config.php 中
class Config extends Facade
{
/**
* 獲取當前Facade對應類名(或者已經綁定的容器對象標識)
* @access protected
* @return string
*/
protected static function getFacadeClass()
{
return 'config';
}
}
2、看繼承的 facade 類 =》 thinkphp/library/think/facade.php文件
class Facade
{
/**
* 綁定對象
* @var array
*/
protected static $bind = [];
/**
* 始終創建新的對象實例
* @var bool
*/
protected static $alwaysNewInstance;
/**
* 綁定類的靜態代理
* @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;
}
}
/**
* 創建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);
}
/**
* 獲取當前Facade對應類名(或者已經綁定的容器對象標識)
* @access protected
* @return string
*/
protected static function getFacadeClass()
{}
/**
* 帶參數實例化當前Facade類
* @access public
* @return mixed
*/
public static function instance(...$args)
{
if (__CLASS__ != static::class) {
return self::createFacade('', $args);
}
}
/**
* 調用類的實例
* @access public
* @param string $class 類名或者標識
* @param array|true $args 變量
* @param bool $newInstance 是否每次創建新的實例
* @return mixed
*/
public static function make($class, $args = [], $newInstance = false)
{
if (__CLASS__ != static::class) {
return self::__callStatic('make', func_get_args());
}
if (true === $args) {
// 總是創建新的實例化對象
$newInstance = true;
$args = [];
}
return self::createFacade($class, $args, $newInstance);
}
// 調用實際類的方法
public static function __callStatic($method, $params)
{
return call_user_func_array([static::createFacade(), $method], $params);
}
}
可以看到是沒有get 靜態方法的,那麼他是如何調用的。
1、執行第一步,使用魔術方法 __callStatic() 。
2、執行 static::createFacade() 函數,使用 Container 獲取當前調用類的對象。
3、執行,call_user_func_array 是 php 內置函數,並使用對象調用 對應的 方法執行代碼,並返回結果。
createFacade 方法解析
/**
* 創建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(); // config
if ($facadeClass) {
$class = $facadeClass; // 存在的話 $class = config
} elseif (isset(self::$bind[$class])) { //看看 self::$bind 是否存在 config
$class = self::$bind[$class];
}
if (static::$alwaysNewInstance) {
$newInstance = true;
}
// 使用容器獲取對象實列
return Container::getInstance()->make($class, $args, $newInstance);
}