di php容器的创建
先来确定一下容器最基础的功能,首先需要容器能存储对象,提取对象。
容器中的对象可以放在一个数组中统一管理,可以把类名作为key,对象作为value,把对象存在数组中。
提取对象的时候只需要传入类名(key)就可以得到对象。
有些时候直接用类名输入得到对象的方式不方便,比如有些类的名称加上命名空间后很长,而且直接输入类名语义不够明显。
所以可以考虑用一个数组记录类名对应的别名。
所以一共要两个数组 $bind, $instance//$bind记录类的别名和类名的映射 类似于这种:
$bind = [‘Container’ => ‘\think\Container’, ‘Event’ =>’\think\Event’];
$instance记录类名和对象的映射 类似于这种:
$instances = [‘\think\Container’ => new \think\Container, ‘\think\Event’ => function(){return new \think\Event}];
容器类的主要功能就是对这两个数组的增删改查
现在来开始写代码,首先需要创建一个容器类。可以使用单例模式来保存容器对象
class Container { protected static $instance; protected $instances = []; protected $bind = []; public static function getInstance() { if (is_null(static::$instance)) { static::$instance = new static; } if (static::$instance instanceof Closure) { return (static::$instance)(); } return static::$instance; } public static function setInstance($instance) { static::$instance = $instance; } } 容器要有最基础的可供外部使用的几个方法 :
bind($abstract, $concrete);绑定类的标识。
$abstract理解为类的别名,$concrete理解为类名
instance(string $abstract, $instance)绑定类名->对象
get($abstract)通过类名获取对象
判断容器中是否存在类及标识
public function bound(string $abstract):bool { return isset($this->bind[$abstract]) || isset($this>instances[$abstract]); } 根据别名获取真是类名
public function getAlias(string $abstract):string { if(isset($this->bind[$abstract])) { $bind = $this->bind[$abstract]; if (is_string($bind)) { return $this->getAlias($bind); } } return $abstract; } 判断容器中是否存在对象的实例
public function exists(string $abstract) { $abstract = $this->getAlias($abstract); return isset($this->instances[$abstract]); } 绑定一个类、闭包、实例、接口实现到容器
如果$abstract输入的是数组那么把数组的key,value递归的绑定
如果$concrete传入闭包直接放入$bind数组中
如果$concrete是对象则放入$instances数组中
如果是字符串则放入$bind中
public function bind($abstract, $concrete = null) { if (is_array($abstract)) { foreach ($abstract as $key => $val) { $this->bind($key, $val); } } elseif ($concrete instanceof Closure) { $this->bind[$abstract] = $concrete; } elseif (is_object($concrete)) { $this->instances[$abstract] = $concrete; } else { $abstract = $this->getAlias($abstract); if ($abstract != $concrete) { $this->bind[$abstract] = $concrete; } } } 输入类名(或类的别名,映射到$instances数组中),$concreate为类的实例
public function instance(string $abstract, $concrete) { $abstract = $this->getAlias($abstract); $this->instances[$abstract] = $concrete; return $this; } 下面几个是需要通过php反射的api解析依赖的过程。比如创建A类对象的时候
A类对象在构造函数中需要依赖B类,这个时候就需要容器来解决依赖的问题
先用ReflectionClass系统提供的类活的类的信息,在用getConstructor方法获得构造函数信息。获得构造函数信息后用getNumberOfParameters方法获得构造函数依赖的参数个数,如果依赖参数的个数为0,则可以直接创建。
getParameters获得具体的参数信息。根据参数的类型去容器中寻找对应类型的参数(getType),如果没有的话,则检查函数是否有默认值(getDefaultValue)。如果都没有的话,则报错,对象无法创建
public function getObjectParam(string $className, array &$vars) { $array = $vars; $value = array_shift($array); if ($value instanceof $className) { array_shift($vars); return $value; } else { return $this->make($className); } } protected function bindParams(ReflectionFunctionAbstract $reflect, array $vars = []): array { if ($reflect->getNumberOfParameters() == 0) { return []; } reset($vars); $type = key($vars) === 0 ? 1 : 0; $params = $reflect->getParameters(); $args = []; foreach ($params as $param) { $name = $param->getName(); $reflectionType = $param->getType(); if ($reflectionType && $reflectionType->isBuiltin() === false) { $args[] = $this->getObjectParam($reflectionType->getName(), $vars); } elseif ($type == 1 && !empty($vars)) { $args[] = array_shift($vars); } elseif ($type == 0 && array_key_exists($name, $vars)) { $args[] = $vars[$name]; } elseif ($param->isDefaultValueAvailable()) { $args[] = $param->getDefaultValue(); } else { throw new InvalidArgumentException('method param miss:' . $name); } } return $args; } public function invokeFunction($function, array $vars = []) { try { $reflect = new ReflectionFunction($function); } catch (\ReflectionException $e) { throw new ReflectionException("function not exists: {$function}()", $function, $e); } $args = $this->bindParams($reflect, $vars); return $function(...$args); } public function invokeClass(string $class, array $vars = []) { try { $reflect = new ReflectionClass($class); } catch (ReflectionException $e) { throw new ReflectionException('class not exists: ' . $class, $class, $e); } $constructor = $reflect->getConstructor(); $args = $constructor ? $this->bindParams($constructor, $vars): []; $object = $reflect->newInstanceArgs($args); return $object; } public function make(string $abstract, array $vars = [], bool $newInstance = false) { $abstract = $this->getAlias($abstract); if (isset($this->instances[$abstract]) && !$newInstance) { return $this->instances[$abstract]; } if ($this->bind[$abstract] && $this->bind[$abstract] instanceof Closure) { $object = $this->invokeFunction($this->bind[$abstract], $vars); } else { $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { $this->instances[$abstract] = $object; } return $object; } public function get($abstract) { if ($this->bound($abstract)) { return $this->make($abstract); } throw new \Exception('class not exists: ' . $abstract, $abstract); } } 本作品采用《CC 协议》,转载必须注明作者和本文链接
关于 LearnKu