怎么创建PHP DI容器

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

本文主要讲解“如何创建一个PHP DI容器”。本文的解释简单明了,易学易懂。接下来,请跟随边肖的思路一起学习和学习“如何创建一个PHP DI容器”!

00-1010先开车,给你一个栗子:

class driver { public function drive()

{

$ car=new car();Echo '老司机在开车',$car-getCar(),PHP _ EOL

}

}classCar{protected$name='普通汽车';publicfunctiongetCar()

{ return $ this-name;

}

}有两个班,司机和汽车。旧驱动程序有方法驱动程序。打电话时,他先拿到整辆车的$car,然后发动汽车。大部分同学都写过这个或者类似的代码,这个代码列表没有问题,挺正常的。但是,要换车,开普通车赶不上姐姐。

clasbenzextendscar { protected $ name=' Mercedes ';

}这个时候需要做一个恶心的操作,需要换老司机的代码。(老司机:我做错了什么?换车也需要我重新学习驾照。因此,我们需要将Car注入外部世界,将Driver与Car脱钩,而不是老司机自己开车时必须自己造车。于是就有了下面的结果。

classDriver { protected $ car公共功能_ _构造(汽车$汽车)

{ $ this-car=$ car;

}publicfunctiondrive()

{echo '老司机在开车',$this-car-getCar(),PHP _ EOL

}

}此时Driver和Car类已经解耦,这两个类的依赖关系依赖于上层代码来管理。此时,老司机会这样“开车”:

$ car=new car();

$ driver=new driver($ car);

$ driver-drive();此时,我们创建一个驱动程序依赖并注入的实例。在上面的例子中,我们已经实现了依赖注入,但是它是手动的,写起来还是感觉不舒服。这么重的活怎么能手动完成?让程序自己做。于是,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容器这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/128943.html

(0)

相关推荐

  • MySQL怎么进行批量更新和删除数据shell脚本

    技术MySQL怎么进行批量更新和删除数据shell脚本MySQL怎么进行批量更新和删除数据shell脚本,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。#

    攻略 2021年10月29日
  • Java中如何使用Iterator和Iterable接口

    技术Java中如何使用Iterator和Iterable接口这篇文章主要介绍“Java中如何使用Iterator和Iterable接口”,在日常操作中,相信很多人在Java中如何使用Iterator和Iterable接口

    2021年11月17日
  • mysql中Too many connections问题怎么处理

    技术mysql中Too many connections问题怎么处理这篇文章将为大家详细讲解有关mysql中Too many connections问题怎么处理,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这

    攻略 2021年11月6日
  • ZooKeeper知识点都有哪些

    技术ZooKeeper知识点都有哪些本篇文章为大家展示了ZooKeeper知识点都有哪些,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、 从集中式到分布式1. 集中式集中式特点

    攻略 2021年11月12日
  • 英文大小写26个字母,二十六个字母大小写小学英语

    技术英文大小写26个字母,二十六个字母大小写小学英语1、二十六个字母大小写书写顺序英文大小写26个字母:2、26个英文字母及发音音标如下: A a [ei] B b [bi:] C c [si:] D d [di:] E

    生活 2021年10月23日
  • 颈椎病自我治疗方法,颈椎病自我治疗有哪些好的方法

    技术颈椎病自我治疗方法,颈椎病自我治疗有哪些好的方法1.调整枕头与睡眠体位。首先是枕头的选择。若引起颈椎病的病因是椎间盘突出或椎体后缘有骨刺者颈椎病自我治疗方法,枕头可稍低。如果病因是椎管后方韧带肥厚内陷,对脊髓后方形成

    生活 2021年10月28日