• 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 sjmsdi;
    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 sjmsdi;
    class Car{
        /*
         * 依赖注入
         */
        public function pay($name){
            echo $name . ' buy car';
        }
    }
    
    <?php
    namespace sjmsdi;
    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('sjmsdiPerson',['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对应的是 hinApp::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 thinkfacade;
    
    use thinkFacade;
    
    /**
     * @see 	hinkApp
     * @mixin 	hinkApp
     * @method 	hinkApp bind(string $bind) static 绑定模块或者控制器
     * @method void initialize() static 初始化应用
     * @method void init(string $module='') static 初始化模块
     * @method 	hinkResponse run() static 执行应用
     * @method 	hinkApp dispatch(	hink
    outeDispatch $dispatch) static 设置当前请求的调度信息
     * @method void log(mixed $log, string $type = 'info') static 记录调试信息
     * @method mixed config(string $name='') static 获取配置参数
     * @method 	hink
    outeDispatch routeCheck() static URL路由检测(根据PATH_INFO)
     * @method 	hinkApp routeMust(bool $must = false) static 设置应用的路由检测机制
     * @method 	hinkModel 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 	hinkValidate validate(string $name = '', string $layer = 'validate', bool $appendSuffix = false, string $common = 'common') static 实例化验证器类
     * @method 	hinkdbQuery 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 	hinkContainer 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);
        }
    
  • 相关阅读:
    ural 1146. Maximum Sum(动态规划)
    ural 1119. Metro(动态规划)
    ural 1013. K-based Numbers. Version 3(动态规划)
    Floyd算法
    杭电21题 Palindrome
    杭电20题 Human Gene Functions
    杭电15题 The Cow Lexicon
    杭电三部曲一、基本算法;19题 Cow Bowling
    杭电1002 Etaoin Shrdlu
    Qt 学习之路 2(37):文本文件读写
  • 原文地址:https://www.cnblogs.com/cl94/p/12688081.html
Copyright © 2020-2023  润新知