splot/di

Splot 框架的依赖注入容器。

0.9.4 2015-06-06 21:43 UTC

This package is auto-updated.

Last update: 2024-09-06 03:33:49 UTC


README

Splot 框架的依赖注入容器。

Build Status SensioLabsInsight HHVM Status

Splot 依赖注入容器是一个独立的 PHP 库,有助于实现以下控制反转和依赖注入模式

它是流行的Symfony 依赖注入组件PHP-DI或其他实现的一个可行替代方案。

功能

  • 支持构造函数(arguments选项)和setter注入(call选项)。
  • 包含一个“参数容器”,参数可以用于各种选项、作为依赖项或在彼此之间包含。
  • 服务可以通知(调用)其他服务(例如,为集合添加处理程序)。
  • 支持工厂定义,其中通过调用其他服务的某个方法来创建服务。
  • 定义可以相互扩展以避免重复常见的公式(extends选项)。
  • 允许可选参数。
  • 从 YML 文件中读取定义。
  • 支持私有、只读和别名服务以及单例服务(默认)和非单例服务(选项)。
  • PHP 容器互操作性项目兼容。
  • 可以将容器状态缓存起来,以防止每次请求都重建它。

TL;DR 文档

通过 Composer 获取

$ composer require splot/di dev-master

在 PHP 中使用

$container = new \Splot\DependencyInjection\Container();

// register services in PHP
$container->setParameter('debug', true);
$container->register('logger', '\Psr\Log\NullLogger');
$container->register('my_service', array(
    'class' => 'MyClass',
    'arguments' => array('MyService', '1.0', '%debug%'),
    'call' => array(
        array('setLogger', array('@logger'))
    )
));

// load parameters and service definitions from a YML file
$container->loadFromFile(__DIR__ .'/services.yml');

// retrieve a service
$myService = $container->get('my_service');

// retrieve a parameter
$debug = $container->getParameter('debug');

您可以在 coverall 测试文件中查看选项的完整列表和大量示例:tests/fixtures/coverall.yml

安装

您可以使用Composer安装 Splot DI。

$ composer require splot/di dev-master

开始使用它只需实例化

$container = new \Splot\DependencyInjection\Container();

使用容器

注册服务

  • $container->register('my_service_name', 'MyClass') - 如果您的类没有其他依赖项,这是最简单的方法
  • $container->register('my_service_name', array('class' => 'MyClass', ...)) - 如果您想添加一些选项(如下文所述),这是更高级的方法

从 YML 加载

您还可以从 YML 文件中加载服务定义和参数。强烈建议这样做,因为它更干净,也更易于阅读。

$container->loadFromFile(__DIR__ .'/services.yml');

您可以在以下位置查看示例 YML 文件:tests/fixtures/coverall.yml

加载的 YML 文件必须分为两个部分:parametersservices(您可以省略任何一个),例如

parameters:
    my_parameter: true

services:
    my_service:
        class: MyApp\MyService

检索服务

要从容器中检索服务,只需调用

$myService = $container->get('my_service_name');

这将实例化服务,并解决和注入任何依赖项。

使用参数

您可以使用容器参数用于参数化常见参数或甚至类名或其他选项。

要设置参数,请调用

$container->setParameter('debug', true);

要获取参数,请调用

$debug = $container->getParameter('debug');

参数也可以相互链接

$container->setParameter('dev_mode', true);
$container->setParameter('debug', '%dev_mode%');

它们也可以作为字符串连接

$container->setParameter('version', 1);
$container->setParameter('app_name', 'MyApp ver. %version%');

在引用参数时,应在参数名称周围使用%符号 - 前后各一个(您也可以通过使用两个%%来转义%%符号)。

引用其他服务和参数

您可以将其他服务和参数作为依赖项注入到服务中。

在引用服务时,无论是在 arguments 选项还是 call 选项中,您需要在名称前添加一个 @ 符号,例如 @my_service

在引用参数时,无论是在 arguments 选项还是 call 选项,或者任何其他选项中,您都需要用 % 符号包围它 - 一个在前面,一个在后面,例如 %my_parameter.name%

选项

在注册服务时,以下选项在 PHP 和 YML 文件中都是可用的。

当注册已经存在的对象作为服务或使用闭包作为服务配方时,其中一些选项已经过时。

所有示例都使用 YML 表示法,但可以一一对应地转换为 PHP 选项数组。

class

服务的类名。应该是一个完全命名空间化的类,例如。

my_service:
    class: MyLib\MyService

如果之前已经定义,也可以是一个参数

my_service:
    class: %my_service.class%

这是唯一必需的选项,除了抽象服务或工厂服务。

arguments

服务构造函数参数列表。可以引用其他服务(使用 @ 表示法)或参数(使用 % 表示法)

my_service:
    class: %my_service.class%
    arguments:
        - MyServiceTitle
        - @logger
        - %debug%

当引用其他服务时,它们可以是可选的 - 如果您在引用末尾添加 ? 符号,那么如果未找到此类服务,则将注入 null

my_service:
    class: %my_service.class%
    arguments:
        - @logger?

在上面的示例中,如果 logger 服务不存在,则将 null 放在其位置。

call

应调用服务上方法列表,在服务实例化后立即调用。每个调用条目都是一个数组,其中索引 0 是方法名,索引 1 是方法参数的数组。

您也可以在此处引用参数或其他服务作为方法参数,例如。

my_service:
    class: %my_service.class%
    call:
        - [setName, ["My Service Title"]]
        - [setVersion, ["1.0", "stable"]]
        - [setLogger, "@logger"]
        - [setDebug, "%debug%"]

如果方法只提供了一个参数,则索引 1 或调用数组可以是该参数,而不是数组(如上面示例中的最后两行)。

factory_service, factory_method, factory_arguments

如果您的服务需要是工厂的产物,则可以指定调用哪个服务上的哪个方法以及使用什么参数来获取该服务。

my_factory:
    class: MyApp\MyServicesFactory

my_service:
    factory_service: my_factory
    factory_method: provideService
    factory_arguments:
        - @logger?
        - %debug%

当获取 my_service 时,将调用 my_factory 服务的 ::provideService() 方法,并使用指定的参数,该方法调用的结果将作为服务返回。它也将作为服务实例缓存,除非将 singleton 选项设置为 false,在这种情况下,每次对 my_service 的请求都将导致上述方法调用。

factory

factory_servicefactory_methodfactory_arguments 选项的简写选项。

一个数组,其中索引 0 = factory_service,1 = factory_method 和 2 = factory_arguments(可选)。

my_service:
    factory: ['@my_factory', 'provideService', [@logger?, %debug%]]

notify

Splot DI 的独特功能是服务能够通知其他服务。这是 Symfony DI 组件中标签和编写编译器传递的替代方案。

notify 实质上是另一个服务的调用,该服务可能存在或不存在。

my_services_collection:
    class MyApp\MyServicesCollection

my_service.one:
    class: MyApp\MyService
    arguments: ['one']
    notify:
        - [my_services_collection, addService, ["service_one", "@"]]

my_services.two:
    class: MyApp\MyService
    arguments: ['two']
    notify:
        - [my_services_collection, addService, ["service_two", "@"]]

根据上述定义,检索服务 my_services_collection 也将导致服务 my_service.onemy_service.two 被实例化,并通过在 my_services_collection 上调用 ::addService() 方法并将为每个注入的服务指定的参数注入到 my_services_collection 中来实现。在这种情况下,特殊字符 @ 指的是服务本身,是 @my_service.one@my_service.two 的简写。

您可以使用 @ 符号在通知参数中引用服务本身,或使用 @= 来引用服务名称。

然而,检索服务 my_service.one 本身不会导致 my_services_collection 被实例化。但稍后,如果 my_services_collection 被实例化,则 my_service.one 将适当地注入到其中。

发送者服务(例如,上面的示例中的 my_services.two)只有在您通过 @ 在通知参数中引用它时才会被实例化。否则,它将遵循正常的实例化策略。

一个服务可以通知无限数量的其他服务。它还可以尝试通知不存在的服务 - 在这种情况下,不会抛出异常也不会引发任何错误。

请参阅下方的“常见用法”部分以了解此功能的用例。

抽象

如果要将此服务标记为不可实例化,请将其设置为true。当您想创建一个将被其他服务重用的通用服务配方时,这很有用。

抽象服务不需要指定一个类。

扩展

指定服务的“父”定义。此服务将从它扩展的服务继承几乎所有选项(那些有意义的选项,例如,abstract选项不继承)并也可以覆盖它们中的任何一个。

my_service:
    class: MyApp\MyService
    arguments: ["my_app", "1.0", %debug%, @logger?]
    call:
        - [setSomething, true]
        - [incrementCounter]

your_service:
    class: YourApp\YourService
    extends: my_service

your_service服务将被实例化为YourApp\YourService类的对象,但与my_service具有相同的构造函数参数和方法调用。

单例

在Splot DI中,所有服务默认为单例。如果希望容器始终返回一个新实例,请将singleton选项设置为false

my_service:
    class: %my_service.class%
    singleton: false

别名

在注册服务时,您还可以注册一些别名。当检索服务时,您将能够使用它们。

my_service:
    class: %my_service.class%
    aliases:
        - my_service.alias
        - my_fake_service
        - your_service

别名

您可以将服务注册为另一个服务的别名。它将简单地使容器也可以通过其新名称引用该服务。

my_service:
    class: %my_service.class%

my_service.alias:
    alias: my_service

私有

如果您不想直接从容器中检索服务,请将其标记为private。您将无法使用$container->get()方法获取它,但您可以将其用作其他服务的依赖项。

my_service:
    class: %my_service.class%
    private: true

只读

如果您想确保您的服务不会被另一个服务覆盖,请将其标记为只读。

my_service:
    class: %my_service.class%
    read_only: true

技巧

紧凑定义

如果您的服务仅是未获取任何构造函数参数的类的实例,并且您不需要设置任何其他选项,那么这

my_service:
    class: MyApp\MyService

可以简化为这样

my_service: MyApp\MyService

紧凑工厂定义

如果您的服务是工厂的产物,并且您不需要设置任何其他选项,那么这

my_service:
    factory: ['@my_factory', 'provideService', [@logger?, %debug%]]

可以简化为这样

my_service: ['@my_factory', 'provideService', [@logger?, %debug%]]

常见配方

以下是一些常见的Splot DI重用或理解配方。

使用“通知”选项添加日志处理器

使用notify选项可以提供很多功能和灵活性。

它的最佳用例是您有一个具有简单API的伪装服务,该API接受动态数量的其他类来以不同的方式处理输入。例如,一个具有许多日志处理器的简单日志记录器(如Monolog

logger:
    class: Monolog\Logger

logger.handler.stream:
    class: Monolog\Handler\StreamHandler
    arguments:
        - "path/to/your.log"
        - 300
    notify:
        - ["logger", "pushHandler", ["@"]]

logger.handler.slack:
    class: Monolog\Handler\SlackHandler
    arguments: ["%slack.api.token%", "%slack.log_channel%"]
    notify:
        - ["logger", "pushHandler", ["@"]]

现在,当您检索logger服务时,它将自动具有StreamHandlerSlackHandler注入。而且,您的应用程序的其他部分(例如其他模块或“包”)添加自己的处理器也没有什么限制。

贡献

问题和拉取请求非常受欢迎!在创建拉取请求时,请包括您更改的全量测试覆盖率。