From 778a70ba9842ebbc081e0c82a3f46cb2d3f385c5 Mon Sep 17 00:00:00 2001 From: Iltar van der Berg Date: Fri, 8 Apr 2016 15:51:59 +0200 Subject: [PATCH] Created a trait to sort tagged services --- .../Compiler/AddCacheWarmerPass.php | 14 +-- .../Compiler/ConfigCachePass.php | 15 +--- .../ControllerArgumentValueResolverPass.php | 34 +------- .../Compiler/PropertyInfoPass.php | 34 +------- .../Compiler/SerializerPass.php | 43 +++------- .../Compiler/AddCacheWarmerPassTest.php | 2 +- .../Compiler/ConfigCachePassTest.php | 3 +- .../Compiler/PriorityTaggedServiceTrait.php | 47 ++++++++++ .../Compiler/PriorityTaggedServiceTrait.php | 85 +++++++++++++++++++ 9 files changed, 158 insertions(+), 119 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTrait.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php index 5fe2f6202086..8f5353121ce8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php @@ -11,9 +11,9 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Reference; /** * Registers the cache warmers. @@ -22,6 +22,8 @@ */ class AddCacheWarmerPass implements CompilerPassInterface { + use PriorityTaggedServiceTrait; + /** * {@inheritdoc} */ @@ -31,20 +33,12 @@ public function process(ContainerBuilder $container) return; } - $warmers = array(); - foreach ($container->findTaggedServiceIds('kernel.cache_warmer') as $id => $attributes) { - $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; - $warmers[$priority][] = new Reference($id); - } + $warmers = $this->findAndSortTaggedServices('kernel.cache_warmer', $container); if (empty($warmers)) { return; } - // sort by priority and flatten - krsort($warmers); - $warmers = call_user_func_array('array_merge', $warmers); - $container->getDefinition('cache_warmer')->replaceArgument(0, $warmers); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php index a8e1c549a2d6..d73e941f427b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php @@ -12,8 +12,8 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; /** * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority. @@ -23,23 +23,16 @@ */ class ConfigCachePass implements CompilerPassInterface { + use PriorityTaggedServiceTrait; + public function process(ContainerBuilder $container) { - $resourceCheckers = array(); - - foreach ($container->findTaggedServiceIds('config_cache.resource_checker') as $id => $tags) { - $priority = isset($tags[0]['priority']) ? $tags[0]['priority'] : 0; - $resourceCheckers[$priority][] = new Reference($id); - } + $resourceCheckers = $this->findAndSortTaggedServices('config_cache.resource_checker', $container); if (empty($resourceCheckers)) { return; } - // sort by priority and flatten - krsort($resourceCheckers); - $resourceCheckers = call_user_func_array('array_merge', $resourceCheckers); - $container->getDefinition('config_cache_factory')->replaceArgument(0, $resourceCheckers); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php index f4b64a347891..e087e691440f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php @@ -12,8 +12,8 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; /** * Gathers and configures the argument value resolvers. @@ -22,6 +22,8 @@ */ class ControllerArgumentValueResolverPass implements CompilerPassInterface { + use PriorityTaggedServiceTrait; + public function process(ContainerBuilder $container) { if (!$container->hasDefinition('argument_resolver')) { @@ -32,34 +34,4 @@ public function process(ContainerBuilder $container) $argumentResolvers = $this->findAndSortTaggedServices('controller.argument_value_resolver', $container); $definition->replaceArgument(1, $argumentResolvers); } - - /** - * Finds all services with the given tag name and order them by their priority. - * - * @param string $tagName - * @param ContainerBuilder $container - * - * @return array - */ - private function findAndSortTaggedServices($tagName, ContainerBuilder $container) - { - $services = $container->findTaggedServiceIds($tagName); - - $sortedServices = array(); - foreach ($services as $serviceId => $tags) { - foreach ($tags as $attributes) { - $priority = isset($attributes['priority']) ? $attributes['priority'] : 0; - $sortedServices[$priority][] = new Reference($serviceId); - } - } - - if (empty($sortedServices)) { - return array(); - } - - krsort($sortedServices); - - // Flatten the array - return call_user_func_array('array_merge', $sortedServices); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php index f2a92021e928..f05445f1a627 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php @@ -12,8 +12,8 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; /** * Adds extractors to the property_info service. @@ -22,6 +22,8 @@ */ class PropertyInfoPass implements CompilerPassInterface { + use PriorityTaggedServiceTrait; + /** * {@inheritdoc} */ @@ -45,34 +47,4 @@ public function process(ContainerBuilder $container) $accessExtractors = $this->findAndSortTaggedServices('property_info.access_extractor', $container); $definition->replaceArgument(3, $accessExtractors); } - - /** - * Finds all services with the given tag name and order them by their priority. - * - * @param string $tagName - * @param ContainerBuilder $container - * - * @return array - */ - private function findAndSortTaggedServices($tagName, ContainerBuilder $container) - { - $services = $container->findTaggedServiceIds($tagName); - - $sortedServices = array(); - foreach ($services as $serviceId => $tags) { - foreach ($tags as $attributes) { - $priority = isset($attributes['priority']) ? $attributes['priority'] : 0; - $sortedServices[$priority][] = new Reference($serviceId); - } - } - - if (empty($sortedServices)) { - return array(); - } - - krsort($sortedServices); - - // Flatten the array - return call_user_func_array('array_merge', $sortedServices); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php index 6188feab553e..c7d5cb40a59f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php @@ -11,9 +11,9 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Reference; /** * Adds all services with the tags "serializer.encoder" and "serializer.normalizer" as @@ -23,6 +23,8 @@ */ class SerializerPass implements CompilerPassInterface { + use PriorityTaggedServiceTrait; + public function process(ContainerBuilder $container) { if (!$container->hasDefinition('serializer')) { @@ -31,42 +33,17 @@ public function process(ContainerBuilder $container) // Looks for all the services tagged "serializer.normalizer" and adds them to the Serializer service $normalizers = $this->findAndSortTaggedServices('serializer.normalizer', $container); + + if (empty($normalizers)) { + throw new \RuntimeException('You must tag at least one service as "serializer.normalizer" to use the Serializer service'); + } $container->getDefinition('serializer')->replaceArgument(0, $normalizers); // Looks for all the services tagged "serializer.encoders" and adds them to the Serializer service $encoders = $this->findAndSortTaggedServices('serializer.encoder', $container); - $container->getDefinition('serializer')->replaceArgument(1, $encoders); - } - - /** - * Finds all services with the given tag name and order them by their priority. - * - * @param string $tagName - * @param ContainerBuilder $container - * - * @return array - * - * @throws \RuntimeException - */ - private function findAndSortTaggedServices($tagName, ContainerBuilder $container) - { - $services = $container->findTaggedServiceIds($tagName); - - if (empty($services)) { - throw new \RuntimeException(sprintf('You must tag at least one service as "%s" to use the Serializer service', $tagName)); - } - - $sortedServices = array(); - foreach ($services as $serviceId => $tags) { - foreach ($tags as $attributes) { - $priority = isset($attributes['priority']) ? $attributes['priority'] : 0; - $sortedServices[$priority][] = new Reference($serviceId); - } + if (empty($encoders)) { + throw new \RuntimeException('You must tag at least one service as "serializer.encoder" to use the Serializer service'); } - - krsort($sortedServices); - - // Flatten the array - return call_user_func_array('array_merge', $sortedServices); + $container->getDefinition('serializer')->replaceArgument(1, $encoders); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php index 61d31cb86b7b..204fdf09b653 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php @@ -21,7 +21,7 @@ public function testThatCacheWarmersAreProcessedInPriorityOrder() $services = array( 'my_cache_warmer_service1' => array(0 => array('priority' => 100)), 'my_cache_warmer_service2' => array(0 => array('priority' => 200)), - 'my_cache_warmer_service3' => array(), + 'my_cache_warmer_service3' => array(0 => array()), ); $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php index c0eef3d627ce..48c753b6e2d4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php @@ -21,7 +21,7 @@ public function testThatCheckersAreProcessedInPriorityOrder() $services = array( 'checker_2' => array(0 => array('priority' => 100)), 'checker_1' => array(0 => array('priority' => 200)), - 'checker_3' => array(), + 'checker_3' => array(0 => array()), ); $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); @@ -52,7 +52,6 @@ public function testThatCheckersAreProcessedInPriorityOrder() public function testThatCheckersCanBeMissing() { - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); $container = $this->getMock( 'Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds') diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php new file mode 100644 index 000000000000..74c6d1958186 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Trait that allows a generic method to find and sort service by priority option in the tag. + * + * @author Iltar van der Berg + */ +trait PriorityTaggedServiceTrait +{ + /** + * Finds all services with the given tag name and order them by their priority. + * + * @param string $tagName + * @param ContainerBuilder $container + * + * @return Reference[] + */ + private function findAndSortTaggedServices($tagName, ContainerBuilder $container) + { + $services = $container->findTaggedServiceIds($tagName); + + $queue = new \SplPriorityQueue(); + + foreach ($services as $serviceId => $tags) { + foreach ($tags as $attributes) { + $priority = isset($attributes['priority']) ? $attributes['priority'] : 0; + $queue->insert(new Reference($serviceId), $priority * -1); + } + } + + return iterator_to_array($queue); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTrait.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTrait.php new file mode 100644 index 000000000000..cb877d7a6c44 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTrait.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class PriorityTaggedServiceTraitTest extends \PHPUnit_Framework_TestCase +{ + public function testThatCacheWarmersAreProcessedInPriorityOrder() + { + $services = array( + 'my_service1' => array(array('priority' => 100)), + 'my_service2' => array(array('priority' => 200)), + 'my_service3' => array(array('priority' => -500)), + 'my_service4' => array(array()), + 'my_service5' => array(array()), + 'my_service6' => array(array('priority' => -500)), + 'my_service7' => array(array('priority' => -499)), + 'my_service8' => array(array('priority' => 1)), + 'my_service9' => array(array()), + 'my_service10' => array(array('priority' => -1000)), + 'my_service11' => array(array('priority' => -1000)), + 'my_service12' => array(array('priority' => -1000)), + 'my_service13' => array(array('priority' => -1000)), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $container = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('findTaggedServiceIds', 'getDefinition', 'hasDefinition') + ); + + $container + ->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + $container + ->expects($this->atLeastOnce()) + ->method('getDefinition') + ->with('my_custom_tag') + ->will($this->returnValue($definition)); + + $definition + ->expects($this->once()) + ->method('replaceArgument') + ->with(0, array( + new Reference('my_service2'), + new Reference('my_service1'), + new Reference('my_service8'), + new Reference('my_service4'), + new Reference('my_service5'), + new Reference('my_service9'), + new Reference('my_service7'), + new Reference('my_service3'), + new Reference('my_service6'), + new Reference('my_service10'), + new Reference('my_service11'), + new Reference('my_service12'), + new Reference('my_service13'), + )); + + (new PriorityTaggedServiceTraitImplementation())->test('my_custom_tag', $container); + } +} + +class PriorityTaggedServiceTraitImplementation +{ + use PriorityTaggedServiceTrait; + + public function test($tagName, ContainerBuilder $container) + { + return $this->findAndSortTaggedServices($tagName, $container); + } +}