Компонент DependencyInjection (внедрение зависимости)

Компонент DependencyInjection реализует сервис-контейнер, совместимый с PSR-11, который позволяет вам стандартизировать и централизовать то, как строятся объекты в вашем приложении.

Для вступления во Внедрение зависимостей и сервис-контейнерв, см. Service Container.

Установка

Вы можете установить компонент двумя способами:

Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application won't be able to find the classes of this Symfony component.

Базовое применение

У вас может быть простой класс вроде следующего Mailer, который вы хотите сделать доступным в качестве сервиса:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Mailer
{
    private $transport;

    public function __construct()
    {
        $this->transport = 'sendmail';
    }

    // ...
}

Вы можете зарегистрировать это в контейнере, как сервис:

1
2
3
4
use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();
$container->register('mailer', 'Mailer');

Улучшением класса для повышения его гибкости будет позволить контейнеру устанавливать использованный transport. Если вы измените класс так, чтобы это передавалсь в конструктор:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Mailer
{
    private $transport;

    public function __construct($transport)
    {
        $this->transport = $transport;
    }

    // ...
}

То вы сможете устанавливать выбор транспорта в контейнере:

1
2
3
4
5
6
use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();
$container
    ->register('mailer', 'Mailer')
    ->addArgument('sendmail');

Этот класс теперь намного более гибкий, так как вы отделили выбор транспорта от реализации, и поместили его в контейнер.

То, какой почтовый транспорт вы выбрали, может быть чем-то, о чём стоит знать другим сервисам. Вы можете избежать его изменения в нескольких местах, сделав его параметром в контейнере, а потом ссылаясь на этот параметр в качестве аргумента конструктора сервиса:

1
2
3
4
5
6
7
use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();
$container->setParameter('mailer.transport', 'sendmail');
$container
    ->register('mailer', 'Mailer')
    ->addArgument('%mailer.transport%');

Теперь, когда сервис mailer находится в контейнере, вы можете внедрить его в качестве зависимости других классов. Если у вас есть класс NewsletterManager, то делается это так:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class NewsletterManager
{
    private $mailer;

    public function __construct(\Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    // ...
}

При определении сервиса newsletter_manager, сервис mailer ещё не существует. Используйте класс Reference, чтобы сообщить контейнеру внедрить сервис mailer, когда он инициализирует менеджер новостных сообщений:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

$container = new ContainerBuilder();

$container->setParameter('mailer.transport', 'sendmail');
$container
    ->register('mailer', 'Mailer')
    ->addArgument('%mailer.transport%');

$container
    ->register('newsletter_manager', 'NewsletterManager')
    ->addArgument(new Reference('mailer'));

Если NewsletterManager не требовал Mailer, и его внедрение было необязательным, то вы можете использовать внедрение сеттера:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class NewsletterManager
{
    private $mailer;

    public function setMailer(\Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    // ...
}

Вы также можете теперь выбрать не внедрять Mailer в NewsletterManager. Если же вы хотите сделать это, то контейнер может вызвать сеттер-метод:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

$container = new ContainerBuilder();

$container->setParameter('mailer.transport', 'sendmail');
$container
    ->register('mailer', 'Mailer')
    ->addArgument('%mailer.transport%');

$container
    ->register('newsletter_manager', 'NewsletterManager')
    ->addMethodCall('setMailer', array(new Reference('mailer')));

Потом вы можете получить ваш сервис newsletter_manager из контейнера таким образом:

1
2
3
4
5
6
7
use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();

// ...

$newsletterManager = $container->get('newsletter_manager');

Избегание зависимости вашего кода от контейнера

Несмотря на то, что вы можете извлекать сервисы из контейнера напрямую, лучше это минимизировать. Например, в случае с NewsletterManager, в который вы внедрили сервис mailer, вместо того, чтобы запросить его из контейнера. Вы могли внедрить контейнер, и потом извлечь из него сервис mailer, но он был бы привязан к этому конкретному контейнеру, что усложнило бы повторное использование класса где-либо.

Вам нужно будет получить сервис из контейнера рано или поздно,ноэто должно быть сделано минимальное количество раз в точке входа вашего приложения.

Установка контейнера с файлами конфигурации

Кроме установки сервисов, используя PHP, как описано выше, вы также можете использовать файлы конфигурации. Это позволяет вам использовать XML или YAML для написания описаний для сервисов, а не использовать PHP для определения сервисов, как в примере выше. В любых случаях, кроме самых маленьких приложений, логино упорядочить определения сервиса, переместив их в один или более файлов конфигурации. Чтобы сделать это, вам также понадобится установить компонент Config.

Хагрузка файла конфигурации XML:

1
2
3
4
5
6
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.xml');

Загрузка файла конфигурации YAML:

1
2
3
4
5
6
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.yaml');

Note

Если вы хотите загрузить файлы конфигурации YAML, то вам также понадобится установить компонент Yaml.

Tip

Если ваше приложение использует нетрадиционные расширения файла (например, ваши файлы XML имеют расширение .config), то вы можете передать тип файла в качестве второго необязательного параметра метода load():

1
2
// ...
$loader->load('services.config', 'xml');

Если вы хотите использовать PHP, чтобы создавать сервисы, то вы можете переместить это в отдельный файл конфигурации и загружать его схожим образом:

1
2
3
4
5
6
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;

$container = new ContainerBuilder();
$loader = new PhpFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.php');

Теперь вы можете устанавливать сервисы newsletter_manager и mailer, используя файлы конфигурации:

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    parameters:
        # ...
        mailer.transport: sendmail
    
    services:
        mailer:
            class:     Mailer
            arguments: ['%mailer.transport%']
        newsletter_manager:
            class:     NewsletterManager
            calls:
                - [setMailer, ['@mailer']]
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <parameters>
            <!-- ... -->
            <parameter key="mailer.transport">sendmail</parameter>
        </parameters>
    
        <services>
            <service id="mailer" class="Mailer">
                <argument>%mailer.transport%</argument>
            </service>
    
            <service id="newsletter_manager" class="NewsletterManager">
                <call method="setMailer">
                    <argument type="service" id="mailer" />
                </call>
            </service>
        </services>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    use Symfony\Component\DependencyInjection\Reference;
    
    // ...
    $container->setParameter('mailer.transport', 'sendmail');
    $container
        ->register('mailer', 'Mailer')
        ->addArgument('%mailer.transport%');
    
    $container
        ->register('newsletter_manager', 'NewsletterManager')
        ->addMethodCall('setMailer', array(new Reference('mailer')));
    

Узнайте больше

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.