Как работать с объектами определений сервиса

Как работать с объектами определений сервиса

Определения сервиса - это инструкции, описывающие то, как контейнер должен построить сервис. Они не яляются сервисами, используемыми вашими приложениями. Контейнер создаст настоящие экземпляры класса, основываясь на конфигурации в определении.

Обычно, вы бы использовали YAML, XML или PHP, чтобы описать определения сервиса. Но если вы делаете с сервис-контейнером продвинутые вещи, вроде работы с Пропуском компилятора или создания Расширения внедрения зависимости, вам может понадобиться работать напрямую с объектами Definition, который определяют то, как будет инстанциирован сервис.

Получение и установка определений сервиса

Существует несколько полезных методов для работы с определениями сервиса:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// узнать, есть ли определение "app.mailer"
$container->hasDefinition('app.mailer');
// узнать, если определения или дополнительного имени "app.mailer" нет
$container->has('app.mailer');

// получить определение "app.user_config_manager"
$definition = $container->getDefinition('app.user_config_manager');
// получить определение с ID или дополнительным именем "app.user_config_manager"
$definition = $container->findDefinition('app.user_config_manager');

// добавить новое определение "app.number_generator"
$definition = new Definition(\AppBundle\NumberGenerator::class);
$container->setDefinition('app.number_generator', $definition);

// шорткат для предыдущего метода
$container->register('app.number_generator', \AppBundle\NumberGenerator::class);

// или создать сервис, id которого соответствует его классу
$container->register(\AppBundle\NumberGenerator::class);

Работа с определением

Создание нового определения

В дополнение к изменению и получению существующих определений, вы также можете определять новые определения сервиса с классом Definition.

Класс

Первый необязательный аргумент класса Definition - это полностью квалифицированное имя класса объекта, возвращённого, когда сервис вызывается из контейнера:

1
2
3
4
5
6
7
8
9
10
11
use AppBundle\Config\UserConfigManager;
use AppBundle\Config\CustomConfigManager;
use Symfony\Component\DependencyInjection\Definition;

$definition = new Definition(UserConfigManager::class);

// переопределить класс
$definition->setClass(CustomConfigManager::class);

// сделать так, чтобы для этого определения был сконфигурирован класс
$class = $definition->getClass();

Аргументы конструктора

Второй необязательный аргумент класса Definition - это массив с аргументами, переданными конструктору объекта, возвращённого, когда сервис вызывается из контейнера:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use AppBundle\Config\DoctrineConfigManager;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

$definition = new Definition(DoctrineConfigManager::class, array(
    new Reference('doctrine'), // ссылка на другой сервис
    '%app.config_table_name%'  // будет решён до значения параметра контейнера
));

// получить все аргументы, сконфигурированные для этого определения
$constructorArguments = $definition->getArguments();

// получить конкретный аргумент
$firstArgument = $definition->getArgument(0);

// добавить новый аргумент
$definition->addArgument($argument);

// заменить аргумент конкретным индексом (0 = первый аргумент)
$definition->replaceArgument($index, $argument);

// заменить все ранее сконфигурированные аргументы переданным массивом
$definition->setArguments($arguments);

Caution

Не используйте get(), чтобы получить сервис, который вы хотите внедрить в качестве аргумента конструктора, сервис ещё не доступен. Вместо этого, используйте экземпляр Reference,как показано выше.

Вызовы метода

Если сервис, с которым вы работаете, использует сеттер-внедрение, тогда вы также можете изменить любые вызовы метода в определении:

1
2
3
4
5
6
7
8
// получить все сконфигурированные вызовы метода
$methodCalls = $definition->getMethodCalls();

// сконфигурировать новый вызов метода
$definition->addMethodCall('setLogger', array(new Reference('logger')));

// заменить все созданные ранее вызовы метода переданным массивом
$definition->setMethodCalls($methodCalls);

Tip

Существует больше примеров конкретных способов работы с определениями в блоках PHP-кода в статьях о Сервис-контейнере, например,
Использование фабрики для создания сервисов и Как управлять общими зависимостями с родительскими сервисами.

Note

Методы здесь, который изменяют определения сервиса, могут быть использованы только перед тем, как будет скомпилирован контейнер. Как только контейнер скомпилирован, вы больше не сможете изменять определения сервиса. Чтобы узнать больше о компиляции контейнера, смотрите Компиляция контейнера.

Требование файлов

Могут быть случаи, когда вам понадобится включить другой файл прямо перед тем, как будет загружен сам сервис. Чтобы сделать это, вы можете использовать метод setFile():

1
$definition->setFile('/src/path/to/file/foo.php');

Отметьте, что Symfony внутренне вызовет PHP-утверждение require_once, что означает, что ваш файл будет включён только единожды в каждом запросе.