🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] * * * * * ## 1 Container实现 ### 0 实现文件 ~~~ /lirary/think/Container.php ~~~ ### 1 核心方法 #### bind() 注册内容到容器 ~~~ public function bind($abstract, $concrete = null) { if (is_array($abstract)) { $this->bind = array_merge($this->bind, $abstract); } elseif ($concrete instanceof \Closure) { $this->bind[$abstract] = $concrete; } elseif (is_object($concrete)) { $this->instances[$abstract] = $concrete; } else { $this->bind[$abstract] = $concrete; } return $this; } ~~~ > 主要分为四种绑定 > * 批量绑定 > * 闭包绑定 > * 对象绑定 > * 类标识/接口绑定 > 其中对象绑定添加绑定内容到Container的instances。 > 其他三种绑定添加绑定内容到Container的bind中 * * * * * #### make() 获取绑定内容 ~~~ public function make($abstract, $vars = [], $newInstance = false) { if (true === $vars) { // 总是创建新的实例化对象 $newInstance = true; $vars = []; } if (isset($this->instances[$abstract]) && !$newInstance) { $object = $this->instances[$abstract]; } else { if (isset($this->bind[$abstract])) { $concrete = $this->bind[$abstract]; if ($concrete instanceof \Closure) { $object = $this->invokeFunction($concrete, $vars); } else { $object = $this->make($concrete, $vars, $newInstance); } } else { $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { $this->instances[$abstract] = $object; } } return $object; } ~~~ > * 首先判断$vars值是否为true。用来判断是强制创建新的对象实例 > * 然后 读取Container中的instances中是否已有实例内容 > * 如果Container的instance没有实例内容,则读取Container的bind中绑定的实现 > 1. 如果bind中的是闭包Closure则执行闭包,获取执行结果 > 2. 不是闭包(类,接口)则继续重新调用make,这时则会执行 $object = $this->invokeClass($abstract, $vars); > * 将执行的结果保存到Container的instances中作为缓存。 > * 然后执行结果的object。 > make()主要是用来读取instances中缓存的实例内容。 > 如果没有则从bind中绑定的内容进行创建。 > 然后缓存到instances中,缓存后下次则读取instances中缓存的内容 > 如果强制创建新的,则从bind重新创建实例,缓存到instances中 * * * * * ### 2 上层方法 #### get() 获取绑定内容 ~~~ public static function get($abstract, $vars = [], $newInstance = false) { return static::getInstance()->make($abstract, $vars, $newInstance); } ~~~ > 直接调用make()方法,与make方法过程一致。 * * * * * #### set() 注册内容到容器 ~~~ public static function set($abstract, $concrete = null) { return static::getInstance()->bind($abstract, $concrete); } ~~~ > 直接调用bind()方法,与bind方法过程一致。 * * * * * #### instance() 绑定实例 ~~~ public function instance($abstract, $instance) { if (isset($this->bind[$abstract])) { $abstract = $this->bind[$abstract]; } $this->instances[$abstract] = $instance; return $this; } ~~~ > 注册实例到Container的instances中 * * * * * #### bound() 检测是否绑定 ~~~ public function bound($abstract) { return isset($this->bind[$abstract]) || isset($this->instances[$abstract]); } ~~~ > 检测是否已绑定过程。 * * * * * ### 3 底层实现 #### getInstance() ~~~ public static function getInstance() { if (is_null(static::$instance)) { static::$instance = new static; } return static::$instance; } ~~~ > 获取全局唯一容器对象。 > 经典的单例模式。 * * * * * #### invokeFunction() 反射执行函数或闭包 ~~~~ public function invokeFunction($function, $vars = []) { $reflect = new \ReflectionFunction($function); $args = $this->bindParams($reflect, $vars); return $reflect->invokeArgs($args); } ~~~~ > ReflectionFunction反射执行函数或闭包。 * * * * * #### invokeMethod() 反射执行类方法或对象方法 ~~~ public function invokeMethod($method, $vars = []) { if (is_array($method)) { $class = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]); $reflect = new \ReflectionMethod($class, $method[1]); } else { // 静态方法 $reflect = new \ReflectionMethod($method); } $args = $this->bindParams($reflect, $vars); return $reflect->invokeArgs(isset($class) ? $class : null, $args); } ~~~ > ReflectionMethod 反射执行类方法或静态方法 * * * * * #### invoke() 反射执行(上面两个的封装) ~~~ public function invoke($callable, $vars = []) { if ($callable instanceof \Closure) { $result = $this->invokeFunction($callable, $vars); } else { $result = $this->invokeMethod($callable, $vars); } return $result; } ~~~ > 调用invokeFunction()或者invokeMethod()反射执行callable * * * * * #### invokeClass() 创建类实例 ~~~ public function invokeClass($class, $vars = []) { $reflect = new \ReflectionClass($class); $constructor = $reflect->getConstructor(); if ($constructor) { $args = $this->bindParams($constructor, $vars); } else { $args = []; } return $reflect->newInstanceArgs($args); } ~~~ > ReflectionClass 反射创建类的对象实例 * * * * * #### bindParams() 合并参数 ~~~ protected function bindParams($reflect, $vars = []) { $args = []; if ($reflect->getNumberOfParameters() > 0) { // 判断数组类型 数字数组时按顺序绑定参数 reset($vars); $type = key($vars) === 0 ? 1 : 0; $params = $reflect->getParameters(); foreach ($params as $param) { $name = $param->getName(); $class = $param->getClass(); if ($class) { $className = $class->getName(); $args[] = $this->make($className); } elseif (1 == $type && !empty($vars)) { $args[] = array_shift($vars); } elseif (0 == $type && isset($vars[$name])) { $args[] = $vars[$name]; } elseif ($param->isDefaultValueAvailable()) { $args[] = $param->getDefaultValue(); } else { throw new \InvalidArgumentException('method param miss:' . $name); } } } return $args; } ~~~ > 合并多个参数到一个数组中。 * * * * * ## 2 助手函数 #### app() 读取实例 ~~~ function app($name = 'think\App', $args = [], $newInstance = false) { return Container::get($name, $args, $newInstance); } ~~~ > 调用Container的get方法获取容器中的实例 #### bind() 注册内容到容器 ~~~ function bind($abstract, $concrete = null) { return Container::getInstance()->bind($abstract, $concrete); } ~~~ > 调用Conatiner的bind()到注册内容到容器