Компонент DependencyInjection (внедрение зависимости)¶
Компонент DependencyInjection реализует сервис-контейнер, совместимый с PSR-11, который позволяет вам стандартизировать и централизовать то, как строятся объекты в вашем приложении.
Для вступления во Внедрение зависимостей и сервис-контейнерв, см. Service Container.
Установка¶
1 | $ composer require symfony/dependency-injection
|
Также вы можете клонировать репозиторий https://github.com/symfony/dependency-injection.
Note
If you install this component outside of a Symfony application, you must
require the vendor/autoload.php
file in your code to enable the class
autoloading mechanism provided by Composer. Read
this article for more details.
Базовое применение¶
У вас может быть простой класс вроде следующего 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;
$containerBuilder = new ContainerBuilder();
$containerBuilder->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;
$containerBuilder = new ContainerBuilder();
$containerBuilder
->register('mailer', 'Mailer')
->addArgument('sendmail');
|
Этот класс теперь намного более гибкий, так как вы отделили выбор транспорта от реализации, и поместили его в контейнер.
То, какой почтовый транспорт вы выбрали, может быть чем-то, о чём стоит знать другим сервисам. Вы можете избежать его изменения в нескольких местах, сделав его параметром в контейнере, а потом ссылаясь на этот параметр в качестве аргумента конструктора сервиса:
1 2 3 4 5 6 7 | use Symfony\Component\DependencyInjection\ContainerBuilder;
$containerBuilder = new ContainerBuilder();
$containerBuilder->setParameter('mailer.transport', 'sendmail');
$containerBuilder
->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;
$containerBuilder = new ContainerBuilder();
$containerBuilder->setParameter('mailer.transport', 'sendmail');
$containerBuilder
->register('mailer', 'Mailer')
->addArgument('%mailer.transport%');
$containerBuilder
->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;
$containerBuilder = new ContainerBuilder();
$containerBuilder->setParameter('mailer.transport', 'sendmail');
$containerBuilder
->register('mailer', 'Mailer')
->addArgument('%mailer.transport%');
$containerBuilder
->register('newsletter_manager', 'NewsletterManager')
->addMethodCall('setMailer', array(new Reference('mailer')));
|
Потом вы можете получить ваш сервис newsletter_manager
из контейнера
таким образом:
1 2 3 4 5 6 7 | use Symfony\Component\DependencyInjection\ContainerBuilder;
$containerBuilder = 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;
$containerBuilder = new ContainerBuilder();
$loader = new XmlFileLoader($containerBuilder, 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;
$containerBuilder = new ContainerBuilder();
$loader = new YamlFileLoader($containerBuilder, 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;
$containerBuilder = new ContainerBuilder();
$loader = new PhpFileLoader($containerBuilder, 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')));
Узнайте больше¶
- Compiling the Container
- Компиляция контейнера
- Container Building Workflow
- Рабочий процесс построения контейнера
- The Symfony 3.3 DI Container Changes Explained (autowiring, _defaults, etc)
- Объяснение изменений контейнера DI в Symfony 3.3 (autowiring, _defaults, и др.)
- How to Create Service Aliases and Mark Services as Private
- Как создавать альтернативные имена сервиса и отмечать сервисы, как приватные
- Defining Services Dependencies Automatically (Autowiring)
- Автоматическое определение зависимостей сервиса (автомонтирование)
- Service Method Calls and Setter Injection
- Вызовы метода сервиса и сеттер-внедрение
- How to Work with Compiler Passes
- Как работать с пропусками компилятора в пакетах
- How to Configure a Service with a Configurator
- Как сконфигурировать сервис с помощью конфигуратора
- How to Debug the Service Container & List Services
- Как отлаживать сервис-контейнер и список сервисов
- How to work with Service Definition Objects
- Как работать с объектами определений сервиса
- How to Inject Values Based on Complex Expressions
- Как внедрять значения, основанные на сложных выражениях
- Using a Factory to Create Services
- Использование фабрики для созданя сервисов
- How to Import Configuration Files/Resources
- Как импортировать файлы/ресурсы конфигурации
- Types of Injection
- Типы внедрения
- Lazy Services
- Ленивые сервисы
- How to Make Service Arguments/References Optional
- Как сделать аргументы/ссылки сервисов необязательными
- Введение в параметры
- How to Manage Common Dependencies with Parent Services
- Как управлять общими зависимостями с родительскими сервисами
- How to Retrieve the Request from the Service Container
- How to Retrieve the Request from the Service Container
- How to Decorate Services
- Как декорировать сервисы
- Локаторы сервисов
- Service Subscribers & Locators
- How to Define Non Shared Services
- Как определять необщие сервисы
- How to Inject Instances into the Container
- Как внедрять экземпляры в контейнер
- How to Work with Service Tags
- Как работат с тегами сервисов
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.