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

Skip to content

Commit 5e103e2

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

File tree

7 files changed

+155
-0
lines changed

7 files changed

+155
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
17+
/**
18+
* Find all service tags which are defined, but not used an yield a warning log message.
19+
*
20+
* @author Florian Pfitzer <[email protected]>
21+
*/
22+
class UnusedTagsPass implements CompilerPassInterface
23+
{
24+
25+
public function process(ContainerBuilder $container)
26+
{
27+
if (!$container->hasParameter('compiler.validate.ignore_unused_tag.enabled') || !$container->getParameter('compiler.validate.ignore_unused_tag.enabled')) {
28+
return;
29+
}
30+
$compiler = $container->getCompiler();
31+
$formatter = $compiler->getLoggingFormatter();
32+
33+
$unusedTags = $container->findUnusedTags();
34+
$ignore = $container->getParameter('compiler.validate.ignore_unused_tag.ignore');
35+
36+
$tags = $container->findTags();
37+
foreach ($unusedTags as $tag) {
38+
if (in_array($tag, $ignore)) {
39+
continue;
40+
}
41+
// check for typos
42+
$candidates = array();
43+
foreach ($tags as $definedTag) {
44+
if ($definedTag === $tag) {
45+
continue;
46+
}
47+
if (false !== strpos($definedTag, $tag) || levenshtein($tag, $definedTag) <= strlen($tag) / 3) {
48+
$candidates[] = $definedTag;
49+
}
50+
}
51+
52+
$services = array_keys($container->findTaggedServiceIds($tag));
53+
$message = 'Tag "'.$tag.'" was defined on the service(s) '.implode(",", $services).', but was never used.';
54+
if (!empty($candidates)) {
55+
$message .= ' Did you mean "'.implode('", "', $candidates).'"?';
56+
}
57+
$compiler->addLogMessage($formatter->format($this, $message));
58+
}
59+
}
60+
}

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

Lines changed: 30 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,33 @@ 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+
->addDefaultsIfNotSet()
538+
->children()
539+
->arrayNode('ignore_unused_tag')
540+
->info('validate usage of unknown tags')
541+
->canBeEnabled()
542+
->children()
543+
->arrayNode('ignore')
544+
->fixXmlConfig('ignore')
545+
->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end()
546+
->prototype('scalar')->end()
547+
->end()
548+
->end()
549+
->end()
550+
->end()
551+
->end()
552+
->end()
553+
->end()
554+
->end()
555+
;
556+
}
527557
}

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.ignore_unused_tag.enabled', $config['validate']['ignore_unused_tag']['enabled']);
828+
$container->setParameter('compiler.validate.ignore_unused_tag.ignore', $config['validate']['ignore_unused_tag']['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/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<xsd:element name="translator" type="translator" minOccurs="0" maxOccurs="1" />
3131
<xsd:element name="validation" type="validation" minOccurs="0" maxOccurs="1" />
3232
<xsd:element name="annotations" type="annotations" minOccurs="0" maxOccurs="1" />
33+
<xsd:element name="compiler" type="compiler" minOccurs="0" maxOccurs="1" />
3334
</xsd:all>
3435

3536
<xsd:attribute name="http-method-override" type="xsd:boolean" />
@@ -177,4 +178,23 @@
177178
<xsd:attribute name="debug" type="xsd:string" />
178179
<xsd:attribute name="file-cache-dir" type="xsd:string" />
179180
</xsd:complexType>
181+
182+
<xsd:complexType name="compiler">
183+
<xsd:sequence>
184+
<xsd:element name="validate" type="compiler_validate" minOccurs="0" maxOccurs="1" />
185+
</xsd:sequence>
186+
</xsd:complexType>
187+
188+
<xsd:complexType name="compiler_validate">
189+
<xsd:sequence>
190+
<xsd:element name="ignore_unused_tag" type="ignore_unused_tags" minOccurs="0" maxOccurs="1" />
191+
</xsd:sequence>
192+
</xsd:complexType>
193+
194+
<xsd:complexType name="ignore_unused_tags">
195+
<xsd:sequence>
196+
<xsd:element name="ignore" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
197+
</xsd:sequence>
198+
<xsd:attribute name="enabled" type="xsd:boolean" />
199+
</xsd:complexType>
180200
</xsd:schema>

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)