SymfonyWorld Online 2021 Winter Edition December 9 – 10, 2021 100% Online 25 talks and 10 workshops

New in Symfony 5.1: Abstract service arguments

Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.

Contributed by
Islam Israfilov
in #35076.

Sometimes, when defining services in your Symfony applications, there are arguments that can't be added in config files. The reason is that their values can only be calculated at runtime in a compiler pass or bundle extension.

In those cases, it's common to add an empty argument with some comment explaining that the value will be injected later. For example, the second argument of this service is the full list of directories where Twig templates are stored. That list is only available when running the application, because bundles can add their own directories too:

1
2
3
4
5
<service id="twig.template_iterator" class="Symfony\Bundle\TwigBundle\TemplateIterator">
    <argument type="service" id="kernel" />
    <argument type="collection" /> <!-- Twig paths -->
    <argument>%twig.default_path%</argument>
</service>

This other service needs the root namespace of the application, something that's better to calculate dynamically when running the application (instead of forcing the user to configure this value manually):

1
2
3
4
<service id="maker.generator" class="Symfony\Bundle\MakerBundle\Generator">
    <argument type="service" id="maker.file_manager" />
    <argument /> <!-- root namespace -->
</service>

In Symfony 5.1 we've improved this config to replace the "empty argument + comment" by proper abstract service arguments. These are arguments whose values can only be calculated at runtime in compiler passes or bundle extensions.

This is how the previous example looks like when using abstract arguments:

1
2
3
4
<service id="maker.generator" class="Symfony\Bundle\MakerBundle\Generator">
    <argument type="service" id="maker.file_manager" />
    <argument type="abstract" key="$rootNamespace">defined in MakerPass</argument>
</service>

The key value defines the argument name in the service constructor and the value enclosed in <argument> ... </argument> is an optional comment about the argument. If you use YAML to define services, use this other syntax based on the !abstract keyword:

1
2
3
4
maker.generator:
    class: Symfony\Bundle\MakerBundle\Generator
    arguments:
        $rootNamespace: !abstract defined in MakerPass

This is the config needed when using PHP:

1
2
3
4
5
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;

$builder->register('maker.generator', Generator::class)
    ->setArgument('$rootNamespace', new AbstractArgument('defined in MakerPass'));

If you don't replace the value of the abstract arguments in some compiler pass or bundle extension, you'll see the following error message:

Argument "$rootNamespace" of service "maker.generator" is abstract (defined in MakerPass), did you forget to define it?
Help the Symfony project!

As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.

Comments

That's a great improvment.
Is it possible to use it with the PHP service definition?
@Florent yes it is, by setting an AbstractArgument object as the value of the argument (which is exactly what loaders are doing).
@Florent

Yep! Behind the scenes, this works via a new AbstractArgumentClass:

$builder->register('foo', FooWithAbstractArgument::class)
->addArgument(new AbstractArgument('foo', '$baz', 'should be defined by Pass'));

Cheers!
Great!
Does this combine correctly with unnamed service arguments? Take your Generator as example, how would you statically pass the maker.file_manager?

maker.generator:
class: Symfony\Bundle\MakerBundle\Generator
arguments:
- '@maker.file_manager'
$rootNamespace: !abstract defined in MakerPass

or

maker.generator:
class: Symfony\Bundle\MakerBundle\Generator
arguments:
$fileManager: '@maker.file_manager'
$rootNamespace: !abstract defined in MakerPass

Comments are closed.

To ensure that comments stay relevant, they are closed for old posts.