noem / container
模块化服务容器
Requires
- php: ^8.0
- noem/tiny-proxy: dev-main
- php-di/invoker: ^2.3
- psr/container: ^1.0 || ^2.0
Requires (Dev)
- mockery/mockery: ^1.4
- noem/composer-file-embed: dev-master
- phpunit/phpunit: ^9.5
- squizlabs/php_codesniffer: ^3.6
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), ];