[FrameworkBundle] [DependencyInjection] added logging of unused tags during container compilation

This commit is contained in:
Florian Pfitzer 2014-10-29 10:15:52 +01:00 committed by Fabien Potencier
parent 156368fa43
commit f51fe4ac41
4 changed files with 119 additions and 0 deletions

View File

@ -0,0 +1,86 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Find all service tags which are defined, but not used and yield a warning log message.
*
* @author Florian Pfitzer <pfitzer@wurzel3.de>
*/
class UnusedTagsPass implements CompilerPassInterface
{
/**
* whitelisted tags
*
* @var array
*/
protected $whitelist = array(
"console.command",
"data_collector",
"form.type",
"form.type_extension",
"form.type_guesser",
"kernel.cache_clearer",
"kernel.cache_warmer",
"kernel.event_listener",
"kernel.event_subscriber",
"kernel.fragment_renderer",
"monolog.logger",
"routing.loader",
"security.remember_me_aware",
"security.voter",
"serializer.encoder",
"templating.helper",
"translation.dumper",
"translation.extractor",
"translation.loader",
"twig.extension",
"twig.loader",
"validator.constraint_validator",
"validator.initializer",
);
public function process(ContainerBuilder $container)
{
$compiler = $container->getCompiler();
$formatter = $compiler->getLoggingFormatter();
$tags = $container->findTags();
$unusedTags = $container->findUnusedTags();
foreach ($unusedTags as $tag) {
// skip whitelisted tags
if (in_array($tag, $this->whitelist)) {
continue;
}
// check for typos
$candidates = array();
foreach ($tags as $definedTag) {
if ($definedTag === $tag) {
continue;
}
if (false !== strpos($definedTag, $tag) || levenshtein($tag, $definedTag) <= strlen($tag) / 3) {
$candidates[] = $definedTag;
}
}
$services = array_keys($container->findTaggedServiceIds($tag));
$message = sprintf('Tag "%s" was defined on the service(s) %s, but was never used.', $tag, implode(',', $services));
if (!empty($candidates)) {
$message .= sprintf(' Did you mean "%s"?', implode('", "', $candidates));
}
$compiler->addLogMessage($formatter->format($this, $message));
}
}
}

View File

@ -28,6 +28,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CompilerDebugDum
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationExtractorPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -89,6 +90,7 @@ class FrameworkBundle extends Bundle
$container->addCompilerPass(new TranslationDumperPass());
$container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new SerializerPass());
$container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING);
if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);

View File

@ -90,6 +90,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
private $expressionLanguageProviders = array();
/**
* @var array with tag names used by findTaggedServiceIds
*/
private $usedTags = array();
/**
* Sets the track resources flag.
*
@ -1064,6 +1069,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
public function findTaggedServiceIds($name)
{
$this->usedTags[] = $name;
$tags = array();
foreach ($this->getDefinitions() as $id => $definition) {
if ($definition->hasTag($name)) {
@ -1089,6 +1095,19 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
return array_unique($tags);
}
/**
* Returns all tags not queried by findTaggedServiceIds
*
* @return array An array of tags
*/
public function findUnusedTags()
{
$tags = array_values(array_diff($this->findTags(), $this->usedTags));
$tags = array_unique($tags);
return $tags;
}
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
{
$this->expressionLanguageProviders[] = $provider;

View File

@ -560,6 +560,18 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(), $builder->findTaggedServiceIds('foobar'), '->findTaggedServiceIds() returns an empty array if there is annotated services');
}
public function testFindUnusedTags()
{
$builder = new ContainerBuilder();
$builder
->register('foo', 'Bar\FooClass')
->addTag('kernel.event_listener', array('foo' => 'foo'))
->addTag('kenrel.event_listener', array('bar' => 'bar'))
;
$builder->findTaggedServiceIds('kernel.event_listener');
$this->assertEquals(array('kenrel.event_listener'), $builder->findUnusedTags(), '->findUnusedTags() returns an array with unused tags');
}
/**
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::findDefinition
*/