PHP ReflectionClass反射类 依赖注入 控制反转
依赖注入:A类需要调用B类的功能,所以需要一个B类的对象,一般的写法是在A类中直接实例化B类以获取对象,而依赖注入则是在A类外部实例化B类,然后将B类的对象通过参数注入到A类。
控制反转:将类与类之间的依赖关系管理由内部提到外部进行管理,以此降低程序组件间的耦合度。
ReflectionClass反射类:可用于判断一个类是否可以被实例化、是否有构造函数、获取构造函数的参数、判断参数是否为一个依赖类。
使用反射,我们可以实现从外部判断一个类需要依赖哪些其他类,然后使用程序自动将被依赖类实例化,注入要调用的类。
下面是一个浅显易懂的例子,Controller
类需要依赖Db
类,所以在构造函数中将Db
作为参数,我们在test.php
中编写一个maker()
函数用于自动创建类的对象,当被依赖的类需要依赖其它类时,maker()
函数会被递归调用。
Db.php
<?php
namespace support;
class Db
{
public function __construct()
{}
public function query()
{}
}
Controller.php
<?php
namespace application;
use support\Db;
class Controller
{
public $db;
public function __construct(Db $db, $data = FALSE)
{
$this->db = $db;
}
public function hello()
{
var_dump($this->db);
echo "Hello World \n";
}
}
test.php
<?php
namespace test;
use ReflectionClass;
function maker($className){
// 实例化反射类
$reflection = new ReflectionClass($className);
var_dump($reflection);
//判断类是否可以实例化
$isInstantiable = $reflection->isInstantiable();
var_dump($isInstantiable);
if($isInstantiable){ //此类可以实例化才能继续
//获取构造函数
$constructor = $reflection->getConstructor();
var_dump($constructor);
if($constructor){ //需要被注入的类写在构造函数的参数中,以此检测是否需要注入
//获取构造函数参数
$params = $constructor->getParameters();
var_dump($params);
if($params){ //需要被注入的类写在构造函数的参数中,以此检测是否需要注入
//遍历此函数的参数,判断参数为需要注入的类还是其他值
$dependencies = [];
foreach($params as $param){
//参数为类时getClass()会返回一个带有类名的对象,不为类时会返回NULL
$dependency = $param->getClass();
if(is_null($dependency)){
//不为类时直接保存到数组占位
$dependencies[] = $dependency;
}else{
//需要注入的参数为类时,递归调用本函数获取需要注入的类的对象
$dependencies[] = maker($dependency->name);
}
}
var_dump($dependencies);
//使用处理好的参数实例化本类,并返回对象
return $reflection->newInstanceArgs($dependencies);
}else{ //构造函数无参数表示无需注入,直接实例化返回对象
return new $className;
}
}else{ //无构造函数表示无需注入,直接实例化返回对象
return new $className;
}
}else{ //不可以实例化的返回NULL
return NULL;
}
}
include 'Db.php';
include 'Controller.php';
$controller = maker("application\Controller");
$controller -> hello();
结果如下: