在看 Laravel 文档的时候发现入门指南的下一章便是核心架构,对于我这种按部就班往下读的同学这简直是劝退篇。各种之前没有接触过的概念砸得人头晕,容器便是其中之一。不过在拜读过几篇文章后也逐渐理解了容器的作用,所以特此总结一番。

0x01 为何要有容器?

这个问题可以也可以替换为「容器解决了什么问题?」。在此之前我们需要理解依赖注入这个概念,可以看一下这篇文章: 简单解释什么是 依赖注入 和 控制反转

。在实践依赖注入的时候我们会遇到一个问题,这里我将通过示例代码解释,代码如下:

class Bread

{

}

class Bacon

{

}

class Hamburger

{

protected $materials;

public function __construct(Bread $bread, Bacon $bacon)

{

$this->materials = [$bread, $bacon];

}

}

class Cola

{

}

class Meal

{

protected $food;

protected $drink;

public function __construct(Hamburger $hamburger, Cola $cola)

{

$this->food = $hamburger;

$this->drink = $cola;

}

}

复制代码

上面是按照依赖注入实现的一段代码,我们可以看见套餐类(Meal)依赖汉堡类(Hamburger)和可乐类(Cola),并且汉堡类又依赖于面包类(Bread)和培根类(Bacon)。通过依赖注入能达到松耦合的效果但是这也使得实例化一个有多个依赖的类会变得十分麻烦,下面这段代码是实例化一个套餐类的示例:

$bread = new Bread();

$bacon = new Bacon();

$hamburger = new Hamburger($bread, $bacon);

$cola = new Cola();

$meal = new Meal($hamburger, $cola);

复制代码

可以看见为了获得一个套餐对象,我们需要先实例化该对象的依赖,如果依赖还存在依赖,我们还需要在实例化依赖的依赖……为了解决这个问题容器就应运而生了,容器的定位就是「管理类的依赖和执行依赖注入的工具」。通过容器我们可以将实例化这个过程给自动化,比如我们可以直接用一行代码获取套餐对象:

$container->get(Meal::class);

复制代码

0x01 简单容器的实现

下面这段代码是一个简单容器的实现:

class Container

{

/**

* @var Closure[]

*/

protected $binds = [];

/**

* Bind class by closure.

*

* @param string $class

* @param Closure $closure

* @return $this

*/

public function bind(string $class, Closure $closure)

{

$this->binds[$class] = $closure;

return $this;

}

/**

* Get object by class

*

* @param string $class

* @param array $params

* @return object

*/

public function make(string $class, array $params = [])

{

if (isset($this->binds[$class])) {

return ($this->binds[$class])->call($this, $this, ...$params);

}

return new $class(...$params);

}

}

复制代码

这个容器只有两个方法 bind

和 make

, bind

方法将一个类名和一个闭包进行绑定,然后 make

方法将执行指定类名对应的闭包,并返回该闭包的返回值。我们通过容器的使用示例加深理解:

$container = new Container();

$container->bind(Hamburger::class, function (Container $container) {

$bread = $container->make(Bread::class);

$bacon = $container->make(Bacon::class);

return new Hamburger($bread, $bacon);

});

$container->bind(Meal::class, function (Container $container) {

$hamburger = $container->make(Hamburger::class);

$cola = $container->make(Cola::class);

return new Meal($hamburger, $cola);

});

// 输出 Meal

echo get_class($container->make(Meal::class));

复制代码

通过上面这个例子我们可以知道 bind

方法传递的是一个「返回类名对应的实例化对象」的闭包,而且该闭包还接收该容器作为参数,所以我们还可以在该闭包内使用容器获取依赖。上面这段代码虽然看起来似乎比使用 new

关键字还复杂,但实际上对每一个类,我们只需要 bind

一次即可。以后每次需要该对象直接用 make

方法即可,在我们的工程中肯定会节省很多代码量。

0x02 通过反射强化容器

「反射」官方手册php.net/manual/zh/b…

在上面的的简单容器的例子里,我们还需要通过 bind

方法写好实例化的「脚本」,那我们试想有没有一种方法能够直接生成我们需要的实例呢?其实通过「反射」并在构造函数指定参数的「类型提示类」我们就能实现自动解决依赖的功能。因为通过反射我们可以获取指定类构造函数所需要的参数和参数类型,所以我们的容器可以自动解决这些依赖。示例代码如下:

/**

* Get object by class

*

* @param string $class

* @param array $params

* @return object

*/

public function make(string $class, array $params = [])

{

if (isset($this->binds[$class])) {

return ($this->binds[$class])->call($this, $this, ...$params);

}

return $this->resolve($abstract);

}

/**

* Get object by reflection

*

* @param $abstract

* @return object

* @throws ReflectionException

*/

protected function resolve($abstract)

{

// 获取反射对象

$constructor = (new ReflectionClass($abstract))->getConstructor();

// 构造函数未定义,直接实例化对象

if (is_null($constructor)) {

return new $abstract;

}

// 获取构造函数参数

$parameters = $constructor->getParameters();

$arguments = [];

foreach ($parameters as $parameter) {

// 获得参数的类型提示类

$paramClassName = $parameter->getClass()->name;

// 参数没有类型提示类,抛出异常

if (is_null($paramClassName)) {

throw new Exception('Fail to get instance by reflection');

}

// 实例化参数

$arguments[] = $this->make($paramClassName);

}

return new $abstract(...$arguments);

}

复制代码

以上代码基于只是修改了原容器类的 make

方法, binds

数组中没有找到指定类绑定的闭包后执行 resolve

方法。其中 resolve

方法只是简单的通过反射获取指定类的构造函数并将其依赖实例化,最后实例化指定类。到了这一步以后我们实例化套餐类就真的只需要一行代码了,连配置都不用:-D。

$container->make(Meal::class);

复制代码

当然现在这个容器还是相当简陋的,因为如果指定类依赖标量值(比如:字符串,数组,数值等非对象类型)会直接抛出异常,也无法指定部分依赖并且如果依赖的是接口的话还会出错/(ㄒoㄒ)/~~,但这些功能都在一些成熟的容器库都有。如果感兴趣可以去看它们的源代码,这里我推荐看 Pipmle

这个项目。

0x03 总结

本文主要介绍了容器的应用场景并实现了一个简单的容器,通过使用容器我们能够很方便的解决依赖注入带来的问题。但是容器也并不是没有缺点,因为大部分容器都应用了反射技术,这会带来较大的性能消耗而且通过容器间接生成的实例 IDE 往往不能识别它的类型,所以就不会有自动提示(可以通过写文档注释解决)。不过个人感觉引入容器其实还是利大于弊滴(纯属个人感觉)!

php 容器实现,PHP 依赖注入容器实现相关推荐

  1. 又一个强大的PHP5.3依赖注入容器

    简单的服务容器 一个简单的 php 5.3 依赖注入容器. 项目地址:https://github.com/godruoyi/easy-container Why 目前比较流行的 PHP 容器: Pi ...

  2. 依赖注入容器Unity Application Block快速入门

    概述 Unity是微软模式与实践团队开发的一个轻量级.可扩展的依赖注入容器,之前我也有过一篇文章<Enterprise Library 4.0中的依赖注入容器(Unity)预览>对其做过介 ...

  3. Pimple - 一个简单的 PHP 依赖注入容器

    链接 官网 WebSite GitHub - Pimple 这是 Pimple 3.x 的文档.如果你正在使用 Pimple 1.x ,请查看 Pimple 1.x 文档. 阅读 Pimple 1.x ...

  4. 依赖注入容器Autofac的详解[转]

    依赖注入容器Autofac的详解 发表于 2011 年 09 月 22 日 由 renfengbin 分享到:GMAIL邮箱         Hotmail邮箱 delicious digg Auto ...

  5. spring依赖注入_Spring源码阅读:Spring依赖注入容器

    依赖注入 依赖注入是Spring框架最核心的能力,Spring框架提供的AOP,WebMVC等其它功能都是以依赖注入容器作为基础构建的,Spring依赖注入容器类似于一个用于组装对象的框架内核,任何应 ...

  6. asp.net core自定义依赖注入容器,替换自带容器

    依赖注入 在asp.net core程序中,众所周知,依赖注入基本上贯穿了整个项目,以通用的结构来讲解,控制器层(Controller层)依赖业务层(Service层),业务层依赖于仓储层(Repos ...

  7. ASP.NET Core依赖注入容器中的动态服务注册

    介绍 在ASP.NET Core中,每当我们将服务作为依赖项注入时,都必须将此服务注册到ASP.NET Core依赖项注入容器.但是,一个接一个地注册服务不仅繁琐且耗时,而且容易出错.因此,在这里,我 ...

  8. php依赖注入 简书,PHP 依赖注入容器

    2020-11-13_5fae3e971d521.png 介绍 使用 PHP 的反射类 ReflectionClass,创建容器方便管理依赖注入. 代码 interface GrowthInterfa ...

  9. 通过编写DI容器来了解依赖注入-从头开始! (第2部分)

    这是我的" DI从零开始"系列的第二部分. 在上一篇文章中,我们讨论了我们的基本示例以及"手动"方法存在的问题. 现在,我们要使服务网络的布线自动化. DI S ...

最新文章

  1. 解读2019华为第001号文件:AI时代软件开发的第一要义是可信
  2. 最远的你是我最近的爱
  3. 浅析TCP之SACK(选择性确认)
  4. OpenCV推断onnx格式目标检测模型(SSD)(附完整代码)
  5. python 重启内核_Python从零开始的内核回归
  6. 制作作品图片_不懂人文后期制作流程?来,大师手把手教你
  7. Yahoo,希望你和微软Bing能过的幸福
  8. 跨越DDD从理论到工程落地的鸿沟
  9. Mysql学习总结(52)——最全面的MySQL 索引详解
  10. 腾讯京东内部大换血,高管“遭殃”,年轻人“欢呼”!
  11. c语言数组中循坏移动问题,如何将一个数组的元素循环左移?
  12. c++ 之条件运算符--- ?:
  13. [Android实例教程] 教你如何拍照+相册选择图片+剪裁图片完整实现
  14. Python学习手册之类和继承
  15. explain mysql执行顺序_面试前必须知道的MySQL命令【explain】
  16. phpstudy不执行php文件,phpstudy运行时突然无法报错
  17. 《统计学》第八版贾俊平第五章概率与概率分布
  18. excel转置怎么操作_Excel如何快速将一行转置成一列,一列转置成一行?
  19. 网页设计课设【登录注册系统及增删改查】
  20. python3强智教务系统个人课表爬虫

热门文章

  1. 最全的搜索引擎优化(SEO)术语表
  2. QT使用winsocket
  3. no.5_得到4升的水
  4. split分片主要源码解析
  5. 【Servlet】JSP学习-概念和运行原理
  6. python计算n到n+100之间所有奇数的和_Python基本操作题
  7. RunTime.getRunTime().addShutdownHook的用法
  8. 01.elasticsearch-mapping全面解析
  9. 洛谷——P1583 魔法照片
  10. 报错:out cannot be resolved【已解决】