thinkphp5.1之Container容器和依赖注入
Container容器的命名空间:namespace think;
Container容器通过 set 方法绑定类的别名和完整的类名,数据会放到 $this->bind 数组里以便后面实例化类用;然后通过 get 的方法实例化类和类的自动依赖注入。
例子
container::set(‘demo2’,’\app\common\Demo’);//绑定类的命名空间名,为实例化做准备。
container::get(‘demo2’,[‘name’=>‘小可爱’]);//获取容器中的对象实例
container::get方法详解
在获取容器中的对象实例是,首先调用了make()方法。
$abstract为标识时先取出对应的类名或匿名函数。如果是匿名函数调用$this->invokeFunction实例化类,如果是类名把标识和类名保存到$this->name数组中,最后调用自己make()方法。
if ($concrete instanceof Closure) {
//匿名函数
$object = $this->invokeFunction($concrete, $vars);
} else {
//类名
$this->name[$abstract] = $concrete;
return $this->make($concrete, $vars, $newInstance);
}
$abstract为类名时调用$object = $this->invokeClass($abstract, $vars)来实例化类,如果不需要每次实例化会把实例保存到$this->instances数组中,已完整类名为键。
/**
* 调用反射执行类的实例化 支持依赖注入
* @access public
* @param string $class 类名
* @param array $vars 参数
* @return mixed
*/
public function invokeClass($class, $vars = [])
{
try {
//调用类反射机制
$reflect = new ReflectionClass($class);
//判断有没有__make方法
if ($reflect->hasMethod('__make')) {
//调用方法反射机制,参数(类名,方法名)
$method = new ReflectionMethod($class, '__make');
if ($method->isPublic() && $method->isStatic()) {
$args = $this->bindParams($method, $vars);
//使用数组给方法传送参数,并执行他。参数(调用方法的对象_如果是静态对象设置为null,使用数组传送的方法参数)
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);
}
}
/**
* 绑定参数
* @access protected
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
* @param array $vars 参数
* @return array
*/
protected function bindParams($reflect, $vars = [])
{
// 判断数组类型 数字数组时按顺序绑定参数
reset($vars);
$type = key($vars) === 0 ? 1 : 0;
$params = $reflect->getParameters();//获取参数
foreach ($params as $param) {
$name = $param->getName();//参数名
$lowerName = Loader::parseName($name);//整理参数名
$class = $param->getClass();//参数是否是类
if ($class) {
//参数是类时
$args[] = $this->getObjectParam($class->getName(), $vars);
} elseif (1 == $type && !empty($vars)) {
//传入参数是数字数组,删除第一个并绑定到$args[]
$args[] = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) {
//传入参数是关联数组,绑定到$args[]
$args[] = $vars[$name];
} elseif (0 == $type && isset($vars[$lowerName])) {
//传入参数是关联数组,绑定到$args[]
$args[] = $vars[$lowerName];
} elseif ($param->isDefaultValueAvailable()) {
//传入数组找不到使用默认值,否则抛出异常
$args[] = $param->getDefaultValue();
} else {
throw new InvalidArgumentException('method param miss:' . $name);
}
}
return $args;
}
判断构造函数中有依赖注入调用$this->getObjectParam函数完成依赖注入的实例化,但不可传入参数。
/**
* 获取对象类型的参数值,用于依赖注入
* @access protected
* @param string $className 类名
* @param array $vars 参数
* @return mixed
*/
protected function getObjectParam($className, &$vars)
{
$array = $vars;
$value = array_shift($array);
if ($value instanceof $className) {
$result = $value;
array_shift($vars);
} else {
//实例化依赖注入,不可传入参数
$result = $this->make($className);
}
return $result;
}