Resolve services subscribed to with ServiceSubscriberTrait #366
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 agetSubscribedServices
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 serviceFoo
using the nameBar
(defined['Bar' => 'Foo']
ingetSubscribedServices()
, requested ascontainer->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 thegetSubscribedServices()
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 avalue
argument was the only argument forAutowire
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 whetherAutowired
is provided.At the moment this PR is still missing some bits, mainly:
#[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__
?)#[SubscribedService]
is used it is possible to use a self defined name instead of__METHOD__
which I didn't account for at the momentFurther 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 itFixes #321