thinkphp5源码剖析系列3-容器与门面

容器和门面是tp框架的精髓所在

涉及容器与门面之前我们先链接几个简单的设计模式

  • 单例模式
<?php
namespace sjms;
// 单例模式
class Single{
    private static $me = null;
    // 防止对象被new
    private function __construct(){
        
    }
    public static function  getInstance(){
        self::$me || self::$me = new self;
        return self::$me;
    }
}
  • 注册树模式
<?php
namespace sjms;
// 注册树模式
class Register{
    // 注册树池子
    private static $objects = [];
    // 注册对象
    public static function set($key, $object){
        self::$objects[$key] = $object;
    }
    // 从注册树获取对象,如果没有注册对象
    public static function get($key){
        if(!isset(self::$objects[$key])){
            self::$objects[$key] = new $key;
        }
        return self::$objects[$key];
    }
    // 注销注册树对象
    public static function _unset($key){
        if(isset(self::$objects[$key]))
            unset(self::$objects[$key]);
    }
}
  • 依赖注入
<?php
namespace sjms\di;
class Person{
    /*
     * 依赖注入
     * 依赖:Person类Car类
     * 注入:将Car类Person类
     */
    public function buy($obj){
        // $bmw = new Car();
        $price = $obj->pay();
        return $price;
    }
}
  • 反射机制
    public function index()
    {
        // 实例化对象
        $obj1 = new Car();
        // 对象的反射类
        $obj2 = new \ReflectionClass($obj1);
        // 相当于实例化这个类
        $instance = $obj2->newInstance();
        // 获取这个类的所有方法[返回对象数组集
        $methods = $obj2->getMethods();
        // 执行某个方法
        echo $instance->pay();
        // 获取单个方法[返回对象
        $method = $obj2->getMethod('pay');
        // 判断某个方法是否是公用的[返回对象
        $method = new \ReflectionMethod($instance, 'pay');
        $isPublic = $method->isPublic();
        var_export("is public : {$isPublic}");
        // 获取方法的所有形参[返回对象数组集
        var_dump($method->getParameters());
    }
  • 构造自己的容器类
<?php
namespace sjms\di;
class Car{
    /*
     * 依赖注入
     */
    public function pay($name){
        echo $name . ' buy car';
    }
}
<?php
namespace sjms\di;
class Person{
    public function __construct(Car $c,$name)
    {
        $this->name = $name;
        $this->obj = $c;
    }
    /*
     * 依赖注入
     * 依赖:Person类Car类
     * 注入:将Car类Person类
     */
    public function buy(){
        $this->obj->pay($this->name);
    }
}
<?php
namespace sjms;
// 自己的容器类
class Continer{
    private $instances = [];
    private static $instance = NULL;
    public static function getInstance(){
        if(is_null(static::$instance)){
            static::$instance = new static;
        }
        return static::$instance;
    }

    public function set($key, $value){
        $this->instances[$key] = $value;
    } 

    // 获取容器里面的实例,会用到反射机制
    public function get($key,$data = []){
        if(!empty($this->instances[$key])){
            $key = $this->instances[$key];
        }
        // 反射类
        $reflect = new \ReflectionClass($key);
        // 获取类的构造函数
        $c = $reflect->getConstructor();
        if(!$c){
            return new $key;
        }
        // 获取类的构造函数所有的参数
        $params = $c->getParameters();
        if(empty($params)){
            return new $key;
        }
        // 如果构造函数有参
        foreach($params as $param){
            $class = $param->getClass();
            if(!$class){
                // 如果不是类参数
                $paramName = $param->name;
                // 自动传参
                if(isset($data[$paramName])){
                    $args[] = $data[$paramName];
                }
                // todo
            }else{
                // 递归[依赖注入的思维]
                $args[] = $this->get($class->name);
            }
        }
        return $reflect->newInstanceArgs($args);
    }
}
 public function index()
    {
        $c = Continer::getInstance();
        $c->get('\sjms\di\Person',['name' => 'tom'])->buy();

    }
  • tp5中的容器类,Container.php
    /**
     * 获取容器中的对象实例
     * @access public
     * @param  string        $abstract       类名或者标识
     * @param  array|true    $vars           变量
     * @param  bool          $newInstance    是否每次创建新的实例
     * @return object
     */
    public static function get($abstract, $vars = [], $newInstance = false)
    {
        return static::getInstance()->make($abstract, $vars, $newInstance);
    }

    /**
     * 创建类的实例
     * @access public
     * @param  string        $abstract       类名或者标识
     * @param  array|true    $vars           变量
     * @param  bool          $newInstance    是否每次创建新的实例
     * @return object
     */
    public function make($abstract, $vars = [], $newInstance = false)
    {
        if (true === $vars) {
            // 总是创建新的实例化对象
            $newInstance = true;
            $vars        = [];
        }

        $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
    
        // 如果注册树上面有,则从注册树上面取
        if (isset($this->instances[$abstract]) && !$newInstance) {
            return $this->instances[$abstract];
        }

        // 如果有绑定标识
        if (isset($this->bind[$abstract])) {
            $concrete = $this->bind[$abstract];

            if ($concrete instanceof Closure) {
                $object = $this->invokeFunction($concrete, $vars);
            } else {
                // 绑定别名
                $this->name[$abstract] = $concrete;
                return $this->make($concrete, $vars, $newInstance);
            }
        } else {
            $object = $this->invokeClass($abstract, $vars);
        }

        // 挂载注册树
        if (!$newInstance) {
            $this->instances[$abstract] = $object;
        }

        return $object;
    }

    /**
     * 调用反射执行类的实例化 支持依赖注入
     * @access public
     * @param  string    $class 类名
     * @param  array     $vars  参数
     * @return mixed
     */
    public function invokeClass($class, $vars = [])
    {
        try {
            $reflect = new ReflectionClass($class);

            // 是否有构造类的__make方法,如果有则直接make
            if ($reflect->hasMethod('__make')) {
                $method = new ReflectionMethod($class, '__make');

                if ($method->isPublic() && $method->isStatic()) {
                    $args = $this->bindParams($method, $vars);
                    return $method->invokeArgs(null, $args);
                }
            }

            // 获取构造函数
            $constructor = $reflect->getConstructor();

            // 处理依赖注入
            $args = $constructor ? $this->bindParams($constructor, $vars) : [];

            return $reflect->newInstanceArgs($args);

        } catch (ReflectionException $e) {
            throw new ClassNotFoundException('class not exists: ' . $class, $class);
        }
    }
  • 门面模式

门面为容器中的类提供了一个静态调用接口
相对于传统的静态方法调用,带来了更好的可测试性和扩展性
门面模式其实就是通过静态的方式调用类的方法
门面模式的实现:
1:门面类继承门面基类,,覆盖父类的getFacadeClass()方法,此方法是映射装饰类的类名(由于容器的绑定标识机制,app对应的是 \thin\App::class

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------

namespace think\facade;

use think\Facade;

/**
 * @see \think\App
 * @mixin \think\App
 * @method \think\App bind(string $bind) static 绑定模块或者控制器
 * @method void initialize() static 初始化应用
 * @method void init(string $module='') static 初始化模块
 * @method \think\Response run() static 执行应用
 * @method \think\App dispatch(\think\route\Dispatch $dispatch) static 设置当前请求的调度信息
 * @method void log(mixed $log, string $type = 'info') static 记录调试信息
 * @method mixed config(string $name='') static 获取配置参数
 * @method \think\route\Dispatch routeCheck() static URL路由检测(根据PATH_INFO)
 * @method \think\App routeMust(bool $must = false) static 设置应用的路由检测机制
 * @method \think\Model model(string $name = '', string $layer = 'model', bool $appendSuffix = false, string $common = 'common') static 实例化模型
 * @method object controller(string $name, string $layer = 'controller', bool $appendSuffix = false, string $empty = '') static 实例化控制器
 * @method \think\Validate validate(string $name = '', string $layer = 'validate', bool $appendSuffix = false, string $common = 'common') static 实例化验证器类
 * @method \think\db\Query db(mixed $config = [], mixed $name = false) static 数据库初始化
 * @method mixed action(string $url, $vars = [], $layer = 'controller', $appendSuffix = false) static 调用模块的操作方法
 * @method string parseClass(string $module, string $layer, string $name, bool $appendSuffix = false) static 解析应用类的类名
 * @method string version() static 获取框架版本
 * @method bool isDebug() static 是否为调试模式
 * @method string getModulePath() static 获取当前模块路径
 * @method void setModulePath(string $path) static 设置当前模块路径
 * @method string getRootPath() static 获取应用根目录
 * @method string getAppPath() static 获取应用类库目录
 * @method string getRuntimePath() static 获取应用运行时目录
 * @method string getThinkPath() static 获取核心框架目录
 * @method string getRoutePath() static 获取路由目录
 * @method string getConfigPath() static 获取应用配置目录
 * @method string getConfigExt() static 获取配置后缀
 * @method string setNamespace(string $namespace) static 设置应用类库命名空间
 * @method string getNamespace() static 获取应用类库命名空间
 * @method string getSuffix() static 是否启用类库后缀
 * @method float getBeginTime() static 获取应用开启时间
 * @method integer getBeginMem() static 获取应用初始内存占用
 * @method \think\Container container() static 获取容器实例
 */
class App extends Facade
{
    /**
     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
     * @access protected
     * @return string
     */
    protected static function getFacadeClass()
    {
        return 'app';
    }
}

2:门面基类,门面基类实现 __callStatic魔术方法,将静态调用转发到 装饰类的对象调用


<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------

namespace think;

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)
    {
        var_dump($method);
        return call_user_func_array([static::createFacade(), $method], $params);
    }
}

  • 门面模式调用
    public function index()
    {
        // 门面模式的静态调用 相当于 就是容器类调用对象方法
        $boo = \Config::get('app.') == Container::get('config')->get('app.');
        var_dump($boo);
    }
posted @ 2020-04-12 22:13  cl94  阅读(356)  评论(0编辑  收藏  举报