• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    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]);
      }
    }
    
    

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

    点击这里查看完整代码

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    您可能感兴趣的文章:
    • PHP 应用容器化以及部署方法
    • 深入理解 PHP7 中全新的 zval 容器和引用计数机制
    • PHP解耦的三重境界(浅谈服务容器)
    • PHP进阶学习之依赖注入与Ioc容器详解
    • php 接口类与抽象类的实际作用
    • php接口和抽象类使用示例详解
    • PHP调用wsdl文件类型的接口代码分享
    • PHP生成json和xml类型接口数据格式
    • PHP中抽象类、接口的区别与选择分析
    • PHP容器类的两种实现方式示例
    上一篇:PDO::_construct讲解
    下一篇:PHP将英文数字转换为阿拉伯数字实例讲解
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

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