PHP实现自动依赖注入
自动依赖注入可以帮助我们更快速地导入一个类,不用手动进行实例化就可以使用这个类。本文将使用反射来获取参数列表和类型,实现自动依赖注入,并获得类实例。
下面有Customer、Request、Config三个类,Customer类构造方法需要Request类实例,Request类构造方法需要Config类实例,如下
Config
class Config
{
public function __construct()
{
echo 'Config.';
}
}
Request
class Request
{
public function __construct(Config $config)
{
}
}
Customer
class Customer
{
public function __construct(Request $request)
{
var_dump($request);
}
}
在通常情况下,我们的调用方式为
new Customer(new Request(new Config()));
这样调用需要我们手动去new一个Request类,然后给到Customer类的构造方法,如果Request类的构造方法也有一个或多个类实例作为参数,那我们就还需要手动实例化更多的类。参数自动注入可以帮助我们不用去考虑这些依赖,直接获取Customer实例,让程序自动去实例化所需要的依赖,原理就是使用反射获取参数列表,并判断参数类型,如果参数为类实例则自动实例化,并将实例当作参数来实例化需要的类。
获取Customer类构造方法的参数类型
$reflectionClass = new ReflectionClass(Customer::class);
$construct = $reflectionClass->getConstructor();
foreach ($construct->getParameters() as $parameter) {
// 这里假设只有类实例
$parameterType = $parameter->getType();
if ($parameterType) {
var_dump($parameterType->getName());
}
}
以上例程输出
string(7) "Request"
成功打印出类名,接下来就简单了。我们来实现一个Instance类,使用Instance类的静态方法get获取Customer实例,自动解决依赖问题。
class Instance
{
/**
* 获取一个类实例
* @param $className
* @return bool|object
* @throws ReflectionException
*/
public static function get($className)
{
$reflectionClass = new ReflectionClass($className);
// 如果$className不能被实例化则直接抛出异常,测试return false
if (!$reflectionClass->isInstantiable()) {
return false;
}
// 检查构造方法,如果没有构造方法直接返回实例
$construct = $reflectionClass->getConstructor();
if (is_null($construct)) {
return new $className;
}
// 检查构造方法的参数,如果没有参数则直接返回实例
$parameters = $construct->getParameters();
if (empty($parameters)) {
return new $className;
}
// 调用$className的参数
$callParameters = [];
foreach ($parameters as $parameter) {
$parameterType = $parameter->getType();
$name = $parameter->getName();
if (is_null($parameterType)) {
// 如果不是类实例则使用默认值
try {
$value = $parameter->getDefaultValue();
} catch (ReflectionException $reflectionException) {
// 没有默认值则使用null
$value = null;
}
$callParameters[$name] = $value;
} else {
// 参数为一个类实例则递归实例化类
$callParameters[$name] = self::get($parameterType->getName());
}
}
// 返回准备好的实例
return $reflectionClass->newInstanceArgs($callParameters);
}
}
使用Instance类的get方法来获取Customer类实例
Instance::get(Customer::class);
已经成功打印出Request类的实例$request并且已经执行了Config类的构造方法。
至此,我们已经能够自动解决实例化时的依赖问题。如果需要解决方法依赖的话,原理也是类似的。