avris/container

具有自动装配功能的依赖注入容器

v1.0.0 2018-01-09 19:17 UTC

This package is not auto-updated.

Last update: 2024-09-20 02:59:22 UTC


README

具有自动装配功能的依赖注入容器

安装

composer require avris/container

使用

基础

容器解决定义服务之间的依赖关系,以简化开发过程,避免代码重复,促进互操作性,并提高可维护性和可测试性。见:[依赖注入模式](https://en.wikipedia.org/wiki/Dependency_injection)。

$parameterProvider = new SimpleParameterProvider(['ROOT_DIR' => __DIR__]);
// parameter provider is optional
  
$container = new Container($parameterProvider);
 
$container->set('number', 4);
$container->set(Foo::class, new Foo);
$container->setDefinition(Bar::class, [
    'arguments' => [
        '$foo' => '@' . Foo::class,
        '$dir' => '%ROOT_DIR%/bar',
        '$number' => '@number',
        '$float' => 69.123,
    ],
    'public' => true,
]);
$container->setDefinition(BarInterface::class, Bar::class); // alias

$container->get('number');              // 4
$container->get(Foo::class);            // new Foo
$container->get(BarInterface::class);   // new Bar(new Foo, __DIR__ . '/bar', 4, 69.123)
$container->getParameter('ROOT_DIR');   // __DIR__

选项

  • class -- 服务的类,如果没有提供,将默认为服务名称。如果给定的类实现了 Resolver 接口,它将被实例化,并且执行其 resolve 方法以提供要放入容器中的实际值。
  • arguments -- 构造函数参数。
  • calls -- 在构建服务后立即执行的方法调用(setter 注入等)

     'calls' => [
         ['setLogger', ['@logger']],
         ['registerListener', ['@listenerA']],
         ['registerListener', ['@listenerB']],
     ],
    
  • tags -- 一个字符串数组,有助于将类似的服务分组在一起;带标签的服务可以用 #tagName 注入

     $container->setDefinition(HandlerA::class, ['tags' => 'handler']);
     $container->setDefinition(HandlerB::class, ['tags' => 'handler']);
     $container->setDefinition(HandlerC::class, ['tags' => 'handler']);
     $container->setDefinition(Manager::class, ['arguments' => ['$handlers' => '#handler']]);
    
  • factory -- 确定每次 get 是否创建新的服务(true),或者是否应重用单个服务(false,默认)。
  • resolve -- 不使用 class + arguments 构建服务,您可以使用 resolve 定义其创建方式

     $container->setDefinition('foo', ['resolve' => 4]); // 4
     $container->setDefinition('language', ['resolve' => '@Request.locale.language']); // $container->get('Request')->getLocale()->getLanguage()
    
  • public -- 确定服务是否可以通过 get 直接访问,或者它是否只能注入到其他服务中。

ContainerCompiler:自动装配和自动配置

通常很明显,哪个服务应该注入到另一个服务中。例如,当您的服务有一个构造函数参数 Psr\Cache\CacheItemPoolInterface $cache,并且容器中确实有一个名为 Psr\Cache\CacheItemPoolInterface 的服务时,则明确写出 ['arguments' => ['$cache' => '@Psr\Cache\CacheItemPoolInterface'] 是多余的。您始终可以手动指定依赖项,然后自动装配不会覆盖它们。

自动装配不是魔法 -- 它只是遵循简单的规则来确定,应该将哪个服务注入到构造函数中

  • 如果参数是一个在容器中定义的类,则使用此服务,
  • 如果参数是一个在容器中未定义的类,则尝试自动装配该类并从它创建一个私有服务,
  • 如果参数是数组且其名称以 s 结尾(例如 array $helpers),则注入具有特定标签(#helper)的服务数组。
  • 如果参数是 Bag 类型,则通过其名称注入配置值(例如 Bag $localisation -> @config.localisation),
  • 如果其名称以 env 开头,则注入一个参数:(例如 string $envCacheDir -> %CACHE_DIR%
  • 如果上述任何一项都不成立,但存在默认值,则直接使用该默认值,
  • 如果上述任何一项都不成立,则抛出异常 -- 此参数应明确定义。

自动配置是使您的生活更简单的一种方法。例如,如果您正在使用 Twig,您可能希望所有扩展 Twig\Extension\AbstractExtension 的代码中的类都自动注册为 twig 扩展。自动配置允许您定义应该添加到它们的默认配置(标签、公共等)。

要使用自动装配和自动配置,运行 ContainerCompiler

$container = new Container;

$services = [
    'App\' => [
        'dir' => '%MODULE_DIR%/src/',
        'exclude' => ['#^Entity/#'],
    ],
    
    'App\Foo' => [
        'arguments' => [
            '$bar' => 5,
        ],
    ],
    
    'App\Bar' => [
        'public' => true,
    ],
];

$autoconfiguration = [
    'Twig\Extension\AbstractExtension' => [
        'tags' => ['twigExtension'],
    ],
];

$definitions = new ContainerCompiler(
    $container,
    $services,
    $autoconfiguration
))->compile();
        
/** @var ServiceDefinition $definition */
foreach ($definitions as $name => $definition) {
    if (!$container->has($name)) {
        $container->setDefinition($name, $definition);
    }
}

在此示例中,除了(/src/Entity 目录之外),整个 %MODULE_DIR%/src/ 都将被扫描以查找 PHP 文件,并且所有找到的类都将作为私有服务自动装配。如果其中一些未使用且未公开,则将从容器中删除。

只要您缓存 compile() 的结果,编译容器对生产环境中的性能没有影响。

服务定位器

服务定位器仅允许对所选名称列表中的服务进行访问,限制了对容器中服务的访问。

$container = new Container();
$container->set('foo', 'abc');
$container->set('bar', 'def');
$container->set('secret', 'XYZ');
 
$locator = new ServiceLocator($container, ['foo', 'bar']);
 
$locator->get('foo');     // 'abc'
$locator->get('bar');     // 'def'
$locator->get('secret');  // Exception

ContainerAssistedBuilder

ContainerAssistedBuilder 可用于将多个封装了一组服务定义的 ContainerBuilderExtension 连接在一起,形成一个库。例如,请参阅 Avris Localisator

Micrus

该容器最初是作为 Micrus 框架 的一部分构建的。

版权