di php容器的创建

先来确定一下容器最基础的功能,首先需要容器能存储对象,提取对象。
容器中的对象可以放在一个数组中统一管理,可以把类名作为key,对象作为value,把对象存在数组中。
提取对象的时候只需要传入类名(key)就可以得到对象。
有些时候直接用类名输入得到对象的方式不方便,比如有些类的名称加上命名空间后很长,而且直接输入类名语义不够明显。
所以可以考虑用一个数组记录类名对应的别名。
所以一共要两个数组 $bind, $instance//$bind记录类的别名和类名的映射 类似于这种:
$bind = [‘Container’ => ‘\think\Container’, ‘Event’ =>’\think\Event’];
$instance记录类名和对象的映射 类似于这种:
$instances = [‘\think\Container’ => new \think\Container, ‘\think\Event’ => function(){return new \think\Event}];
容器类的主要功能就是对这两个数组的增删改查
现在来开始写代码,首先需要创建一个容器类。可以使用单例模式来保存容器对象

class Container
{
  protected static $instance;

 protected $instances = [];

 protected $bind = [];

 public static function getInstance()
 {
    if (is_null(static::$instance)) {
          static::$instance = new static;
      }
      if (static::$instance instanceof Closure) {
          return (static::$instance)();
      }
      return static::$instance;
  }

  public static function setInstance($instance)
  {
    static::$instance = $instance;
  }
}

容器要有最基础的可供外部使用的几个方法 :
bind($abstract, $concrete);绑定类的标识。
$abstract理解为类的别名,$concrete理解为类名
instance(string $abstract, $instance)绑定类名->对象
get($abstract)通过类名获取对象

判断容器中是否存在类及标识

  public function bound(string $abstract):bool
  {
    return isset($this->bind[$abstract]) || isset($this>instances[$abstract]);
  }

根据别名获取真是类名

  public function getAlias(string $abstract):string
  {
      if(isset($this->bind[$abstract])) {
          $bind = $this->bind[$abstract];
         if (is_string($bind)) {
              return $this->getAlias($bind);
          }
     }
    return $abstract;
  }

判断容器中是否存在对象的实例

 public function exists(string $abstract)
 {
    $abstract = $this->getAlias($abstract);
     return isset($this->instances[$abstract]);
  }

绑定一个类、闭包、实例、接口实现到容器
如果$abstract输入的是数组那么把数组的key,value递归的绑定
如果$concrete传入闭包直接放入$bind数组中
如果$concrete是对象则放入$instances数组中
如果是字符串则放入$bind中

 public function bind($abstract, $concrete = null)
 {
    if (is_array($abstract)) {
          foreach ($abstract as $key => $val) {
              $this->bind($key, $val);
          }
     }
    elseif ($concrete instanceof Closure) {
          $this->bind[$abstract] = $concrete;
      } elseif (is_object($concrete)) {
          $this->instances[$abstract] = $concrete;
      }
    else {
          $abstract = $this->getAlias($abstract);
         if ($abstract != $concrete) {
              $this->bind[$abstract] = $concrete;
          }
     }
}

输入类名(或类的别名,映射到$instances数组中),$concreate为类的实例

  public function instance(string $abstract, $concrete)
 {
     $abstract = $this->getAlias($abstract);
      $this->instances[$abstract] = $concrete;
     return $this;
  }

下面几个是需要通过php反射的api解析依赖的过程。比如创建A类对象的时候
A类对象在构造函数中需要依赖B类,这个时候就需要容器来解决依赖的问题
先用ReflectionClass系统提供的类活的类的信息,在用getConstructor方法获得构造函数信息。获得构造函数信息后用getNumberOfParameters方法获得构造函数依赖的参数个数,如果依赖参数的个数为0,则可以直接创建。
getParameters获得具体的参数信息。根据参数的类型去容器中寻找对应类型的参数(getType),如果没有的话,则检查函数是否有默认值(getDefaultValue)。如果都没有的话,则报错,对象无法创建

  public function getObjectParam(string $className, array &$vars)
 {
    $array = $vars;
      $value = array_shift($array);

     if ($value instanceof $className) {
         array_shift($vars);
         return $value;
      } else {
          return $this->make($className);
      }
 }
  protected function bindParams(ReflectionFunctionAbstract $reflect, array $vars = []): array
  {
      if ($reflect->getNumberOfParameters() == 0) {
          return [];
      }
     reset($vars);
      $type = key($vars) === 0 ? 1 : 0;
      $params = $reflect->getParameters();
      $args = [];
     foreach ($params as $param) {
          $name = $param->getName();
          $reflectionType = $param->getType();
         if ($reflectionType && $reflectionType->isBuiltin() === false) {
              $args[] = $this->getObjectParam($reflectionType->getName(), $vars);
          } elseif ($type == 1 && !empty($vars)) {
              $args[] = array_shift($vars);
          } elseif ($type == 0 && array_key_exists($name, $vars)) {
              $args[] = $vars[$name];
          } elseif ($param->isDefaultValueAvailable()) {
              $args[] = $param->getDefaultValue();
          } else {
              throw new InvalidArgumentException('method param miss:' . $name);
          }
     }
    return $args;
  }

  public function invokeFunction($function, array $vars = [])
 {
    try {
          $reflect = new ReflectionFunction($function);
      } catch (\ReflectionException $e) {
          throw new ReflectionException("function not exists: {$function}()", $function, $e);
      }
      $args = $this->bindParams($reflect, $vars);
     return $function(...$args);
  }

  public function invokeClass(string $class, array $vars = [])
 {
    try {
          $reflect = new ReflectionClass($class);
      } catch (ReflectionException $e) {
          throw new ReflectionException('class not exists: ' . $class, $class, $e);
      }
      $constructor = $reflect->getConstructor();
      $args = $constructor ? $this->bindParams($constructor, $vars): [];
      $object = $reflect->newInstanceArgs($args);
     return $object;
  }

  public function make(string $abstract, array $vars = [], bool $newInstance = false)
 {
    $abstract = $this->getAlias($abstract);
     if (isset($this->instances[$abstract]) && !$newInstance) {
          return $this->instances[$abstract];
      }
  if ($this->bind[$abstract] && $this->bind[$abstract] instanceof Closure) {
      $object = $this->invokeFunction($this->bind[$abstract], $vars);
  } else {
      $object = $this->invokeClass($abstract, $vars);
  }
  if (!$newInstance) {
      $this->instances[$abstract] = $object;
  }
      return $object;
  }

  public function get($abstract)
 {
    if ($this->bound($abstract)) {
          return $this->make($abstract);
      }
      throw new \Exception('class not exists: ' . $abstract, $abstract);
  }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!