Skip to content

[DependencyInjection] don't move locator tag for service subscriber #48093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

RobertMe
Copy link
Contributor

@RobertMe RobertMe commented Nov 3, 2022

Q A
Branch? 4.4
Bug fix? yes
New feature? no
Deprecations? no
Tickets -
License MIT
Doc PR -

From the commit message:
Decorators move tags applied to the decorated service to the decorating service. But this (sometimes) breaks when the decorated service is a service subscriber, which has the argument for the container explicitly set.

This mostly works because the locator for the service subscriber is applied twice. The RegisterServiceSubscriberPass which creates the locator also sets a binding on the service. The
ResolveServiceSubscriberPass replaces the arguments referencing the ContainerInterface or ServiceProviderInterface for those services tagged with the container.service_subscriber.locator tag. So when the argument isn't provided in the service definition it will automatically be set using the binding. And in case the argument is set, it will be replaced by the Resolver pass based on the tag.

But this thus breaks in case a service explicitly sets the argument (which means the binding isn't applied) and the service being decorated (meaning the locator tag is "lost"). So add the locator tag to the list of tags to keep on the original service.

Explanation:
I found this issue when decorating the Router. The Router (in FrameworkBundle) uses a service subscriber, but this lead to a deprecation message for autowiring Psr\...\ContainerInterface. Debugging also showed that the full container was injected, and not the extracted service locator (service locator service actually was logged as removed for being unused). After investigation the issue was found to be as described above. The router service is declared with an argument for the $container parameter of the constructor, i.e.:

<service id="router.default" class="Symfony\Bundle\FrameworkBundle\Routing\Router">
            <argument type="service" id="Psr\Container\ContainerInterface" />

Which leads to the binding, as declared in the RegisterServiceSubscribersPass pass not being applied. Later on the DecoratorServicePass pass moves the tags of the decorated service to the decorating service, where it only keeps the container.service_subscriber tag on the original service, and moves the container.service_subscriber.locator tag to the decorating service. When afterwards the ResolveServiceSubscribersPass pass is executed it will replace the arguments to the ContainerInterface with the created locator service (as defined in the RegisterServiceSubscribersPass). But this then fails because the container.service_subscriber.locator tag isn't applied anymore.

So when the router isn't decorated the ResolveServiceSubscribersPass pass is the one which makes the service subscriber work, replacing the original argument as defined in the service definition. But when it is decorated this breaks because the tag is missing.
The unit tests didn't detect this because: 1. the container isn't injected (and thus not validated); 2. even with validation of the container it would work as the binding would be applied. This is why I also kept the original unit test (but expanding the test with validating the container), which would still pass (based on the binding), and adding the additional test which explicitly sets the $container argument, which would fail (for the binding not being applied, and the tag being missing because of the decorator).

Decorators move tags applied to the decorated service to the decorating
service. But this (sometimes) breaks when the decorated service is a
service subscriber, which has the argument for the container explicitly
set.

This mostly works because the locator for the service subscriber is
applied twice. The RegisterServiceSubscriberPass which creates the
locator also sets a binding on the service. The
ResolveServiceSubscriberPass replaces the arguments referencing the
ContainerInterface or ServiceProviderInterface for those services tagged
with the container.service_subscriber.locator tag. So when the argument
isn't provided in the service definition it will automatically be set
using the binding. And in case the argument is set, it will be replaced
by the Resolver pass based on the tag.

But this thus breaks in case a service explicitly sets the
argument (which means the binding isn't applied) and the service being
decorated (meaning the locator tag is "lost"). So add the locator tag
to the list of tags to keep on the original service.
@RobertMe RobertMe force-pushed the replace-container-of-decorated-service-subscriber-with-argument branch from c4b0e31 to 3c7dbb4 Compare November 3, 2022 17:29
@nicolas-grekas
Copy link
Member

Thank you @RobertMe.

@nicolas-grekas nicolas-grekas merged commit ad0e25f into symfony:4.4 Nov 4, 2022
@RobertMe RobertMe deleted the replace-container-of-decorated-service-subscriber-with-argument branch November 4, 2022 08:08
@fabpot fabpot mentioned this pull request Nov 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants