Skip to content
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

Resolve services subscribed to with ServiceSubscriberTrait #366

Closed

Conversation

RobertMe
Copy link
Contributor

First off I do want to note that I've only looked at PHPStan source code for about a day. So please bear with me, and I'm certain this code can be cleaned up given some tips :)

So, the actual PR: Symfony has the feature of "service subscribers" where a service / class can implement the ServiceSubscriberInterface having a getSubscribedServices method returning a mapping of desired services / service ids to be injected and their name in the actual container. The DI container will then create a slimmed down container / locator with the requested services. This feature however isn't understood by this extension at all. So when requesting the service Foo using the name Bar (defined ['Bar' => 'Foo'] in getSubscribedServices(), requested as container->get('Bar')) this extension will "fail" and falsely report "Service "Bar" is not registered in the container".

To simplify the usage of this interface / mechanism Symfony also contains a trait, ServiceSubscriberTrait, which uses some "magic" to implement the getSubscribedServices() method. This by scanning all methods in the class, and, in Symfony 6, checking whether they have the #[SubscribedService] attribute. In these cases this method will be "mapped" to requesting a service, by using the method name (equaling __METHOD__) as service id and the return type as service to request (so [<__METHOD__> => <return type of method>]). In Symfony 6 it's also possible to explicitly set the service to request by using #[SubscribedService(attributes: new Autowire(service: '<service id>'))] and some variants (attributes can be an array, previously a value argument was the only argument for Autowire which could also explicitly request a service by prefixing it with @, obviously arguments can be positional as well, ...).

So what this PR tries to solve is to resolve the service id needed from the "actual" container. This by checking whether the method which calls ->get / ->has has this #[SubscribedService] attribute and if so it also tries to find whether Autowired is provided.

At the moment this PR is still missing some bits, mainly:

  • Support for older Symfony versions. For example before Symfony 6 the usage of #[SubscribedService] was optional (and previously this attribute didn't even exist). In this case it might / should fallback to checking whether the method calling ->get / ->has has no arguments and a type hint defined (and the ->get / ->has argument is __METHOD__?)
  • When #[SubscribedService] is used it is possible to use a self defined name instead of __METHOD__ which I didn't account for at the moment

Further development might be target at actually inspecting getSubscribedServices as well. As this most likely will be a constant array (although it might merge with a parent call). But I have no idea whether that is actually possible and how to implement it

Fixes #321

@RobertMe
Copy link
Contributor Author

Closing in favor of a completely new PR / solution I'm working on (which uses the container XML to extract the service locators per service class. So will work with most (/any) scenarios, including when getSubscribedServices is implemented manually, when the service locator is created in a compiler pass, when #[TaggedLocator] is used, ...)

@RobertMe RobertMe closed this Oct 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ServiceSubscriberTrait
1 participant