10000 [FrameworkBundle] Add debug:autoconfigure command · symfony/symfony@09bb892 · GitHub
[go: up one dir, main page]

Skip to content

Commit 09bb892

Browse files
aaa2000nicolas-grekas
authored andcommitted
[FrameworkBundle] Add debug:autoconfigure command
Close #26295
1 parent 5440d67 commit 09bb892

File tree

12 files changed

+419
-1
lines changed

12 files changed

+419
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ CHANGELOG
5252
* Added a `InMemoryTransport` to Messenger. Use it with a DSN starting with `in-memory://`.
5353
* Added `framework.property_access.throw_exception_on_invalid_property_path` config option.
5454
* Added `cache:pool:list` command to list all available cache pools.
55+
* Added `debug:autoconfiguration` command to display the autoconfiguration of interfaces/classes
5556

5657
4.2.0
5758
-----
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
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\Console\Command\Command;
15+
use Symfony\Component\Console\Input\InputArgument;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Input\InputOption;
18+
use Symfony\Component\Console\Output\OutputInterface;
19+
use Symfony\Component\Console\Style\SymfonyStyle;
20+
use Symfony\Component\DependencyInjection\ChildDefinition;
21+
use Symfony\Component\DependencyInjection\ContainerBuilder;
22+
use Symfony\Component\DependencyInjection\Dumper\YamlDumper;
23+
use Symfony\Component\VarDumper\Cloner\VarCloner;
24+
use Symfony\Component\VarDumper\Dumper\AbstractDumper;
25+
use Symfony\Component\VarDumper\Dumper\CliDumper;
26+
27+
/**
28+
* A console command for autoconfiguration information.
29+
*
30+
* @internal
31+
*/
32+
final class DebugAutoconfigurationCommand extends Command
33+
{
34+
protected static $defaultName = 'debug:autoconfiguration';
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
protected function configure()
40+
{
41+
$this
42+
->setDefinition([
43+
new InputArgument('search', InputArgument::OPTIONAL, 'A search filter'),
44+
new InputOption('tags', null, InputOption::VALUE_NONE, 'Displays autoconfiguration interfaces/class grouped by tags'),
45+
])
46+
->setDescription('Displays current autoconfiguration for an application')
47+
->setHelp(<<<'EOF'
48+
The <info>%command.name%</info> command displays all services that
49+
are autoconfigured:
50+
51+
<info>php %command.full_name%</info>
52+
53+
You can also pass a search term to filter the list:
54+
55+
<info>php %command.full_name% log</info>
56+
57+
EOF
58+
)
59+
;
60+
}
61+
62+
/**
63+
* {@inheritdoc}
64+
*/
65+
protected function execute(InputInterface $input, OutputInterface $output)
66+
{
67+
$io = new SymfonyStyle($input, $output);
68+
$errorIo = $io->getErrorStyle();
69+
70+
$autoconfiguredInstanceofItems = $this->getContainerBuilder()->getAutoconfiguredInstanceof();
71+
72+
if ($search = $input->getArgument('search')) {
73+
$autoconfiguredInstanceofItems = array_filter($autoconfiguredInstanceofItems, function ($key) use ($search) {
74+
return false !== stripos(str_replace('\\', '', $key), $search);
75+
}, ARRAY_FILTER_USE_KEY);
76+
77+
if (!$autoconfiguredInstanceofItems) {
78+
$errorIo->error(sprintf('No autoconfiguration interface/class found matching "%s"', $search));
79+
80+
return 1;
81+
}
82+
}
83+
84+
ksort($autoconfiguredInstanceofItems, SORT_NATURAL);
85+
86+
$io->title('Autoconfiguration');
87+
if ($search) {
88+
$io->text(sprintf('(only showing classes/interfaces matching <comment>%s</comment>)', $search));
89+
}
90+
$io->newLine();
91+
92+
/** @var ChildDefinition $autoconfiguredInstanceofItem */
93+
foreach ($autoconfiguredInstanceofItems as $key => $autoconfiguredInstanceofItem) {
94+
$tableRows = [];
95+
96+
foreach ($autoconfiguredInstanceofItem->getTags() as $tag => $tagAttributes) {
97+
$tableRows[] = ['Tag', $tag];
98+
if ($tagAttributes !== [[]]) {
99+
$tableRows[] = ['Tag attribute', $this->dumpTagAttribute($tagAttributes)];
100+
}
101+
}
102+
103+
if ($autoconfiguredInstanceofItem->getMethodCalls()) {
104+
$tableRows[] = ['Method call', $this->dumpMethodCall($autoconfiguredInstanceofItem)];
105+
}
106+
107+
if ($autoconfiguredInstanceofItem->getBindings()) {
108+
$tableRows[] = ['Bindings', $this->dumpBindings($autoconfiguredInstanceofItem)];
109+
}
110+
111+
$io->title(sprintf('Autoconfiguration for "%s"', $key));
112+
$io->newLine();
113+
$io->table(['Option', 'Value'], $tableRows);
114+
}
115+
}
116+
117+
private function dumpMethodCall(ChildDefinition $autoconfiguredInstanceofItem)
118+
{
119+
$tagContainerBuilder = new ContainerBuilder();
120+
foreach ($tagContainerBuilder->getServiceIds() as $serviceId) {
121+
$tagContainerBuilder->removeDefinition($serviceId);
122+
$tagContainerBuilder->removeAlias($serviceId);
123+
}
124+
$tagContainerBuilder->addDefinitions([$autoconfiguredInstanceofItem]);
125+
126+
$dumper = new YamlDumper($tagContainerBuilder);
127+
preg_match('/calls\:\n((?: +- .+\n)+)/', $dumper->dump(), $matches);
128+
129+
return preg_replace('/^\s+/m', '', $matches[1]);
130+
}
131+
132+
private function dumpBindings(ChildDefinition $autoconfiguredInstanceofItem)
133+
{
134+
$tagContainerBuilder = new ContainerBuilder();
135+
foreach ($tagContainerBuilder->getServiceIds() as $serviceId) {
136+
$tagContainerBuilder->removeDefinition($serviceId);
137+
$tagContainerBuilder->removeAlias($serviceId);
138+
}
139+
140+
$dumper = new YamlDumper($tagContainerBuilder);
141+
foreach ($autoconfiguredInstanceofItem->getBindings() as $bindingKey => $bindingValue) {
142+
$tagContainerBuilder->setParameter($bindingKey, $bindingValue->getValues()[0]);
143+
}
144+
145+
preg_match('/parameters\:\n((?: + .+\n)+)/', $dumper->dump(), $matches);
146+
147+
return preg_replace('/^\s+/m', '', $matches[1]);
148+
}
149+
150+
private function dumpTagAttribute(array $tagAttribute)
151+
{
152+
$cloner = new VarCloner();
153+
$cliDumper = new CliDumper(null, null, AbstractDumper::DUMP_LIGHT_ARRAY);
154+
155+
return $cliDumper->dump($cloner->cloneVar(current($tagAttribute)), true);
156+
}
157+
158+
private function getContainerBuilder(): ContainerBuilder
159+
{
160+
$kernel = $this->getApplication()->getKernel();
161+
$buildContainer = \Closure::bind(function () { return $this->buildContainer(); }, $kernel, \get_class($kernel));
162+
$container = $buildContainer();
163+
$container->getCompilerPassConfig()->setRemovingPasses([]);
164+
$container->getCompilerPassConfig()->setAfterRemovingPasses([]);
165+
$container->compile();
166+
167+
return $container;
168+
}
169+
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,9 @@ public function load(array $configs, ContainerBuilder $container)
404404
$container->registerForAutoconfiguration(LocaleAwareInterface::class)
405405
->addTag('kernel.locale_aware');
406406
$container->registerForAutoconfiguration(ResetInterface::class)
407-
->addTag('kernel.reset', ['method' => 'reset']);
407+
->addTag('kernel.reset', ['method' => 'reset'])
408+
->addTag('kernel.reset2', ['method' => 'reset2'])
409+
;
408410

409411
if (!interface_exists(MarshallerInterface::class)) {
410412
$container->registerForAutoconfiguration(ResettableInterface::class)

src/Symfony/Bundle/FrameworkBundle/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.debug_autoconfiguration" class="Symfony\Bundle\FrameworkBundle\Command\DebugAutoconfigurationCommand">
74+
<tag name="console.command" comma 10000 nd="debug:autoconfiguration" />
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"/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\DebugAutoconfigurationBundle\Autoconfiguration;
4+
5+
class Bindings
6+
{
7+
private $paramOne;
8+
private $paramTwo;
9+
10+
public function __construct($paramOne, $paramTwo)
11+
{
12+
$this->paramOne = $paramOne;
13+
$this->paramTwo = $paramTwo;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\DebugAutoconfigurationBundle\Autoconfiguration;
4+
5+
class MethodCalls
6+
{
7+
public function setMethodCallOne()
8+
{
9+
}
10+
11+
public function setMethodCallTwo()
12+
{
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\DebugAutoconfigurationBundle\Autoconfiguration;
4+
5+
class TagsAttributes
6+
{
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\DebugAutoconfigurationBundle;
4+
5+
use Symfony\Component\HttpKernel\Bundle\Bundle;
6+
7+
class DebugAutoconfigurationBundle extends Bundle
8+
{
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\DebugAutoconfigurationBundle\DependencyInjection;
4+
5+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\DebugAutoconfigurationBundle\Autoconfiguration\Bindings;
6+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\DebugAutoconfigurationBundle\Autoconfiguration\MethodCalls;
7+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\DebugAutoconfigurationBundle\Autoconfiguration\TagsAttributes;
8+
use Symfony\Component\DependencyInjection\ContainerBuilder;
9+
use Symfony\Component\DependencyInjection\Extension\Extension;
10+
use Symfony\Component\DependencyInjection\Reference;
11+
12+
class DebugAutoconfigurationExtension extends Extension
13+
{
14+
public function load(array $configs, ContainerBuilder $container)
15+
{
16+
$container->registerForAutoconfiguration(MethodCalls::class)
17+
->addMethodCall('setMethodOne', [new Reference('logger')])
18+
->addMethodCall('setMethodTwo', [['paramOne', 'paramOne']]);
19+
20+
$container->registerForAutoconfiguration(Bindings::class)
21+
->setBindings([
22+
'$paramOne' => new Reference('logger'),
23+
'$paramTwo' => 'binding test',
24+
]);
25+
26+
$container->registerForAutoconfiguration(TagsAttributes::class)
27+
->addTag('debugautoconfiguration.tag1', ['method' => 'debug'])
28+
->addTag('debugautoconfiguration.tag2', ['test'])
29+
;
30+
}
31+
}

0 commit comments

Comments
 (0)
0