加入收藏 | 设为首页 | 会员中心 | 我要投稿 开发网_郴州站长网 (http://www.0735zz.com/)- 云通信、区块链、物联设备、云计算、站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

PHP实现一个轻量级容器的技巧

发布时间:2022-07-14 12:05:05 所属栏目:PHP教程 来源:互联网
导读:这篇文章主要介绍了PHP实现一个轻量级容器的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。 什么是容器 在开发过程中,经常会用到的一个概率就是依赖注入,我们借助依懒注
  这篇文章主要介绍了PHP实现一个轻量级容器的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
 
  什么是容器
 
  在开发过程中,经常会用到的一个概率就是依赖注入,我们借助依懒注入来解耦代码,选择性的按需加载服务,而这些通常都是借助容器来实现。
 
  容器实现对类的统一管理,并且确保对象实例的唯一性
 
  常用的容器网上有很多,如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]);
    }
  实际我们容器中注入的对象是多种多样的,所以我们单独抽出实例化方法。
 
  public function make($name)
    {
      if (!is_string($name)) {
        throw new InvalidArgumentException(sprintf(
          'The name parameter must be of type string, %s given',
          is_object($name) ? get_class($name) : gettype($name)
        ));
      }
   
      if (isset($this->resolvedEntries[$name])) {
        return $this->resolvedEntries[$name];
      }
   
      if (!$this->has($name)) {
        throw new NotFoundException("No entry or class found for {$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_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]);
    }
  }
  这样我们就拥有了一个功能丰富,使用方便的轻量级容器了,赶快整合到你的项目中去吧。

(编辑:开发网_郴州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读