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
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
Is it possible to use it with the PHP service definition?
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!
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