noem/container

模块化服务容器

dev-main 2022-04-04 18:44 UTC

This package is auto-updated.

Last update: 2024-09-04 23:52:11 UTC


README

一个现代化的自动连接服务容器,利用PHP8属性来标记和关联服务及其依赖关系。

它具有以下特性

  • 使用服务提供者模式从多个模块聚合服务
  • 全面支持利用 php-di/invoker 的自动连接策略
  • 通过自动注入轻量级代理对象来解决循环依赖

安装

通过composer安装此包

composer require noem/container

使用

容器通过吸收一个或多个服务提供者的服务工厂函数来工作。一个 Provider 看起来是这样的

interface Provider
{

    /**
     * Returns a list of all container entries registered by this service provider.
     *
     * - the key is the entry name
     * - the value is a callable that will return the entry, aka the **factory**
     *
     * Factories have the following signature:
     *        callable( mixed... ):mixed
     * Factories can declare any number & type of parameters and can expect them to be resolved by the Container
     *
     * @psalm-return array<string,callable(mixed...):mixed>
     * @return callable[]
     */
    public function getFactories(): array;

    /**
     * Returns a list of all container entries extended by this service provider.
     *
     * - the key is the entry name
     * - the value is a callable that will return the modified entry
     *
     * Callables have the following signature:
     *        function( mixed $previous, mixed ...$params )
     * The $previous parameter MUST be the first one. Additional parameters will be resolved by the Container.

     * @psalm-return array<string,callable(mixed, mixed...):mixed>
     * @return callable[]
     */
    public function getExtensions(): array;
}

属性

PHP 8引入了一个名为属性的新功能,它允许向类、函数、方法和参数添加任意元数据。这为我们编写服务定义提供了很大的灵活性。

在服务编译期间,容器将解析所有函数属性并将它们通过实现 AttributeAwareContainer 使其可用于手动和自动解决依赖关系。

interface AttributeAwareContainer extends ContainerInterface
{
    public function getIdsWithAttribute(string $attribute, ?callable $matching = null): array;

    public function getAttributesOfId(string $id, ?string $attributeFQCN = null): array;
}

服务级别属性

别名

示例:#[Alias( 'my-other-service-id' )]

用于在多个ID下宣传您的服务而不重复定义。一个用例是在消费者中鼓励接口分离,同时为他们提供一个实现多个接口的类。

class MyContainer implements ContainerInterface, WritableContainerInterface, FlushableContainerinterface {
   // ...
}

服务可以定义如下

MyContainer::class => 
    #[Alias(ContainerInterface::class)]
    #[Alias(WritableContainerInterface::class)]
    #[Alias(FlushableContainerinterface::class)]
    fn() => new MyContainer()

容器现在能够将任何接口FQCN解析为MyContainer的实例。

// These all return the same instance:
$container->get(MyContainer::class);
$container->get(ContainerInterface::class);
$container->get(WritableContainerInterface::class);
$container->get(FlushableContainerinterface::class);

标签

示例:Tag( 'event-listener' )

您可以使用此属性作为低耦合方式实现可扩展的“事物列表”。这种属性的一个自然应用是将事件订阅者连接到PSR-14 ListenerProvider。

$services = [
    'listener.all' =>
        #[Tag('event-listener')]
        fn( LoggerInterface $logger ) => function( object $event ) use ($logger){
            $logger->info( 'Event triggered: ' . print_r( $event, true ) );
        },
];

Tag属性还支持指定一个可选的priority,它在将服务传递给消费者之前用于对服务进行排序。默认优先级是50

示例:Tag( 'event-listener', 10 )

参数级别属性

Id

示例:#[Id( 'service-id' )]

可以用于工厂/扩展函数的参数。它指示容器通过获取指定的条目来解决参数。它优先于其他参数解决方法。

$services = [
    'my-string' => fn() => 'hello',
    'greeting' =>
        fn(#[Id('my-string')] string $string) => "{$string} world",
];

$container = new Container(new ServiceProvider($services));
$greeting = $container->get('greeting'); // 'hello world'

WithAttr

示例:#[WithAttr( MyCustomAttr::class )]

解析为所有已用指定属性注释的服务。您可以选择传递一个键/值对的映射,它将用于过滤结果。

示例:#[WithAttr( MyCustomAttr::class, [ 'name' => 'foo' ] )]

这将仅返回那些已用#[MyCustomAttr('foo')]注释的服务(当然,前提是foo映射到属性的name属性)。

TaggedWith

示例:#[TaggedWith( 'foo' )]

这是WithAttr( Tag::class, [ 'name' => $MY_TAG ] )的简写,用于获取所有指定标签的服务。

$services = [
    ListenerProviderInterface:: =>
        fn(#[TaggedWith('event-listener')] callable ...$listeners) => new ListenerProvider(...$listeners),
];