本文参考博客(https://blog.csdn.net/dream_successor/article/details/79078905, https://www.cnblogs.com/i6010/articles/10559630.html),非常感谢原博主.为了加深理解,做了部分修改,增加了适当注释.
前一篇简单理解了PHP依赖注入,在实际开发过程中,我们并不知道依赖的类将来会扩展成什么样子,因此我们需要在实现依赖注入的时候,通过容器利用PHP反射类去实现. 这里的容器其实就是一个能通过反射类实现依赖注入,并且最终返回我们需要的实例的类.
大致实现思路:
以下是示例代码,
创建 test.php:
<?php
//设置三个有依赖关系的类: A依赖B,B依赖C
class C {
public function test(){
echo __CLASS__.'<br>';
}
}
class B {
private $c;
public function __construct(C $c){
$this->c = $c;
}
public function test(){
$this->c->test();
echo __CLASS__.'<br>';
}
}
class A {
private $b;
public function __construct(B $b){
$this->b = $b;
}
public function test(){
$this->b->test();
echo __CLASS__.'<br>';
}
}
创建容器类container.php
<?php
class Container {
private $repository = [];
public function __set($name,$value){
$this->repository[$name] = $value;
}
public function __get($name){
if(isset($this->repository[$name])){
return $this->build($this->repository[$name]);
}
throw new Exception($name.'is not exist in the repository');
}
/**
*创建类的实例,并注入依赖
*@param $name string 类名
*@return object 返回类的实例
*/
public function build($name){
//如果是匿名函数,直接返回执行结果
if($name instanceof Closure){
return $name($this);
}
//通过反射获取类的内部结构,实例化类
$reflector = new ReflectionClass($name);
//检查类是否可以实例化, 排除抽象类, 接口
if(!$reflector->isInstantiable()){
throw new Exception('类不能实例化');
}
//获取类的构造函数
$constructor = $reflector->getConstructor();
//若无构造函数,说明被依赖类中没有再依赖其他类,直接返回实例化对象
if(is_null($constructor)){
return new $name;
}
//获取构造函数参数,返回参数列表数组
$parameters = $constructor->getParameters();
//递归解析参数,获取依赖的参数或者依赖类的对象
$dependencies = $this->getDependencies($parameters);
//创建类的实例,将递归解析获取的参数传递到构造函数
return $reflector->newInstanceArgs($dependencies);
}
/**
*获取依赖的类
*@param $parameters array 构造函数的参数列表数组
*@return array 返回由依赖类的实例和构造函数普通参数的默认值组成的数组
*/
public function getDependencies($parameters){
$dependencies = [];
foreach($parameters as $parameter){
$dependency = $parameter->getClass();
if(is_null($dependency)){//参数不是一个类,获取默认值
$dependencies[] = $this->resolveNonClass($parameter);
}else{//是一个类, 递归解析
$dependencies[] = $this->build($parameter->name);
}
}
return $dependencies;
}
/**
*获取构造函数参数的默认值
*@param $parameter
*@return 返回构造函数参数默认的值
*/
public function resolveNonClass($parameter){
//如果有默认值则返回
if($parameter->isDefaultValueAvailable()){
return $parameter->getDefaultValue();
}
throw new Exception('');
}
}
在test.php中引入container类并使用依赖注入:
<?php
...
require_once './container.php';
$container = new Container;
$container->a = 'A';
$a = $container->a; //创建A类的实例, 并自动注入依赖.
$a->test(); // 输出 C B A