Skip to content

[DependencyInjection] Service subscriber without autowire #60272

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

Open
wants to merge 5 commits into
base: 7.3
Choose a base branch
from

Conversation

Neirda24
Copy link
Contributor

@Neirda24 Neirda24 commented Apr 25, 2025

Q A
Branch? 7.3
Bug fix? no
New feature? yes
Deprecations? no
Issues Fix #...
License MIT

TL;DR

We needed to use the ServiceSubscriber feature in a reusable Bundle.
The best practices for Bundles is to not use the Autowire feature.
But the way service subscriber are registered / resolved only works with autowired services at the moment.

The goal of this PR is to provide a way to easily extract the Locator for a service subscriber class / definition and use it in a custom compiler pass for example.

class MyPass implements CompilerPassInterface
{
    public function process(...)
    {
        $myServiceSubscriber = $container->getDefinition('my_service_subscriber');

        $locator = RegisterServiceSubscribersPass::registerLocator($container, 'my_service_subscriber');

        $myServiceSubscriber->addMethodCall('setContainer', [$locator]);
    }
}

More details

  1. The ServiceSubscriberInterface is parsed by the RegisterServiceSubscribersPass if the service definition has the container.service_subscriber
  2. It process the getSubscribedServices static method to cerate a $serviceMap
  3. It registers a ServiceLocator specific for this Service
  4. It adds a tag container.service_subscriber.locator with the id of the service locator
  5. It automatically binds the locator on the service for anything matching PsrContainerInterface
  6. later on, it goes through ResolveServiceSubscribersPass which clears tags + inject the locator

There is mo way to insert a compiler pass in between those two, they have the same priority in the same PassConfig type.

Apart from re-doing all the logic ourselves, there are no easy way to create a service locator solely based on the interface.

Why do I need to use a ServiceSubscriber in my Bundle ?

Our bundle provides extension points, allowing developers to create their own services that integrate seamlessly with the bundle's internal logic. This improves the developer experience (DX) by simplifying service wiring.

Since we can't predict in advance which services these extensions will depend on, using a ServiceSubscriber is ideal. It allows us to support dynamic dependencies without requiring developers to write a compiler pass.

For example, if a developer adds a trait like TwigAwareTrait to a class, the twig service becomes a dependency. We want to give them the flexibility to inject such additional services—beyond those provided by default in the service locator—in a clean and declarative way.

@Neirda24 Neirda24 changed the title Service subscriber without autowire [DependencyInjection] Service subscriber without autowire Apr 25, 2025
@stof
Copy link
Member

stof commented Apr 25, 2025

I'd like to understand more what issue you face.

I'm using ServiceSubscriberInterface on a non-autowired service since years: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/901069c01e20ef4d6c4e0d638428656ae5bedcdf/src/Resources/config/change_password.xml#L24-L27

You need to configure your setContainer call explicitly, by referencing either PsrContainerInterface::class or ServiceProviderInterface::class (as those are the bindings registered by

@Neirda24
Copy link
Contributor Author

I'd like to understand more what issue you face.

I'm using ServiceSubscriberInterface on a non-autowired service since years: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/901069c01e20ef4d6c4e0d638428656ae5bedcdf/src/Resources/config/change_password.xml#L24-L27

You need to configure your setContainer call explicitly, by referencing either PsrContainerInterface::class or ServiceProviderInterface::class (as those are the bindings registered by

Oh. Didn't know this and din't figured out either. Let me try it out and maybe open a PR on documentation. If it works on my side I will close this PR. Thank you @stof

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