PHP实现依赖注入容器

 PSR-11: Container interface - PHP-FIG

PSR-11规范了容器接口,其中包括ContainerInterface、ContainerExceptionInterface、NotFoundExceptionInterface,如下所示。

接口

<?php
namespace Psr\Container;

/**
 * 容器的接口类,提供了获取容器中对象的方法。
 */
interface ContainerInterface
{
    /**
     * 在容器中查找并返回实体标识符对应的对象。
     *
     * @param string $id 查找的实体标识符字符串。
     *
     * @throws NotFoundExceptionInterface  容器中没有实体标识符对应对象时抛出的异常。
     * @throws ContainerExceptionInterface 查找对象过程中发生了其他错误时抛出的异常。
     *
     * @return mixed 查找到的对象。
     */
    public function get($id);

    /**
     * 如果容器内有标识符对应的内容时,返回 true 。
     * 否则,返回 false。
     *
     * 调用 `has($id)` 方法返回 true,并不意味调用  `get($id)` 不会抛出异常。
     * 而只意味着 `get($id)` 方法不会抛出 `NotFoundExceptionInterface` 实现类的异常。
     *
     * @param string $id 查找的实体标识符字符串。
     *
     * @return bool
     */
    public function has($id);
}
<?php
    
namespace Psr\Container;

/**
 * 容器中的基础异常类。
 */
interface ContainerExceptionInterface
{
}
<?php
namespace Psr\Container;

/**
 * 容器中没有查找到对应对象时的异常
 */
interface NotFoundExceptionInterface extends ContainerExceptionInterface
{
}

实现

要实现一个依赖注册容器,首先我们需要实现PSR-11的ContainerInterface接口(其实不实现也OK,但是为了业界统一规范,还是实现一下比较好,这样一来,大家实现的容器就是通用的了),主要的实现逻辑放在get方法中。

在实例化一个对象之前,我们首先关注的是该类的构造方法中有没有参数,参数对象的实例化需不需要依赖其它对象,例如:

<?php

class Person
{

}

class Student
{
    public function __construct(Person $person)
    {
    }
}

$person = new Person();
$student = new Student($person);

 以上,传统的情况下,如果我们要实例化一个Student对象,因为Student的构造函数中依赖于Person对象,所有在实例化Student对象之前需要实例化Person对象。那如果Person的构造函数中又依赖其它对象呢,那还得手动new一个依赖对象,这样处理感觉就没那么方便了。

所以我们可以使用依赖注入的方式来实现,将对象的实例化交给容器来处理,让容器自动注入依赖,这样一来就不用手动new依赖对象了。

实现原理:递归判断构造函数是否有依赖参数,依赖参数是否有依赖对象,有依赖则自动注入。

<?php
    
namespace Di;

use Psr\Container\ContainerInterface;

class Container implements ContainerInterface
{
    /**
     * 注册树
     *
     * @var array
     */
    protected array $register;

    /**
     * @throws ReflectionException
     */
    public function get(string $class)
    {
        // 构造函数参数
        $constructorParams = [];
        // 判断当前类是否已注册
        if (isset($this->register[$class])) {
            return $this->register[$class];
        }
        // 反射类
        $reflector = new ReflectionClass($class);
        // 是否需要处理构造函数
        if ($reflector->getConstructor()) {
            // 构造函数参数
            $params = $reflector->getConstructor()->getParameters();
            if (!empty($params)) {
                foreach ($params as $param) {
                    // 反射参数类
                    $paramReflector = new ReflectionClass($param->getClass()->name);
                    // 参数类名
                    $paramClass = $paramReflector->name;
                    // 递归实例化参数
                    $constructorParams[] = $this->get($paramClass);
                }
            }
        }
        if (!empty($constructorParams)) {
            return new $class(...$constructorParams);
        }
        $object = new $class();
        // 更新注册树
        $this->register[$class] = $object;
        return $object;
    }

    /**
     * @param string $class
     * @return bool
     */
    public function has(string $class): bool
    {
        return isset($this->register[$class]);
    }
}

注:该容器实现类并未考虑异常情况,可根据自己的业务情况抛异常 

使用

<?php

use Di\Container;
    
class Person
{
    public function eat()
    {
        var_dump('eat');
    }
}

$container = new Container();
/**
* @var Person
*/
$person = $container->get(Person:class);
$person->eat()

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值