vection-framework / dependency-injection
Vection 依赖注入能够快速且有效地注入依赖。
Requires
- php: ^7.2
- psr/container: ^1.0
- psr/log: ~1.0
- vection-framework/contracts: dev-master
README
PHP DI - 依赖注入容器
⚠️ Vection 依赖注入目前处于开发阶段,因此目前仅提供预发布版本。在发布稳定版本之前,可能会进行破坏性更改!
PHP 的依赖注入容器
这个 vection 组件为 PHP 提供了一个强大的依赖注入容器。它支持多种自动注入方式,如注解、属性和接口注入。可选的配置允许映射接口和具体实现。
<?php use Vection\Component\DependencyInjection\Traits\AnnotationInjection; class MyApiController implements LoggerAwareInterface // Auto injection Logger via mapping in container config. { use AnnotationInjection; #[Inject] private IdentityQueryService $identityQueryService; public function __construct(CommandBusInterface $commandBus) { // Auto constructor parameter injection } }
支持的注入类型
-
构造器注入
构造器参数将由容器自动解析并注入。 -
基于属性的注入
使用#[Inject]属性进行类属性的注入。 -
接口注入
映射接口和实现,可以通过构造函数、方法和属性进行注入。 -
接口感知注入
映射接口和实现,通过接口中定义的设置器进行注入。 -
魔术注入
通过使用__inject方法通过属性定义注入依赖。 -
通过类继承注入
子类会自动注入父类中定义的依赖。
安装
Vection 组件仅支持通过 composer 安装。因此,请首先确保您的 composer 已经安装、配置并准备好使用。
$ composer require vection-framework/dependency-injection
如何使用
在解释设置之前,我们先看看如何使用 DI 容器。
构造器注入
此 DI 容器支持自动注入构造函数属性,只要属性是具有类型提示的对象,即不是原始类型!以下示例将注入类型为 FooBar 的对象。
public function __construct(FooBar $fooBar)
注解/属性注入
注解注入通过在属性上使用 #[Inject] 属性提供了将依赖注入到受保护对象属性中的强大功能。使用注解注入需要使用 AnnotationInjection 特性。注解还需要使用完全限定类名(< PHP 7.4)
<?php class Awesome { use AnnotationInjection; #[Inject] protected FooBar $fooBar; }
重要说明
注入的属性不能从构造函数中访问。如果您需要从构造函数中访问注入的属性,您可以在类中使用替代构造方法__init()。此方法将在对象及其依赖项创建后立即调用。
<?php class Awesome { use AnnotationInjection; #[Inject] protected FooBar $fooBar; public function __construct() { // Cannot access $this->fooBar yet } // Alternative construction method, called immediately after the object is created with its dependencies public function __init(): void { // Here you can access $this->fooBar; } }
接口注入
接口注入是解耦具体实现与其接口的绝佳方式。它通过使用接口而不是具体实现来提供注入。
<?php class Awesome { use AnnotationInjection; public function __construct(FooBarInterface $fooBar) {...} }
此类型的注入需要在配置文件中添加以下条目。(有关配置文件的详细文档,您可以在“设置 -> 配置”部分中阅读)
resolve(FooBarInterface::class) ->viaFactory(fn(Container $container) => $container->get(FooBar::class)) ,
接口感知注入
此类型的注入通过其感知接口提供依赖注入。此注入需要配置文件中的映射接口与具体实现的条目。
<?php class Awesome implements LoggerAwareInterface { public function setLogger(LoggerInterface $logger) {...} }
此类型的注入需要在配置文件中添加以下条目。
# First map the interface to the implementation resolve(LoggerInterface::class) ->viaFactory(fn(Container $container) => new Logger()) , # Now we can map the aware interface with the LoggerInterface by using the inject() method resolve(LoggerAwareInterface::class) ->viaSetter('setLogger', LoggerInterface::class) ,
第二个参数(Logger)定义了设置器的名称。
显式注入
此类型的注入提供了一种替代方法,如果所有其他注入类型的构造函数都不适合任何用例。
<?php class Awesome { public function __inject(FooBar $fooBar) { $this->fooBar = $fooBar; } }
通过父类继承注入
此功能允许子类自动通过父类获取依赖注入,如果父类使用任何注入类型。
<?php class AwesomeParent { use AnnotationInjection; #[Inject] protected FooService $service; public function __construct(FooBarInterface $fooBar) {...} } class Awesome extends AwesomeParent { public function getFooBar() { return $this->fooBar; } }
设置
容器的设置简单,只需几行代码。根据您使用的情况和注入类型,您需要定义容器配置的选项。
创建容器
以下代码片段展示了创建工作容器的最快方法。
<?php $container = new Container(); // Create now your class where to start dependency injection $myApplication = $container->get(MyApplication::class);
如果类有一些需要手动传递的参数,您可以使用容器上的create方法。第二个参数是一个数组,它将被传递到类的构造函数中,第三个参数可以用来设置此对象是否为共享对象。共享对象将被容器重用,非共享对象每次请求时都会创建。
$myApplication = $container->create(MyApplication::class, [$optional, $params], $optionalSharedOrNot);
配置
快速设置方法并不适合最佳使用方式,让我们看看一些可选的设置步骤。使用接口注入或一些特殊对象创建需要配置。这可以通过配置文件(例如 container.php)来完成。PHP配置的优点是自动完成和函数使用。配置文件只返回一个包含一些定义的数组。
<?php use function Vection\Component\DependencyInjection\resolve; return [ // configuration here ];
要加载此配置文件,您需要将其添加到容器中。在模块化开发的情况下,您也可以添加多个配置文件。
$container = new Container(); $container->load('path/to/config/container.php'); $container->load('path/other/*/config/container.php');
配置使用DI定义的set函数。此方法以类作为注入主题,并返回一个定义对象,您可以在其中定义注入。通常,它看起来像以下代码片段
// Not recommended writing type $instruction = resolve(My\Awesome\MegaClass::class); $instruction->viaFactory(....);
正确的方式应该像这样
<?php use function Vection\Component\DependencyInjection\resolve; return [ resolve(My\Awesome\MegaClass::class) ->viaFactory(fn(Container $container) => new My\Awesome\MegaClass('example')) , ];
(更多配置文档即将推出…)
第三方库注入
DI容器创建一个内部依赖树。这可能导致无法解析第三方库类(例如,在未知库类的原始构造参数的情况下)并退出错误。为了避免这种情况,建议将DI容器限制在应用程序的命名空间中。
$container->setAllowedNamespacePrefixes(['MyApplication', 'optionalOtherNamespaces']);
但如果你想要通过注入使用第三方库,你确实可以。在这种情况下,你应该手动创建第三方库对象,并将其添加到容器中,现在你可以使用这个类进行注入。
$thirdPartyObject = new ThirdPartyObject(5, 'primitive'); $container->add($thirdPartyClass);
这将通过其类名注册对象,并提供它作为可注入对象。
缓存
建议使用缓存以避免大型应用程序的性能问题。DI组件使用Vection Contracts的Cache contracts。因此,您可以通过实现Vection\Contracts\Cache中的CacheInterface或使用现有的Vection Cache提供程序(Redis,Memcache,APCu)来实现自己的缓存实现。您还可以使用Symfony Bridge(Vection\Bridge\Symfony\Cache\SymfonyCacheProviderBridge)。
$cacheProvider = new Vection\Component\Cache\Provider\RedisCacheProvider(...); $cache = new Vection\Component\Cache\Cache($cacheProvider); $container = new Container(); $container->setCache($cache);
支持
通过Ko-fi支持Vection