13 07 2020

门面为容器中的类提供了一个静态调用接口;

相对于传统的静态调用带来了更好的可测试性和扩展性;


例:

facade\Config::get('app_debug');


thinkphp/library/think/facade/Config.php

<?php
namespace think\facade;

use think\Facade;

/**
 * @see \think\Config
 * @mixin \think\Config
 * @method array load(string $file, string $name = '') static 加载配置文件
 * @method bool has(string $name) static 检测配置是否存在
 * @method array pull(string $name) static 获取一级配置参数
 * @method mixed get(string $name,mixed $default = null) static 获取配置参数
 * @method array set(mixed $name, mixed $value = null) static 设置配置参数
 * @method array reset(string $name ='') static 重置配置参数
 * @method void remove(string $name = '') static 移除配置
 * @method void setYaconf(mixed $yaconf) static 设置开启Yaconf 或者指定配置文件名
 */
class Config extends Facade
{
    /**
     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
     * @access protected
     * @return string
     */
    protected static function getFacadeClass()
    {
        return 'config';
    }
}


Config本身没有任何方法,它继承了Facade的方法,但Facade并没有get这个静态方法 此时,系统自动触发了魔术方法:__callStatic()


thinkphp/library/think/Facade.php

<?php

namespace think;

class Facade
{

    ......
    
    /**
     * 创建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);
    }

    ......
    
    // 调用实际类的方法
    public static function __callStatic($method, $params)
    {
        // 最后调用的是用户自定义函数:call_user_func_array([实例, 方法], 参数)
        // 调用 createFacade() 方法
        return call_user_func_array([static::createFacade(), $method], $params);
    }
}


在 createFacade 方法中,获取类的名称:$class = $class ?: static::class; 即得到 config 这个标识,在容器的make方法中,根据config标识,找到绑定的 think\Config 类,并调用其动态方法 get


最后调用的是:

(new think\Config())->get('app_debug');