feature #15963 added logging of unused tags (Marmelatze, fabpot)
This PR was merged into the 2.8 branch. Discussion ---------- added logging of unused tags | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #11511, #11744 | License | MIT | Doc PR | - This is the same as #11744 but with some minor tweaks and some unit tests for the compiler pass. Commits -------95c9f50
added some testsd3271e1
missing tags in whitelistf51fe4a
[FrameworkBundle] [DependencyInjection] added logging of unused tags during container compilation
This commit is contained in:
commit
8b8e7bb281
@ -0,0 +1,87 @@
|
||||
<?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
|
||||
{
|
||||
private $whitelist = array(
|
||||
'console.command',
|
||||
'config_cache.resource_checker',
|
||||
'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.expression_language_provider',
|
||||
'routing.loader',
|
||||
'security.expression_language_provider',
|
||||
'security.remember_me_aware',
|
||||
'security.voter',
|
||||
'serializer.encoder',
|
||||
'serializer.normalizer',
|
||||
'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 = array_unique(array_merge($container->findTags(), $this->whitelist));
|
||||
|
||||
foreach ($container->findUnusedTags() 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 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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
@ -91,6 +92,7 @@ class FrameworkBundle extends Bundle
|
||||
$container->addCompilerPass(new SerializerPass());
|
||||
|
||||
if ($container->getParameter('kernel.debug')) {
|
||||
$container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
$container->addCompilerPass(new CompilerDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
$container->addCompilerPass(new ConfigCachePass());
|
||||
|
@ -0,0 +1,52 @@
|
||||
<?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\Tests\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
|
||||
|
||||
class UnusedTagsPassTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
$pass = new UnusedTagsPass();
|
||||
|
||||
$formatter = $this->getMock('Symfony\Component\DependencyInjection\Compiler\LoggingFormatter');
|
||||
$formatter
|
||||
->expects($this->at(0))
|
||||
->method('format')
|
||||
->with($pass, 'Tag "kenrel.event_subscriber" was defined on service(s) "foo", "bar", but was never used. Did you mean "kernel.event_subscriber"?')
|
||||
;
|
||||
|
||||
$compiler = $this->getMock('Symfony\Component\DependencyInjection\Compiler\Compiler');
|
||||
$compiler->expects($this->once())->method('getLoggingFormatter')->will($this->returnValue($formatter));
|
||||
|
||||
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder',
|
||||
array('findTaggedServiceIds', 'getCompiler', 'findUnusedTags', 'findTags')
|
||||
);
|
||||
$container->expects($this->once())->method('getCompiler')->will($this->returnValue($compiler));
|
||||
$container->expects($this->once())
|
||||
->method('findTags')
|
||||
->will($this->returnValue(array('kenrel.event_subscriber')));
|
||||
$container->expects($this->once())
|
||||
->method('findUnusedTags')
|
||||
->will($this->returnValue(array('kenrel.event_subscriber', 'form.type')));
|
||||
$container->expects($this->once())
|
||||
->method('findTaggedServiceIds')
|
||||
->with('kenrel.event_subscriber')
|
||||
->will($this->returnValue(array(
|
||||
'foo' => array(),
|
||||
'bar' => array(),
|
||||
)));
|
||||
|
||||
$pass->process($container);
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ class LoggingFormatter
|
||||
{
|
||||
public function formatRemoveService(CompilerPassInterface $pass, $id, $reason)
|
||||
{
|
||||
return $this->format($pass, sprintf('Removed service "%s"; reason: %s', $id, $reason));
|
||||
return $this->format($pass, sprintf('Removed service "%s"; reason: %s.', $id, $reason));
|
||||
}
|
||||
|
||||
public function formatInlineService(CompilerPassInterface $pass, $id, $target)
|
||||
|
@ -90,6 +90,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
*/
|
||||
private $expressionLanguageProviders = array();
|
||||
|
||||
/**
|
||||
* @var string[] 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,16 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
return array_unique($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all tags not queried by findTaggedServiceIds.
|
||||
*
|
||||
* @return string[] An array of tags
|
||||
*/
|
||||
public function findUnusedTags()
|
||||
{
|
||||
return array_values(array_diff($this->findTags(), $this->usedTags));
|
||||
}
|
||||
|
||||
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
|
||||
{
|
||||
$this->expressionLanguageProviders[] = $provider;
|
||||
|
@ -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
|
||||
*/
|
||||
|
Reference in New Issue
Block a user