New in Symfony 2.8: DependencyInjection improvements

Allowed to define the priority of service decoration

Contributed by
Diego Saint Esteben in #15416.

Service decoration is a powerful way to replace some service without actually removing it from the container. This way the new service can make use of the replaced service.

In Symfony 2.8, when more than one service decorate another one, you can define the priority of each decorator to have a more precise control over them. The new option is called decoration_priority, its default value is 0 and the higher its value, the earlier the decorator is applied:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
services:
    app.service1:
        class: AppBundle\Service\Service1

    app.service2:
        class: AppBundle\Service\Service2
        arguments: ['@app.service2.inner']
        decorates: app.service1
        public: false

    app.service3:
        class: AppBundle\Service\Service3
        arguments: ['@app.service3.inner']
        decorates: app.service1
        decoration_priority: 1
        public: false

In the above example, app.service3 is applied first because it defines a priority of 1 and the priority for app.service2 is 0 (the default value). Therefore, this config is equivalent to the following PHP code:

1
$this->services['app.service1'] = new Service2(new Service3(new Service1())));

Added logging of unused tags

Contributed by
Florian Pfitzer in #15963.

In Symfony 2.8, when the configuration of a service adds a tag which is not used in the application, a message like this one is logged:

1
2
Tag "this_tag_is_not_used" was defined on service "app.service1" but
was never used.

All the built-in Symfony service tags (such as console.command and twig.extension) have been whitelisted to avoid polluting your log files with useless messages.

Moreover, if the name of the unused tag is similar to any of the other tags, the log message will add a "Did you mean" section, because this is likely to be a typo in your configuration.

For example, when using the following configuration:

1
2
3
4
5
services:
    app.service1:
        class: AppBundle\Service\Service1
        tags:
            -  { name: 'kenrel.event_listener', ... }

You'll see the following log message:

1
2
Tag "kenrel.event_listener" was defined on service "app.service1", but
was never used. Did you mean "kernel.event_listener"?

Implemented resettable containers

Contributed by
Christophe Coevoet in #15185.

Symfony 2.8 includes a new ResettableContainerInterface which defines a single method called reset(). This method is useful to release memory when the container is no longer being used.

Symfony's Kernel class calls this method during the shutdown process:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// src/Symfony/Component/HttpKernel/Kernel.php
// ...
public function shutdown()
{
    // ...

    if ($this->container instanceof ResettableContainerInterface) {
        $this->container->reset();
    }
}

Symfony's service container implements this interface to remove references to all services during shutdown, improving the odds of destructing services and the container through refcounting rather than waiting for PHP's garbage collector:

1
2
3
4
5
6
7
8
// src/Symfony/Component/DependencyInjection/Container.php
// ...
public function reset()
{
    // ...

    $this->services = array();
}

Simplified Registering of Compiler Passes

Contributed by
Wouter De Jong in #13761.

Compiler passes allow to modify the service container while Symfony is compiling it and before being used by the application. In Symfony 2.7 and previous versions, you needed to register those extensions as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
namespace AppBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use AppBundle\DependencyInjection\Compiler\CustomPass;

class AppBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->addCompilerPass(new CustomPass());
    }
}

In Symfony 2.8, this code is no longer needed because if your extensions implement CompilerPassInterface they will be automatically registered and the process() method will be executed during the container compilation.

Comments

Great! But in my opinion you are forgetting the new `shared` flag in replacement of the `prototype` scope.
Here's the PR: https://github.com/symfony/symfony/pull/14984
there is a typo:

- { name: 'kenrel.event_listener', ... }

kenrel -> kernel
here too:

Tag "kenrel.event_listener" was defined on service "app.service1", but

kenrel -> kernel, search for it ;-)
@Oskar Stark The point of the code is to demonstrate the unused tag via a typo.
In the "Simplified Registering of Compiler Passes" section "... because if your extensions implement CompilerPassInterface ..." . Shoudn't that be "... because if your CompilerPasses implement CompilerPassInterface ..."
Malte, no, this works if you DI Bundle Extension implements CompilerPassInterface. See PR if you are not sure https://github.com/symfony/symfony/pull/13761/files
Great!

Comments are closed.

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