Symfony 7.3 introduces several enhancements to the DependencyInjection component that simplify service configuration, make autoconfiguration more flexible, and enable environment-specific aliasing.

Service Closure Shorthand

chx
Contributed by chx in #59257

Service closures wrap the injected service into a closure allowing it to be lazily loaded only when needed. This is useful when the injected service is expensive to instantiate or only used in specific cases.

When using YAML config, you had to use the custom !service_closure tag to inject service closures. In Symfony 7.3, we've added a shortcut to configure these service closures:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# config/services.yaml
services:
    # this is the traditional way of defining service closures
    App\Service\MyService:
        arguments: [!service_closure '@mailer']

        # in case the dependency is optional
        # arguments: [!service_closure '@?mailer']

    # '@>' is the new shortcut to define service closures
    App\Service\AnotherService:
        arguments: ['@>mailer']

        # the shortcut also works for optional dependencies
        # arguments: ['@>?mailer']

Defining Aliases per Environment

Zuruh
Contributed by Zuruh in #60186

Service aliasing lets you create an alternative ID for existing services. They're useful for defining shortcuts, renaming services without removing the original ones, and more.

In Symfony 7.3, we're improving service aliases so you can define aliases only on certain environments. This is useful for example to create aliases only in the test configuration environment for testing purposes:

1
2
3
4
5
6
7
8
9
10
11
// src/Some/Service.php
namespace App\Some;

// ...
use Symfony\Component\DependencyInjection\Attribute\AsAlias;

#[AsAlias(id: 'app.foo', when: 'test')]
class Service
{
    // ...
}

You can pass more than one environment name if needed:

1
#[AsAlias(id: 'app.foo', when: ['dev', 'test'])]

Resource Tags

Jérôme Tamarelle Kevin Bond
Contributed by Jérôme Tamarelle and Kevin Bond in #59704 and #60011

Sometimes you want to exclude certain classes like model classes, entities, etc. from service autoconfiguration. However, this prevents tagging those classes with a service tag and later using methods like findTaggedServiceIds() to find them.

In Symfony 7.3, we've improved the service container so you can find tagged classes even if they are not registered as services. For example, suppose your application defines a PHP attribute called #[AppModel], which you apply to all model classes that should not be registered as services.

Then, in a service container extension, you can use autoconfiguration to find all classes with that PHP attribute and apply a tag to them using the new addResourceTag() method:

1
2
3
4
5
6
7
8
$this->registerAttributeForAutoconfiguration(AppModel::class, static function (ChildDefinition $definition) {
    // this is the new method introduced in Symfony 7.3
    $definition->addResourceTag('app.model');

    // there's no need to apply the following tag because it's automatically
    // applied by Symfony for you to not register these classes as services
    // $definition->addTag('container.excluded');
});

Related to this change, Symfony 7.3 also improves the registerAttributeForAutoconfiguration() method, allowing you to call it multiple times for the same PHP attribute. Previously, it could only be called once per attribute.

Later, in a compiler pass, you can find these classes using the new findTaggedResourceIds() method:

1
2
3
4
5
6
7
$classes = [];
foreach($containerBuilder->findTaggedResourceIds('app.model') as $id => $tags) {
    $classes[] = $containerBuilder->getDefinition($id)->getClass();
}

// save the list of classes as a container parameter to retrieve it later
$containerBuilder->setParameter('.app.model_classes', $classes);

Symfony itself now uses resource tags. If you check the service configuration of your Symfony applications, you'll probably find the following exclude lines:

1
2
3
4
5
6
7
8
9
10
# config/services.yaml
services:
    # ...

    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'

Starting in Symfony 7.3, these default exclude lines are no longer necessary. Thanks to the new resource tags introduced in this version, service container extensions, test cases, Doctrine entities, and Messenger messages are now automatically tagged and excluded for you.


Special thanks to Jérôme for helping review the contents of this post.

Published in #Living on the edge