当前位置:首页 > PHP教程 > php应用 > 列表

PHP实现一个轻量级容器的方法

发布:smiling 来源: PHP粉丝网  添加日期:2021-11-06 20:47:58 浏览: 评论:0 

这篇文章主要介绍了PHP实现一个轻量级容器的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。

什么是容器

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

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

常用的容器网上有很多,如PHP-DI 、 YII-DI 等各种实现,通常他们要么大而全,要么高度适配特定业务,与实际需要存在冲突。

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

PSR-11

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

  1. /** 
  2.  * Describes the interface of a container that exposes methods to read its entries. 
  3.  */ 
  4. interface ContainerInterface 
  5.   /** 
  6.    * Finds an entry of the container by its identifier and returns it. 
  7.    * 
  8.    * @param string $id Identifier of the entry to look for. 
  9.    * 
  10.    * @throws NotFoundExceptionInterface No entry was found for **this** identifier. 
  11.    * @throws ContainerExceptionInterface Error while retrieving the entry. 
  12.    * 
  13.    * @return mixed Entry. 
  14.    */ 
  15.   public function get($id); 
  16.  
  17.   /** 
  18.    * Returns true if the container can return an entry for the given identifier. 
  19.    * Returns false otherwise. 
  20.    * 
  21.    * `has($id)` returning true does not mean that `get($id)` will not throw an exception. 
  22.    * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`. 
  23.    * 
  24.    * @param string $id Identifier of the entry to look for. 
  25.    * 
  26.    * @return bool 
  27.    */ 
  28.   public function has($id); 

实现示例

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

  1. abstract class AbstractContainer implements ContainerInterface 
  2.  
  3.   protected $resolvedEntries = []; 
  4.  
  5.   /** 
  6.    * @var array 
  7.    */ 
  8.   protected $definitions = []; 
  9.  
  10.   public function __construct($definitions = []) 
  11.   { 
  12.     foreach ($definitions as $id => $definition) { 
  13.       $this->injection($id$definition); 
  14.     } 
  15.   } 
  16.  
  17.   public function get($id
  18.   { 
  19.  
  20.     if (!$this->has($id)) { 
  21.       throw new NotFoundException("No entry or class found for {$id}"); 
  22.     } 
  23.  
  24.     $instance = $this->make($id); 
  25.  
  26.     return $instance
  27.   } 
  28.  
  29.   public function has($id
  30.   { 
  31.     return isset($this->definitions[$id]); 
  32.   } 

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

  1. public function make($name
  2.   { 
  3.     if (!is_string($name)) { 
  4.       throw new \InvalidArgumentException(sprintf( 
  5.         'The name parameter must be of type string, %s given'
  6.         is_object($name) ? get_class($name) : gettype($name
  7.       )); 
  8.     } 
  9.  
  10.     if (isset($this->resolvedEntries[$name])) { 
  11.       return $this->resolvedEntries[$name]; 
  12.     } 
  13.  
  14.     if (!$this->has($name)) { 
  15.       throw new NotFoundException("No entry or class found for {$name}"); 
  16.     } 
  17.  
  18.     $definition = $this->definitions[$name]; 
  19.     $params = []; 
  20.     if (is_array($definition) && isset($definition['class'])) { 
  21.       $params = $definition
  22.       $definition = $definition['class']; 
  23.       unset($params['class']); 
  24.     } 
  25.  
  26.     $object = $this->reflector($definition$params); 
  27.  
  28.     return $this->resolvedEntries[$name] = $object
  29.   } 
  30.  
  31.   public function reflector($concretearray $params = []) 
  32.   { 
  33.     if ($concrete instanceof \Closure) { 
  34.       return $concrete($params); 
  35.     } elseif (is_string($concrete)) { 
  36.       $reflection = new \ReflectionClass($concrete); 
  37.       $dependencies = $this->getDependencies($reflection); 
  38.       foreach ($params as $index => $value) { 
  39.         $dependencies[$index] = $value
  40.       } 
  41.       return $reflection->newInstanceArgs($dependencies); 
  42.     } elseif (is_object($concrete)) { 
  43.       return $concrete
  44.     } 
  45.   } 
  46.  
  47.   /** 
  48.    * @param \ReflectionClass $reflection 
  49.    * @return array 
  50.    */ 
  51.   private function getDependencies($reflection
  52.   { 
  53.     $dependencies = []; 
  54.     $constructor = $reflection->getConstructor(); 
  55.     if ($constructor !== null) { 
  56.       $parameters = $constructor->getParameters(); 
  57.       $dependencies = $this->getParametersByDependencies($parameters); 
  58.     } 
  59.  
  60.     return $dependencies
  61.   } 
  62.  
  63.   /** 
  64.    * 
  65.    * 获取构造类相关参数的依赖 
  66.    * @param array $dependencies 
  67.    * @return array $parameters 
  68.    * */ 
  69.   private function getParametersByDependencies(array $dependencies
  70.   { 
  71.     $parameters = []; 
  72.     foreach ($dependencies as $param) { 
  73.       if ($param->getClass()) { 
  74.         $paramName = $param->getClass()->name; 
  75.         $paramObject = $this->reflector($paramName); 
  76.         $parameters[] = $paramObject
  77.       } elseif ($param->isArray()) { 
  78.         if ($param->isDefaultValueAvailable()) { 
  79.           $parameters[] = $param->getDefaultValue(); 
  80.         } else { 
  81.           $parameters[] = []; 
  82.         } 
  83.       } elseif ($param->isCallable()) { 
  84.         if ($param->isDefaultValueAvailable()) { 
  85.           $parameters[] = $param->getDefaultValue(); 
  86.         } else { 
  87.           $parameters[] = function ($arg) { 
  88.           }; 
  89.         } 
  90.       } else { 
  91.         if ($param->isDefaultValueAvailable()) { 
  92.           $parameters[] = $param->getDefaultValue(); 
  93.         } else { 
  94.           if ($param->allowsNull()) { 
  95.             $parameters[] = null; 
  96.           } else { 
  97.             $parameters[] = false; 
  98.           } 
  99.         } 
  100.       } 
  101.     } 
  102.     return $parameters
  103.   } 

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

  1. /** 
  2.    * @param string $id 
  3.    * @param string | array | callable $concrete 
  4.    * @throws ContainerException 
  5.    */ 
  6.   public function injection($id$concrete
  7.   { 
  8.     if (is_array($concrete) && !isset($concrete['class'])) { 
  9.       throw new ContainerException('数组必须包含类定义'); 
  10.     } 
  11.  
  12.     $this->definitions[$id] = $concrete
  13.   } 

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

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

  1. class Container extends AbstractContainer implements \ArrayAccess 
  2.  
  3.   public function offsetExists($offset
  4.   { 
  5.     return $this->has($offset); 
  6.   } 
  7.  
  8.   public function offsetGet($offset
  9.   { 
  10.     return $this->get($offset); 
  11.   } 
  12.  
  13.   public function offsetSet($offset$value
  14.   { 
  15.     return $this->injection($offset$value); 
  16.   } 
  17.  
  18.   public function offsetUnset($offset
  19.   { 
  20.     unset($this->resolvedEntries[$offset]); 
  21.     unset($this->definitions[$offset]); 
  22.   } 

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

Tags: PHP轻量级容器

分享到: