phpstan-symfony icon indicating copy to clipboard operation
phpstan-symfony copied to clipboard

AutowireLocator does not work well

Open lyrixx opened this issue 8 months ago • 5 comments

When we inject a ServiceLocator, the extension does not understand that. It thinks it's the full container, but it's not. It's a specialed container.

I made a full reproducer:

https://github.com/lyrixx/test/commit/e172840d514a00c3ed8667e828a4197a3028c782#r154945352

(ref https://github.com/symfony/symfony/pull/60096#issuecomment-2782785035)

lyrixx avatar Apr 07 '25 10:04 lyrixx

I don't get it. Please explain the problem.

ondrejmirtes avatar Apr 07 '25 11:04 ondrejmirtes

edit I just noticed this issue should be move to phpstan/phpstan-symfony

In Symfony, you can type hint a parameter with ContainerInterface, but when you add the #[AutowireLocator] attribute, it won't inject the service_container (the real / huge container). Instead it will inject a smaller, specialized container with only few services.

Example:

 class Foo
 {
     public function __construct(
         #[AutowireLocator(MyInterface::class)]
         private ContainerInterface $container,
     ) {}
 
     public function consume(): void
     {
         $this->container->get(Foo::class);
     }
 }

Here, in $this->container, there is only services where the definition is tagged with "App\Reproducer\MyInterface" (here, only Foo service).

If you look at the the dumped definition:

    <service id="App\Reproducer\Consumer" class="App\Reproducer\Consumer" autowire="true" autoconfigure="true">
      <argument type="service" id=".service_locator.ExRZJjZ"/>
    </service>
    <service id=".service_locator.ExRZJjZ" class="Symfony\Component\DependencyInjection\ServiceLocator">
      <tag name="container.service_locator"/>
      <argument type="collection">
        <argument key="App\Reproducer\Foo" type="service_closure" id="App\Reproducer\Foo"/>
      </argument>
    </service>

You can also specified some service ids:

    public function __construct(
        #[AutowireLocator([
            FooHandler::class,
            BarHandler::class,
        ])]
        private ContainerInterface $handlers,
    ) {
    }

If I'm not clear enough, you can read the symfony doc


When running phpstan on this code, it thinks the container is the whole symfony container. And so it tells us the service App\Reproducer\Foo is private. It is in the whole container, but it is not in the real injected container.

So, when a container is injected, phpstan should check if it's the whole service_container or a smaller container

lyrixx avatar Apr 07 '25 12:04 lyrixx

This belongs to phpstan-symfony.

ondrejmirtes avatar Apr 07 '25 13:04 ondrejmirtes

This belongs to phpstan-symfony.

Yes, that what I said in my latest comment. I have no right to move this issue. Could you do it for me? Thanks

lyrixx avatar Apr 07 '25 14:04 lyrixx

Same problem with Symfony\Contracts\Service\ServiceSubscriberInterface

There are several issues / PR related to the container : #352 #89 #420 #252 #411 #338 #321 #79 #74 #28

hlecorche avatar Nov 09 '25 12:11 hlecorche