vection-framework/dependency-injection

Vection 依赖注入能够快速且有效地注入依赖。

v0.3.4 2020-11-09 11:01 UTC

This package is auto-updated.

Last update: 2024-09-16 15:53:33 UTC


README

release QA PHPStan

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

Ko-fi