jitesoft/container

PSR-11 容器,具有构造器注入。

5.1.0 2022-08-15 15:36 UTC

README

pipeline status coverage report Back project Maintainability

容器

IoC 容器,具有 构造器依赖注入

绑定

容器将一个键绑定到一个值,该值可以是任何类型或类名。

如果它是一个对象、原始类型或任何类型的实例,它将实例存储为静态对象,即在每次 get 调用时返回相同的对象。而如果传递了一个类名,容器将尝试在每次 get 调用时创建该类的实例。如果无法创建实例,则抛出 NotFoundException 或 ContainerException。

绑定可以通过在构造函数中传递关联数组或使用 set 方法来完成。要重新绑定,可以使用接受键和值的 rebind 方法。

容器实现了 PSR-11 容器接口。此外,容器实现了 ArrayAccess 接口,可以通过数组索引语法进行获取,例如:$container[Interface::class]

使用方法

容器实现了以下接口

interface ContainerInterface {
  public function get($abstract): mixed;
  public function has($abstract): bool;
}

interface ArrayAccess {
  public function offsetExists($offset);
  public function offsetGet($offset);
  public function offsetSet($offset, $value);
  public function offsetUnset($offset);
}

ArrayAccess 接口允许通过数组索引语法获取、设置和取消设置绑定

$c['Abstract'] = 'Concrete';
echo $c['Abstract']; // 'Concrete'
unset($c['Abstract'];

此外,该类还实现了以下公共方法

class Container {
  public function __construct(?array $bindings);
  public function clear();
  public function set(string $abstract, mixed $concrete, ?bool $singleton = false): bool;
  public function rebind(string $abstract, mixed $concrete, ?bool $singleton = false): void;
  public function unset(string $abstract): void;
  
  public function singleton(string $abstract, mixed $concrete): void;
}

类的构造函数接受一个可选的绑定数组。期望的数组应该是一个关联数组,其中包含抽象类作为键,具体实现作为值。如果需要,具体实现可以是另一个包含 classfunc 键的关联数组,这些键包含要解析到的/与的类或可调用的函数,以及一个包含布尔值的 singleton 键。
如果 singleton 键为 true,容器将始终创建解析值的单个实例,或者只运行解析函数一次。

示例

$container = new Container([

  // With objects:
  InterfaceA::class => $objectInheritingInterfaceA,
  InterfaceB::class => [
    'class' => $objectInheritingInterfaceB,
    'singleton' => true
  ],

  // With classes:
  InterfaceA::class => ClassA::class,
  InterfaceB::class => [
    'class' => ClassB::class,
    'singleton' => true
  ],

  // Or with functions:
  InterfaceA::class => static fn (InterfaceB $ib) => new ClassA($ib),
  InterfaceB::class => [
    'class' => static function(InterfaceC $c) {
      return new ClassB($c);
    },
    'singleton' => true
  ],

]);

$container->get(InterfaceA::class); // Will be a new object of the ClassA class.
$container->get(InterfaceB::class); // On first call, it will be resolved to a ClassB class.
$container->get(InterfaceB::class); // On all other calls, the object will be the same as the first call.

除了数组类型单例绑定之外,接口还可以使用 singleton 方法创建单例绑定。

可以使用 $container->rebind($a, $c, $singleton); 方法在运行时重新绑定。
这将取消之前的绑定并创建一个新的绑定。

要删除所有当前绑定,可以使用 $container->clear(); 方法,这将清空内部条目列表。请注意,这不会清除存储在您的类中的当前解析对象实例,而是从容器中删除所有条目。

类中实现的除 hasclear 方法之外的所有方法在出错时都将抛出异常。
以下两个异常被使用

Jitesoft\Exceptions\Psr\Container\ContainerException implements ContainerExceptionInterface;
Jitesoft\Exceptions\Psr\Container\NotFoundException  implements NotFoundExceptionInterface;

因此,在检查异常时,可以使用底层的 JitesoftException 类、特定类或接口。
但是请注意,NotFoundException 继承自 ContainerException,因此,在两者都可以返回且您想要捕获特定异常的情况下,请先捕获 NotFoundException,然后捕获 ContainerException

依赖注入

当容器能够做到时,它将在解析对象时将依赖项注入到构造函数中。
但在它能够这样做之前有一些要求

  1. 参数需要类型提示。
  2. 参数需要在容器中可解析,或者可以在没有构造函数的情况下创建。

如果容器无法解析参数,它将抛出异常,但根据上述两个要求,这种情况不应该发生。

class ClassA {}

class ClassB {
  __constructor(ClassA $b) { }
}

$container->set(ClassB::class, ClassB::class);
$container->get(ClassB::class); // Will throw a ContainerException due to class A not being bound.

$container->set(ClassA::class, ClassA::class);
$container->get(ClassB::class); // Will not throw any exception. ClassA is resolved and pushed into the constructor of ClassB.

不仅仅是类

容器不仅解析绑定,还可以用来存储其他值。
如果传递的具体不是类名,它将使用它作为单个值,而不是解析。因此,传递字符串或数字作为具体值将使 get 方法返回该值。

$container->set('Something', 123);
$container->get('Something'); // 123

许可证

MIT