Skip to content

Commit 47eefa4

Browse files
bug #45629 [FrameworkBundle] Fix container:lint and #[Autoconfigure(binds: ...)] failing (LANGERGabrielle)
This PR was merged into the 5.4 branch. Discussion ---------- [FrameworkBundle] Fix container:lint and #[Autoconfigure(binds: ...)] failing | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | | License | MIT | Doc PR | # Description Running `container:lint` when a service uses `#[Autoconfigure(binds: ['$myDependency' => {...})]` raises an error : ``` [ERROR] A binding is configured for an argument named "$myDependency" under "_instanceof" in file "/{...}/MyService.php", but no corresponding argument has been found. It may be unused and should be removed, or it may have a typo. ``` # How to reproduce Given a service ```php #[Autoconfigure(bind: ['$myDependency' => '`@app`.my_dependency'])] class MyService { public function __construct(MyDependency $myDependency) { } } ``` ``@app`.my_dependency` is correctly injected into `MyService`. But when running `container:lint` **with the container dumped** (`App_KernelDevDebugContainer.xml` is present and up to date), it raises an error. This only happens with `#[Autoconfigure]`. A similar configuration but using `services.yaml` works as expected : ```yaml __instanceof: App\MyService: binds: $myDependency: '`@app`.my_dependency' ``` # Explanation of the issue 1. [`RegisterAutoconfigureAttributesPass` parses the `#[Autoconfigure]` and registers its binding](https://github.com/symfony/symfony/blob/08fa74a16c84895575e305b2a7ee3a03e371f79b/src/Symfony/Component/DependencyInjection/Compiler/RegisterAutoconfigureAttributesPass.php#L71). 2. [`ResolveBindingsPass` reads the binding and uses it to configure the service `arguments`](https://github.com/symfony/symfony/blob/1255cfffc260500adbfb3268b8f6c644e8bc43dd/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php#L223). 3. The container is dumped, with `App_KernelDevDebugContainer.xml` containing ```xml <service id="App\MyService" class="App\MyService" autowire="true" autoconfigure="true"> <argument type="service" id="app.my_dependency"/> </service> ``` 4. [`ContainerLintCommand` then creates a container from the dump](https://github.com/symfony/symfony/blob/08fa74a16c84895575e305b2a7ee3a03e371f79b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php#L104). 5. [The container is compiled](https://github.com/symfony/symfony/blob/08fa74a16c84895575e305b2a7ee3a03e371f79b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php#L65). Since `Kernel::initializeBundles()` is not called, only the [base passes](https://github.com/symfony/symfony/blob/60ce5a3dfbd90fad60cd39fcb3d7bf7888a48659/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php#L42) are used. 6. [`RegisterAutoconfigureAttributesPass` parses the `#[Autoconfigure]` and registers its binding](https://github.com/symfony/symfony/blob/08fa74a16c84895575e305b2a7ee3a03e371f79b/src/Symfony/Component/DependencyInjection/Compiler/RegisterAutoconfigureAttributesPass.php#L71), **again**. 7. [`ResolveBindingsPass` sees that the service already has an `argument` (from the xml dump from the first `ResolveBindingsPass`)](https://github.com/symfony/symfony/blob/1255cfffc260500adbfb3268b8f6c644e8bc43dd/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php#L181). This mean that the binding is **not** used this time, [and it is never removed from `$unusedBindings`](https://github.com/symfony/symfony/blob/1255cfffc260500adbfb3268b8f6c644e8bc43dd/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php#L126). 8. [The error is then thrown](https://github.com/symfony/symfony/blob/1255cfffc260500adbfb3268b8f6c644e8bc43dd/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php#L76). # Explanation of the fix This fix removes the passes that already processed the dumped container. A more future proof fix would be symfony/symfony@209516b, but it would require changes to `DependencyInjection`. Both fixes have been tested on a mid size Symfony application. Commits ------- 309998b94d [FrameworkBundle] Fix compiler passes processing a container twice when it's loaded from the debug dump
2 parents 1542eaf + 49298d5 commit 47eefa4

File tree

2 files changed

+8
-0
lines changed

2 files changed

+8
-0
lines changed

Command/BuildDebugContainerTrait.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ protected function getContainerBuilder(KernelInterface $kernel): ContainerBuilde
5353
(new XmlFileLoader($container = new ContainerBuilder(), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump'));
5454
$locatorPass = new ServiceLocatorTagPass();
5555
$locatorPass->process($container);
56+
57+
$container->getCompilerPassConfig()->setBeforeOptimizationPasses([]);
58+
$container->getCompilerPassConfig()->setOptimizationPasses([]);
59+
$container->getCompilerPassConfig()->setBeforeRemovingPasses([]);
5660
}
5761

5862
return $this->containerBuilder = $container;

Command/ContainerLintCommand.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ private function getContainerBuilder(): ContainerBuilder
118118
$skippedIds[$serviceId] = true;
119119
}
120120
}
121+
122+
$container->getCompilerPassConfig()->setBeforeOptimizationPasses([]);
123+
$container->getCompilerPassConfig()->setOptimizationPasses([]);
124+
$container->getCompilerPassConfig()->setBeforeRemovingPasses([]);
121125
}
122126

123127
$container->setParameter('container.build_hash', 'lint_container');

0 commit comments

Comments
 (0)