Skip to content

Commit b311017

Browse files
feature #32256 [DI] Add compiler pass and command to check that services wiring matches type declarations (alcalyn, GuilhemN, nicolas-grekas)
This PR was merged into the 4.4 branch. Discussion ---------- [DI] Add compiler pass and command to check that services wiring matches type declarations | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #27744 | License | MIT | Doc PR | PR replacing symfony/symfony#27825. It adds a `lint:container` command asserting the type hints used in your code are correct. Commits ------- 8230a1543e Make it really work on real apps 4b3e9d4c96 Fix comments, improve the feature a6292b917b [DI] Add compiler pass to check arguments type hint
2 parents f0176a9 + d6b6816 commit b311017

File tree

5 files changed

+98
-1
lines changed

5 files changed

+98
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
4.4.0
55
-----
66

7+
* Added `lint:container` command to check that services wiring matches type declarations
78
* Added `MailerAssertionsTrait`
89
* Deprecated support for `templating` engine in `TemplateController`, use Twig instead
910
* Deprecated the `$parser` argument of `ControllerResolver::__construct()` and `DelegatingLoader::__construct()`

Command/ContainerLintCommand.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Command;
13+
14+
use Symfony\Component\Config\ConfigCache;
15+
use Symfony\Component\Config\FileLocator;
16+
use Symfony\Component\Console\Command\Command;
17+
use Symfony\Component\Console\Input\InputInterface;
18+
use Symfony\Component\Console\Output\OutputInterface;
19+
use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
20+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
21+
use Symfony\Component\DependencyInjection\ContainerBuilder;
22+
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
23+
24+
final class ContainerLintCommand extends Command
25+
{
26+
protected static $defaultName = 'lint:container';
27+
28+
/**
29+
* @var ContainerBuilder
30+
*/
31+
private $containerBuilder;
32+
33+
/**
34+
* {@inheritdoc}
35+
*/
36+
protected function configure()
37+
{
38+
$this
39+
->setDescription('Ensures that arguments injected into services match type declarations')
40+
->setHelp('This command parses service definitions and ensures that injected values match the type declarations of each services\' class.')
41+
;
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
protected function execute(InputInterface $input, OutputInterface $output): int
48+
{
49+
$container = $this->getContainerBuilder();
50+
51+
$container->setParameter('container.build_hash', 'lint_container');
52+
$container->setParameter('container.build_time', time());
53+
$container->setParameter('container.build_id', 'lint_container');
54+
55+
$container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100);
56+
57+
$container->compile();
58+
59+
return 0;
60+
}
61+
62+
private function getContainerBuilder(): ContainerBuilder
63+
{
64+
if ($this->containerBuilder) {
65+
return $this->containerBuilder;
66+
}
67+
68+
$kernel = $this->getApplication()->getKernel();
69+
70+
if (!$kernel->isDebug() || !(new ConfigCache($kernel->getContainer()->getParameter('debug.container.dump'), true))->isFresh()) {
71+
$buildContainer = \Closure::bind(function () { return $this->buildContainer(); }, $kernel, \get_class($kernel));
72+
$container = $buildContainer();
73+
$container->getCompilerPassConfig()->setRemovingPasses([]);
74+
} else {
75+
(new XmlFileLoader($container = new ContainerBuilder(), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump'));
76+
}
77+
78+
return $this->containerBuilder = $container;
79+
}
80+
}

Resources/config/console.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@
7070
<tag name="console.command" command="debug:container" />
7171
</service>
7272

73+
<service id="console.command.container_lint" class="Symfony\Bundle\FrameworkBundle\Command\ContainerLintCommand">
74+
<tag name="console.command" command="lint:container" />
75+
</service>
76+
7377
<service id="console.command.debug_autowiring" class="Symfony\Bundle\FrameworkBundle\Command\DebugAutowiringCommand">
7478
<argument>null</argument>
7579
<argument type="service" id="debug.file_link_formatter" on-invalid="null"/>

Resources/config/validator.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</service>
1818
<service id="Symfony\Component\Validator\Validator\ValidatorInterface" alias="validator" />
1919

20-
<service id="validator.builder" class="Symfony\Component\Validator\ValidatorBuilderInterface">
20+
<service id="validator.builder" class="Symfony\Component\Validator\ValidatorBuilder">
2121
<factory class="Symfony\Component\Validator\Validation" method="createValidatorBuilder" />
2222
<call method="setConstraintValidatorFactory">
2323
<argument type="service" id="validator.validator_factory" />

Tests/Functional/Bundle/TestBundle/TestBundle.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\AnnotationReaderPass;
1515
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\Config\CustomConfig;
1616
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\TranslationDebugPass;
17+
use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
18+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1719
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
1820
use Symfony\Component\DependencyInjection\ContainerBuilder;
1921
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -31,5 +33,15 @@ public function build(ContainerBuilder $container)
3133

3234
$container->addCompilerPass(new AnnotationReaderPass(), PassConfig::TYPE_AFTER_REMOVING);
3335
$container->addCompilerPass(new TranslationDebugPass());
36+
37+
$container->addCompilerPass(new class() implements CompilerPassInterface {
38+
public function process(ContainerBuilder $container)
39+
{
40+
$container->removeDefinition('twig.controller.exception');
41+
$container->removeDefinition('twig.controller.preview_error');
42+
}
43+
});
44+
45+
$container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100);
3446
}
3547
}

0 commit comments

Comments
 (0)