千家信息网

怎么创建PHP DI容器

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要讲解了"怎么创建PHP DI容器",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"怎么创建PHP DI容器"吧!由开车开始先开个车,为大家举个
千家信息网最后更新 2026年11月03日怎么创建PHP DI容器

这篇文章主要讲解了"怎么创建PHP DI容器",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"怎么创建PHP DI容器"吧!

由开车开始

先开个车,为大家举个栗子:

class Driver{    public function drive()    {        $car = new Car();        echo '老司机正在驾驶', $car->getCar(), PHP_EOL;    }}class Car{    protected $name = '普通汽车';    public function getCar()    {        return $this->name;    }}

有两个类,Driver和Car,老司机Driver有个方法driver,在调用的时候首先得整辆车$car,然后发车。大多数同学都写过这样或者类似的代码,这样的代码单看没啥毛病,挺正常的。但是,如果我要换辆车,开普通车撩不到妹。

class Benz extends Car{    protected $name = '奔驰';}

这时候就需要做一个比较恶心的操作了,得改老司机的代码了。(老司机:我做错了什么?换辆车还得让我重学驾照……)。因此我们需要把让Car为外界注入,将Driver和Car解耦,不是老司机自己开车的时候还得自己去造车。于是就有了下面的结果

class Driver{    protected $car;    public function __construct(Car $car)    {        $this->car = $car;    }    public function drive()    {        echo '老司机正在驾驶', $this->car->getCar(), PHP_EOL;    }}

此时Driver和Car两个类已经解耦,这两个类的依赖,依靠上层代码去管理。此时,老司机会这样"开车":

$car = new Car();$driver = new Driver($car);$driver->drive();

此时,我们创建Driver依赖的实例,并注入。上面的例子,我们实现了依赖注入,不过是手动的,写起来感觉还是不爽。这么繁重的活怎么能手动来做呢,得让程序自己去做。于是乎,DI容器诞生。

依赖注入容器

依赖注入与IoC模式类似工厂模式,是一种解决调用者和被调用者依赖耦合关系的模式。它解决了对象之间的依赖关系,使得对象只依赖IoC/DI容器,不再直接相互依赖,实现松耦合,然后在对象创建时,由IoC/DI容器将其依赖(Dependency)的对象注入(Inject)其内,这样做可以最大程度实现松耦合。依赖注入说白一点,就是容器将某个类依赖的其他类的实例注入到这个类的实例中。

这段话可能说的有点抽象,回到刚才的例子吧。刚刚我手动完成了依赖注入,比较麻烦,如果一个大型的项目这样做肯定会觉得很繁琐,而且不够优雅。因此我们需要有一位总管代替我们去干这个,这个总管就是容器。类的依赖管理全部交给容器去完成。因此,一般来说容器是一个全局的对象,大家共有的。

做一个自己的DI容器

写一个功能,我们首先需要分析问题,因此我们先要明白,对于一个简单的DI容器需要哪些功能,这直接关系到我们代码的编写。对于一个简单的容器,至少需要满足以下几点:

  • 创建所需类的实例

  • 完成依赖管理(DI)

  • 可以获取单例的实例

  • 全局唯一

综上,我们的容器类大约长这样:

class Container{    /**     * 单例     * @var Container     */    protected static $instance;    /**     * 容器所管理的实例     * @var array     */    protected $instances = [];    private function __construct(){}      private function __clone(){}    /**     * 获取单例的实例     * @param string $class     * @param array ...$params     * @return object     */    public function singleton($class, ...$params)    {}    /**     * 获取实例(每次都会创建一个新的)     * @param string $class     * @param array ...$params     * @return object     */    public function get($class, ...$params)    {}    /**     * 工厂方法,创建实例,并完成依赖注入     * @param string $class     * @param array $params     * @return object     */    protected function make($class, $params = [])    {}    /**     * @return Container     */    public static function getInstance()    {        if (null === static::$instance) {            static::$instance = new static();        }        return static::$instance;    }}

大体骨架已经确定,接下来进入最核心的make方法:

protected function make($class, $params = []){  //如果不是反射类根据类名创建  $class = is_string($class) ? new ReflectionClass($class) : $class;  //如果传的入参不为空,则根据入参创建实例  if (!empty($params)) {    return $class->newInstanceArgs($params);  }  //获取构造方法  $constructor = $class->getConstructor();  //获取构造方法参数  $parameterClasses = $constructor ? $constructor->getParameters() : [];  if (empty($parameterClasses)) {    //如果构造方法没有入参,直接创建    return $class->newInstance();  } else {    //如果构造方法有入参,迭代并递归创建依赖类实例    foreach ($parameterClasses as $parameterClass) {      $paramClass = $parameterClass->getClass();      $params[] = $this->make($paramClass);    }    //最后根据创建的参数创建实例,完成依赖的注入    return $class->newInstanceArgs($params);  }}

为了容器的易用,我做了一些完善:

  • 实现ArrayAccess接口,使单例实例可以直接通过array的方式获取,如果该实例没有,则创建

  • 重写__get方法,更方便的获取

最终版:

class Container implements ArrayAccess{    /**     * 单例     * @var Container     */    protected static $instance;    /**     * 容器所管理的实例     * @var array     */    protected $instances = [];    private function __construct(){}    private function __clone(){}    /**     * 获取单例的实例     * @param string $class     * @param array  ...$params     * @return object     */    public function singleton($class, ...$params)    {        if (isset($this->instances[$class])) {            return $this->instances[$class];        } else {            $this->instances[$class] = $this->make($class, $params);        }        return $this->instances[$class];    }    /**     * 获取实例(每次都会创建一个新的)     * @param string $class     * @param array  ...$params     * @return object     */    public function get($class, ...$params)    {        return $this->make($class, $params);    }    /**     * 工厂方法,创建实例,并完成依赖注入     * @param string $class     * @param array  $params     * @return object     */    protected function make($class, $params = [])    {        //如果不是反射类根据类名创建        $class = is_string($class) ? new ReflectionClass($class) : $class;        //如果传的入参不为空,则根据入参创建实例        if (!empty($params)) {            return $class->newInstanceArgs($params);        }        //获取构造方法        $constructor = $class->getConstructor();        //获取构造方法参数        $parameterClasses = $constructor ? $constructor->getParameters() : [];        if (empty($parameterClasses)) {            //如果构造方法没有入参,直接创建            return $class->newInstance();        } else {            //如果构造方法有入参,迭代并递归创建依赖类实例            foreach ($parameterClasses as $parameterClass) {                $paramClass = $parameterClass->getClass();                $params[] = $this->make($paramClass);            }            //最后根据创建的参数创建实例,完成依赖的注入            return $class->newInstanceArgs($params);        }    }    /**     * @return Container     */    public static function getInstance()    {        if (null === static::$instance) {            static::$instance = new static();        }        return static::$instance;    }    public function __get($class)    {        if (!isset($this->instances[$class])) {            $this->instances[$class] = $this->make($class);        }        return $this->instances[$class];    }    public function offsetExists($offset)    {        return isset($this->instances[$offset]);    }    public function offsetGet($offset)    {        if (!isset($this->instances[$offset])) {            $this->instances[$offset] = $this->make($offset);        }        return $this->instances[$offset];    }    public function offsetSet($offset, $value)    {    }    public function offsetUnset($offset) {        unset($this->instances[$offset]);    }}

现在借助容器我们写一下上面的代码:

$driver = $app->get(Driver::class);$driver->drive();//output:老司机正在驾驶普通汽车复制代码

就这么简单,老司机就能发车。这里默认注入的是Car的实例,如果需要开奔驰,那只需要这样:

$benz = $app->get(Benz::class);$driver = $app->get(Driver::class, $benz);$driver->drive();//output:老司机正在驾驶奔驰复制代码

按照PSR-11的要求,依赖注入容器需要实现Psr\Container\ContainerInterface接口,这里只是演示并未去实现,因为那需要引入Psr依赖库,比较麻烦,其实也很简单,只是多了几个方法,有兴趣的可以自己去了解下PSR-11的要求(传送门)。

这里只是实现了一个非常简陋的DI容器,实际中还需要考虑很多,而且这里的容器功能上还很简陋。还有一些坑没处理,比如出现循环依赖怎么处理、延迟加载的机制……

感谢各位的阅读,以上就是"怎么创建PHP DI容器"的内容了,经过本文的学习后,相信大家对怎么创建PHP DI容器这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

容器 实例 方法 司机 代码 对象 管理 参数 正在 驾驶 两个 功能 只是 就是 工厂 模式 面的 学习 耦合 普通 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网新科技 天津市摩尔互联网科技 互联网科技公司起名 互联网金融科技创新 上海喔噻互联网科技 互联网科技网站 顺网科技2021年互联网排名 深圳互联网科技怎么样 成都玖崟互联网科技有限公司乐商圈怎么样 剑昆互联网络科技有限公司临桂 宁夏众和广鑫互联网科技有限公司 成都铂石互联网科技做什么的 浙南科技城互联网大厦建设工程 威龙国际互联网科技公司 半导体是互联网高科技吗 新氧科技和美国互联网有关系吗 互联网科技手抄报图片 互联网科技收购价值 互联网的科技体现在哪里 互联网企业和网络科技有限公司 云南咖啡 上海喔噻互联网科技有限公司 礼当家互联网科技 银优互联网科技有限公司 顺网科技2021年互联网排名 须眉科技是互联网公司吗 玉溪新华互联网科技好不好 保山互联网科技靠谱吗 互联网科技对化妆品行业 科技发展第三阶段互联网时代
0