stefna / di
轻量级依赖注入容器
Requires
- php: ^8.2
- psr/container: ^2.0
Requires (Dev)
- bnf/phpstan-psr-container: ^1.0
- phpstan/extension-installer: ^1.2
- phpstan/phpstan: ^1.8
- phpstan/phpstan-phpunit: ^1.1
- phpunit/phpunit: ^10.5
- squizlabs/php_codesniffer: ^3.9
- stefna/codestyle: ^1.12
- tomasvotruba/type-coverage: ^0.0.7
This package is auto-updated.
Last update: 2024-09-03 15:38:41 UTC
README
此包是一个轻量级的、与框架无关的依赖注入容器。
要求
PHP 8.2 或更高版本。
安装
composer require stefna/di
使用
由于我们不进行任何自动自动装配,您将始终需要使用 ContainerBuilder
来创建 Container
,因此您需要定义所有您想要访问的内容。
配置容器
要配置容器,请向容器构建器添加 DefinitionSource
。
<?php use Stefna\DependencyInjection\ContainerBuilder; $builder = new ContainerBuilder(); $builder->addDefinition([ ClockInterface::class => fn () => new Clock(), ]); $container = $builder->build(); $clock = $container->get(ClockInterface::class);
批量配置定义
我们提供了两种可以用于批量定义内容的方法。
首先,您有 FilterDefinition
,它允许您提供任何任意的过滤器,该过滤器将与请求的类进行匹配。如果匹配,它将获得定义。
示例
<?php use Stefna\DependencyInjection\Definition\FilterDefinition; use Stefna\DependencyInjection\Helper\Autowire; // this will auto-wire all class that ends with Controller $definition = new FilterDefinition( fn (string $className) => str_ends_with('Controller'), fn (string $className) => Autowires::cls($className) );
我们还提供了一个 NamespaceFilterDefinition
,它使批量定义特定命名空间中的所有内容变得更加容易。
NamespaceFilterDefinition
仅扩展 FilterDefinition
并使其更容易与命名空间一起使用。
示例
<?php use Stefna\DependencyInjection\Definition\NamespaceFilterDefinition; // this will auto-wire all class that are in App\Controller namespace $controllerDefinitions = NamespaceFilterDefinition::autoWire('App\\Controller\\'); // this will just do a simple new $className() on all classes in App\RequestInput namespace $requestInputDefinitions = NamespaceFilterDefinition::create('App\\RequestInput\\');
如果您打算使用此功能为许多类提供自动装配,我建议扩展 FilterDefintion
并在定义上实现 PriorityAware
,返回 Priority::Low
以轻松覆盖“默认”定义。
与其他容器实现一起使用
<?php use Stefna\DependencyInjection\ContainerBuilder; $builder = new ContainerBuilder(); $builder->addContainer($externalContainer); $container = $builder->build(); $clock = $container->get(ClockInterface::class);
自动装配
我们提供了一些轻量级的自动装配辅助工具。
自动装配助手只会在容器中查找依赖项,它不会尝试自动创建不属于容器的对象。
<?php use Stefna\DependencyInjection\ContainerBuilder; use Stefna\DependencyInjection\Helper\Autowire; interface A {} class Obj implements A { public function __construct(public readonly ClockInterface $clock) {} } $builder = new ContainerBuilder(); $builder->addDefinition([ ClockInterface::class => fn () => new Clock(), Obj::class => Autowire::cls(), A::class => Autowire::cls(Obj::class), ]); $container = $builder->build(); $clock = $container->get(ClockInterface::class);
属性
您可以使用属性增强自动装配。
自动装配助手默认只从容器中获取对象。
我们支持两种属性接口
ResolverAttribute
可以用于从容器中解析复杂值ConfigureAttribute
可以用于在注入到类之前重新配置对象
ResolverAttribute
当您想要从类似配置存储之类的源解析标量值时非常有用。
#[\Attribute(\Attribute::TARGET_PARAMETER)] final class ConfigValue implements ResolverAttribute { public function __construct(private readonly string $key) {} public function resolve(string $type, ContainerInterface $container): mixed { $config = $container->get(Config::class); return $config->get($this->key); } } final class Test { public function __construct( #[ConfigValue('site.config.value')] private readonly string $configValue, ) {} }
ConfigureAttribute
当您想要重新配置将被注入的内容时非常有用,例如设置类的自定义日志通道。
#[\Attribute(\Attribute::TARGET_PARAMETER)] final class LogChannel implements ConfigureAttribute { public function __construct(private readonly string $channel) {} public function configure(object $object, ContainerInterface $container): object { if ($object instanceof LoggerInterface && class_exists(ChannelWrapper::class)) { return new ChannelWrapper($object, $this->channel); } if ($container->has(LoggerManager::class)) { return $container->get(LoggerManager::class)->createLogger($this->channel); } // don't know how to add channel just return the incoming logger return $object; } } final class Test { public function __construct( #[LogChannel('test-channel')] private readonly LoggerInterface $logger, ) {} }
工厂
在定义中的所有内容实际上都是工厂。
但我们提供了一个工厂助手,可以帮助去重工厂实例和延迟实例化工厂。
<?php use Stefna\DependencyInjection\Helper\Factory; class ObjFactory { public function __invoke(ContainerInterface $container) { return new Obj($container->get(ClockInterface::class)); } } class ComplexFactory { public function __invoke(ContainerInterface $container, string $className) { if ($className === A::class) { return new Obj($container->get(ClockInterface::class)); } } } class AutowiredComplexFactory { public function __construct( private ClockInterface $clock, ) {} public function __invoke() { if ($className === B::class) { return new Obj($this->clock); } } } $builder->addDefinition([ ObjFactory::class => fn () => new ObjFactory(), Obj::class => Factory::simple(ObjFactory::class), ObjFactory::class => fn () => new ComplexFactory(), A::class => Factory::full(ComplexFactory::class), B::class => Factory::autoWire(AutowiredComplexFactory::class), ]);
自动装配工厂
有两种方法可以自动装配工厂
选项 1:将其添加到容器中,就像其他任何内容一样
use Stefna\DependencyInjection\Helper\Autowire; use Stefna\DependencyInjection\Helper\Factory; $builder->addDefinition([ ComplexFactory::class => Autowire::cls(), // or ComplexFactory::class => fn () => new ComplexFactory(new Clock()), // looks up ComplexFactory from container and if it's not defined crashes A::class => Factory::full(ComplexFactory::class), ]);
选项 2:使用工厂自动装配助手
use Stefna\DependencyInjection\Helper\Autowire; use Stefna\DependencyInjection\Helper\Factory; $builder->addDefinition([ A::class => Factory::autoWire(ComplexFactory::class), ]);
这只是对选项 1 的一个简单包装
贡献
我们始终欢迎收到错误/安全报告和错误/安全修复。
许可
MIT 许可证 (MIT)。请参阅 许可证文件 了解更多信息。