什么是容器

在开发过程中,经常会用到的一个概念就是依赖注入。我们借助依懒注入来解耦代码,选择性的按需加载服务,而这些通常都是借助容器来实现。

容器实现对对象的统一管理,并且确保对象实例的唯一性

容器可以很轻易的找到有很多实现示例,如 PHP-DI 、 YII-DI 等各种实现,通常他们要么大而全,要么高度适配特定业务,与实际需要存在冲突。

出于需要,我们自己造一个轻量级的轮子,为了保持规范,我们基于 PSR-11 来实现。

PSR-11

PSR 是 php-fig 提供的标准化建议,虽然不是官方组织,但是得到广泛认可。PSR-11 提供了容器接口。它包含 ContainerInterface 和 两个异常接口,并提供使用建议。

/*** Describes the interface of a container that exposes methods to read its entries.*/
interface ContainerInterface
{/*** Finds an entry of the container by its identifier and returns it.** @param string $id Identifier of the entry to look for.** @throws NotFoundExceptionInterface  No entry was found for **this** identifier.* @throws ContainerExceptionInterface Error while retrieving the entry.** @return mixed Entry.*/public function get($id);/*** Returns true if the container can return an entry for the given identifier.* Returns false otherwise.** `has($id)` returning true does not mean that `get($id)` will not throw an exception.* It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.** @param string $id Identifier of the entry to look for.** @return bool*/public function has($id);
}

实现示例

我们先来实现接口中要求的两个方法

abstract class AbstractContainer implements ContainerInterface
{protected $resolvedEntries = [];/*** @var array*/protected $definitions = [];public function __construct($definitions = []){foreach ($definitions as $id => $definition) {$this->injection($id, $definition);}}public function get($id){if (!$this->has($id)) {throw new NotFoundException("No entry or class found for {$id}");}$instance = $this->make($id);return $instance;}public function has($id){return isset($this->definitions[$id]);}

实际我们容器中注入的对象是多种多样的,所以我们单独抽出实例化方法。

    protected function make($name){if (isset($this->resolvedEntries[$name])) {return $this->resolvedEntries[$name];}$definition = $this->definitions[$name];$params = [];if (is_array($definition) && isset($definition['class'])) {$params = $definition;$definition = $definition['class'];unset($params['class']);}$object = $this->reflector($definition, $params);return $this->resolvedEntries[$name] = $object;}public function reflector($concrete, array $params = []){if ($concrete instanceof \Closure) {return $concrete($params);} elseif (is_string($concrete)) {$reflection = new \ReflectionClass($concrete);$dependencies = $this->getDependencies($reflection);foreach ($params as $index => $value) {$dependencies[$index] = $value;}return $reflection->newInstanceArgs($dependencies);} elseif (is_object($concrete)) {return $concrete;}}/*** @param \ReflectionClass $reflection* @return array*/private function getDependencies($reflection){$dependencies = [];$constructor = $reflection->getConstructor();if ($constructor !== null) {$parameters = $constructor->getParameters();$dependencies = $this->getParametersByDependencies($parameters);}return $dependencies;}/**** 获取构造类相关参数的依赖* @param array $dependencies* @return array $parameters* */private function getParametersByDependencies(array $dependencies){$parameters = [];foreach ($dependencies as $param) {if ($param->getClass()) {$paramName = $param->getClass()->name;$paramObject = $this->reflector($paramName);$parameters[] = $paramObject;} elseif ($param->isArray()) {if ($param->isDefaultValueAvailable()) {$parameters[] = $param->getDefaultValue();} else {$parameters[] = [];}} elseif ($param->isCallable()) {if ($param->isDefaultValueAvailable()) {$parameters[] = $param->getDefaultValue();} else {$parameters[] = function ($arg) {};}} else {if ($param->isDefaultValueAvailable()) {$parameters[] = $param->getDefaultValue();} else {if ($param->allowsNull()) {$parameters[] = null;} else {$parameters[] = false;}}}}return $parameters;}

如你所见,到目前为止我们只实现了从容器中取出实例,从哪里去提供实例定义呢,所以我们还需要提供一个方法.

    /*** @param string $id* @param string | array | callable $concrete* @throws ContainerException*/public function injection($id, $concrete){if (!is_string($id)) {throw new \InvalidArgumentException(sprintf('The id parameter must be of type string, %s given',is_object($id) ? get_class($id) : gettype($id)));}if (is_array($concrete) && !isset($concrete['class'])) {throw new ContainerException('数组必须包含类定义');}$this->definitions[$id] = $concrete;}

只有这样吗?对的,有了这些操作我们已经有一个完整的容器了,插箱即用。

不过为了使用方便,我们可以再提供一些便捷的方法,比如数组式访问。

class Container extends AbstractContainer implements \ArrayAccess
{public function offsetExists($offset){return $this->has($offset);}public function offsetGet($offset){return $this->get($offset);}public function offsetSet($offset, $value){return $this->injection($offset, $value);}public function offsetUnset($offset){unset($this->resolvedEntries[$offset]);unset($this->definitions[$offset]);}
}

这样我们就拥有了一个功能丰富,使用方便的轻量级容器了,赶快整合到你的项目中去吧。

点击这里查看完整代码

PHP实现一个轻量级容器相关推荐

  1. [UWP]实现一个轻量级的应用内消息通知控件

    [UWP]实现一个轻量级的应用内消息通知控件 原文:[UWP]实现一个轻量级的应用内消息通知控件 在UWP应用开发中,我们常常有向用户发送一些提示性消息的需求.这种时候我们一般会选择MessageDi ...

  2. RunC 轻量级 容器运行工具 简介

    RunC 是什么? RunC 是一个轻量级的工具,它是用来运行容器的,只用来做这一件事,并且这一件事要做好.我们可以认为它就是个命令行小工具,可以不用通过 docker 引擎,直接运行容器.事实上,r ...

  3. Google Guice 一个轻量级的依赖注入框架

    1.美图 2.概述 2.1 背景 在做项目的时候,看见有段代码直接是使用Google Guice 注入了avaitor表达式. 2.1 官网 Github 主页:https://github.com/ ...

  4. hadoop 依赖式job_每天一学:一个轻量级分布式任务调度框架 XXL-JOB

    概述 XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源代码并接入多家公司线上产品线,开箱即用. 官方地址中文版:http://www.xux ...

  5. Task一个轻量级分布式任务计算系统

    Task系统设计与使用 Task是一个轻量级的分布式任务计算系统,他可以帮助你快速编写一个可以在集群环境下运行的分布式方法,而这只需要你使用一行代码就可以在你原有的方法上做到. 一个简单例子: pub ...

  6. 轻量级容器主机 Photon OS

    轻量级容器主机 Photon OS Photon OS photon-jumpbox Features 轻量级容器主机 默认安全 实时内核支持 安装指南 Github 什么是 Photon 操作系统? ...

  7. java 轻量级 job_oxygen: 一个轻量级Java框架,包含ioc、aop、config、cache、job、Jdbc、web等...

    oxygen 轻量级Java框架 介绍 一个轻量级Java框架 oxygen-core 配置管理,支持${attrs.key:defaultValue}表达式获取配置 加解密管理,提供加解密服务内置基 ...

  8. 一个轻量级的域名解析服务器软件--dnsmasq

    一.前言 最近,在研究Hyperledger Fabric的多机部署.因为docker容器分布在多台机器上,解决相互之间通信问题的方案通常有以下几种: K8s,虽然k8s也可以解决这个问题,但是k8s ...

  9. net微服务框架/c#/netcore微服务框架,一个轻量级的.Net 5.0微服务开发框架,同时也适用于单体架构系统的开发

    net微服务框架/c#/netcore微服务框架,一个轻量级的.Net 5.0微服务开发框架,同时也适用于单体架构系统的开发. 支持经典三层与DDD架构开发模式.集成了一系列主流稳定的微服务配套技术栈 ...

最新文章

  1. RequireJs的使用和快速理解
  2. 7分钟了解科大讯飞开发者节:AI红利期来临,全新1024计划发布(未完待续)
  3. 实例:ABAP权限对象设计与权限检查的实现
  4. Python高阶函数和函数嵌套
  5. boost::mp11::mp_max_element相关用法的测试程序
  6. 电除尘原理计算机机箱,高炉煤气布袋除尘器计算机监控系统
  7. 领域应用 | 金融资管领域知识图谱的构建和应用
  8. 区块链企业级解决方案 ( Hyperledger )
  9. 深入理解MySQL底层架构,看这一篇文章就够了!
  10. python3 读取写入excel操作-win32com
  11. Microsoft Jet 数据库引擎打不开文件,它已经被别的用户以独占方式打开,或没有查看数据的权限。...
  12. 使用ajax请求cgi,Python CGI同步AJAX请求
  13. ad09只在一定范围内查找相似对象_AD16中filter查找相似对象list inspector四大编辑指令使用方法...
  14. 2017年10月30日360最新虚拟壳脱壳后完全修复
  15. qp_查看表的数据是否更新了指定的某一天数据
  16. 云展网教程 | 如何更改logo和设置点击logo时的转跳链接?
  17. GameCenter首次登录很慢的解决方案
  18. AndroidStudio 使用ADB命令进行 WIFI调试
  19. “独享宽带”是什么意思?
  20. 01_邂逅vue3开发

热门文章

  1. SpringCloud 从菜鸟到大牛之五 统一配置中心 Spring Cloud Config
  2. 实现tomcat的https单向认证及双向认证
  3. oracle case套case,,套用when case
  4. 一行命令轻松获取JVM默认配置
  5. SpringCloud 如何搭建Eureka注册中心
  6. 【MyBatis框架】高级映射-一对一查询
  7. Markdown绘制UML图
  8. java 泛型 多态_Java 多态
  9. 关于面试时碰到的几个多线程手撕代码题
  10. 分布式幂等性如何保证