Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e874ab1

Browse files
author
Florian Pfitzer
committed
[FrameworkBundle], [DependencyInjection] added logging of unused tags during container compilation
1 parent 2293556 commit e874ab1

File tree

6 files changed

+121
-0
lines changed

6 files changed

+121
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
3+
4+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
5+
use Symfony\Component\DependencyInjection\ContainerBuilder;
6+
7+
/**
8+
* Find all service tags which are defined, but not used an yield a warning log message.
9+
*
10+
* @author Florian Pfitzer <[email protected]>
11+
*/
12+
class UnusedTagsPass implements CompilerPassInterface
13+
{
14+
public function process(ContainerBuilder $container)
15+
{
16+
if (!$container->hasParameter('compiler.validate.tags.enabled') || !$container->getParameter('compiler.validate.tags.enabled')) {
17+
return;
18+
}
19+
$unusedTags = $container->findUnusedTags();
20+
$ignore = $container->getParameter('compiler.validate.tags.ignore');
21+
22+
$tags = $container->findTags();
23+
$logger = $container->get('logger');
24+
foreach ($unusedTags as $tag) {
25+
if (in_array($tag, $ignore)) {
26+
continue;
27+
}
28+
// check for typos
29+
$candidates = array();
30+
foreach ($tags as $definedTag) {
31+
if ($definedTag === $tag) {
32+
continue;
33+
}
34+
$lev = levenshtein($tag, $definedTag);
35+
if ($lev <= strlen($tag) / 3 || false !== strpos($definedTag, $tag)) {
36+
$candidates[] = $definedTag;
37+
}
38+
}
39+
40+
$services = array_keys($container->findTaggedServiceIds($tag));
41+
$message = 'Tag "' . $tag . '" was defined on the service(s) ' . implode(",", $services) . ', but was never used.';
42+
if (!empty($candidates)) {
43+
$message .= ' Did you mean "' . implode('", "', $candidates) . '"';
44+
}
45+
$logger->warning($message);
46+
}
47+
}
48+
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public function getConfigTreeBuilder()
9292
$this->addValidationSection($rootNode);
9393
$this->addAnnotationsSection($rootNode);
9494
$this->addSerializerSection($rootNode);
95+
$this->addCompilerSection($rootNode);
9596

9697
return $treeBuilder;
9798
}
@@ -524,4 +525,31 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode)
524525
->end()
525526
;
526527
}
528+
529+
private function addCompilerSection(ArrayNodeDefinition $rootNode)
530+
{
531+
$rootNode
532+
->children()
533+
->arrayNode('compiler')
534+
->info('dependency injection compiler configuration')
535+
->children()
536+
->arrayNode('validate')
537+
->children()
538+
->arrayNode('tags')
539+
->info('validate usage of unknown tags')
540+
->canBeEnabled()
541+
->children()
542+
->arrayNode('ignore')
543+
->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end()
544+
->prototype('scalar')->end()
545+
->end()
546+
->end()
547+
->end()
548+
->end()
549+
->end()
550+
->end()
551+
->end()
552+
->end()
553+
;
554+
}
527555
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ public function load(array $configs, ContainerBuilder $container)
137137
$loader->load('serializer.xml');
138138
}
139139

140+
if (isset($config['compiler'])) {
141+
$this->registerCompilerConfiguration($config['compiler'], $container);
142+
}
143+
140144
$this->addClassesToCompile(array(
141145
'Symfony\\Component\\Config\\FileLocator',
142146

@@ -818,6 +822,12 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde
818822
}
819823
}
820824

825+
private function registerCompilerConfiguration(array $config, ContainerBuilder $container)
826+
{
827+
$container->setParameter('compiler.validate.tags.enabled', $config['validate']['tags']['enabled']);
828+
$container->setParameter('compiler.validate.tags.ignore', $config['validate']['tags']['ignore']);
829+
}
830+
821831
/**
822832
* Loads the security configuration.
823833
*

src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass;
2828
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FragmentRendererPass;
2929
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass;
30+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
3031
use Symfony\Component\DependencyInjection\ContainerBuilder;
3132
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
3233
use Symfony\Component\DependencyInjection\Scope;
@@ -81,6 +82,7 @@ public function build(ContainerBuilder $container)
8182
$container->addCompilerPass(new TranslationDumperPass());
8283
$container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING);
8384
$container->addCompilerPass(new SerializerPass());
85+
$container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING);
8486

8587
if ($container->getParameter('kernel.debug')) {
8688
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
8484
*/
8585
private $expressionLanguage;
8686

87+
/**
88+
* @var array with tag names used by findTaggedServiceIds
89+
*/
90+
private $usedTags = array();
91+
8792
/**
8893
* Sets the track resources flag.
8994
*
@@ -1031,6 +1036,7 @@ public function resolveServices($value)
10311036
*/
10321037
public function findTaggedServiceIds($name)
10331038
{
1039+
$this->usedTags[] = $name;
10341040
$tags = array();
10351041
foreach ($this->getDefinitions() as $id => $definition) {
10361042
if ($definition->hasTag($name)) {
@@ -1056,6 +1062,19 @@ public function findTags()
10561062
return array_unique($tags);
10571063
}
10581064

1065+
/**
1066+
* Returns all tags not queried by findTaggedServiceIds
1067+
*
1068+
* @return array An array of tags
1069+
*/
1070+
public function findUnusedTags()
1071+
{
1072+
$tags = array_values(array_diff($this->findTags(), $this->usedTags));
1073+
array_unique($tags);
1074+
1075+
return $tags;
1076+
}
1077+
10591078
/**
10601079
* Returns the Service Conditionals.
10611080
*

src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,20 @@ public function testfindTaggedServiceIds()
491491
$this->assertEquals(array(), $builder->findTaggedServiceIds('foobar'), '->findTaggedServiceIds() returns an empty array if there is annotated services');
492492
}
493493

494+
public function testfindUnusedTags()
495+
{
496+
$builder = new ContainerBuilder();
497+
$builder
498+
->register('foo', 'Bar\FooClass')
499+
->addTag('kernel.event_listener', array('foo' => 'foo'))
500+
->addTag('kenrel.event_listener', array('bar' => 'bar'))
501+
;
502+
$builder->findTaggedServiceIds('kernel.event_listener');
503+
$this->assertEquals($builder->findUnusedTags(), array(
504+
'kenrel.event_listener'
505+
), '->findUnusedTags() returns an array with unused tags');
506+
}
507+
494508
/**
495509
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::findDefinition
496510
*/

0 commit comments

Comments
 (0)