avris/container 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

avris/container

Composer 安装命令:

composer require avris/container

包简介

A dependency injection container with autowiring

README 文档

README

A dependency injection container with autowiring

Installation

composer require avris/container

Usage

Basics

Container resolves dependencies between defined services, in order to simplify the development process, avoid duplication of code, facilitate interoperability and improve maintainability and testability. See: Dependency Injection pattern.

$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__

Options

  • class -- the class of the service, will default to the service name if not given. If the given class implements the Resolver interface, it will be instantiated, and it's resolve method executed to provide an actual value to be put in the container.
  • arguments -- constructor arguments.
  • calls -- method calls to be executed right after constructing the service (setter injection etc.)

     'calls' => [
         ['setLogger', ['@logger']],
         ['registerListener', ['@listenerA']],
         ['registerListener', ['@listenerB']],
     ],
    
  • tags -- an array of string that help group similar services together; tagged services can be injected with #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 -- determines if each get should create a new service (true), or should one service be reused (false, default).
  • resolve -- instead of using class + arguments to construct the service, you can use resolve to define how it should be created:

     $container->setDefinition('foo', ['resolve' => 4]); // 4
     $container->setDefinition('language', ['resolve' => '@Request.locale.language']); // $container->get('Request')->getLocale()->getLanguage()
    
  • public -- determines if the service should be accessible directly with get, or can it only be injected into other services.

ContainerCompiler: autowiring and autoconfiguration

Usually it's obvious, which service should be injected into another. For instance when your service has a constructor argument Psr\Cache\CacheItemPoolInterface $cache, and the container does have a service named Psr\Cache\CacheItemPoolInterface, then explicitly writing ['arguments' => ['$cache' => '@Psr\Cache\CacheItemPoolInterface'] is redundant. You can always specify the dependencies manually, then autowiring won't overwrite them.

Autowiring is not magic -- it just follows simple rules to determine, which service should be injected into the constructor:

  • if the argument is a class which is defined in the container, use this service,
  • if the argument is a class which is not defined in the container, try to autowire that class and create a private service out of it,
  • if the argument is an array and its name ends with s (e.g. array $helpers), inject an array of services with a specific tag (#helper).
  • if the argument is of type Bag, inject the config value with its name (e.g. Bag $localisation -> @config.localisation),
  • if its name starts with env, inject a parameter: (e.g. string $envCacheDir -> %CACHE_DIR%)
  • if none of the above is true, but there is a default value, just use it,
  • if none of the above is true, throw an exception -- this argument should be defined explicitly.

Autoconfiguration is another way to make your life simpler. For instance, if you're using Twig, you might want all the classes in your code that extend Twig\Extension\AbstractExtension to be automatically registered as twig extension. Autoconfiguration lets you define what default config (tags, public etc.) should be added to them.

To use autowiring and autoconfiguration, run the 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);
    }
}

In this example, the whole %MODULE_DIR%/src/ except for (the /src/Entity dir) will be scanned for PHP files and all the found classes will be autowired as private services. If some of them are not used and not public, they will be removed from the container.

Compiling the container has no impact on performance on production environment, as long as you cache the result of compile().

ServiceLocator

Service locator restricts access to services in the container only to a selected list of names:

$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 can be used to join together a couple of ContainerBuilderExtensions which encapsulate a set of service definitions that form a library together. For an example, see Avris Localisator.

Micrus

This container was originally built as a part of the Micrus framework.

Copyright

avris/container 适用场景与选型建议

avris/container 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 353 次下载、GitHub Stars 达 0, 最近一次更新时间为 2018 年 01 月 09 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「dependency injection」 「container」 「di」 「Autowiring」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 avris/container 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 avris/container 我们能提供哪些服务?
定制开发 / 二次开发

基于 avris/container 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 353
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 3
  • 依赖项目数: 5
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2018-01-09