feature #18482 Created a trait to sort tagged services (iltar)
This PR was squashed before being merged into the 3.2-dev branch (closes #18482).
Discussion
----------
Created a trait to sort tagged services
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | ~
| License | MIT
| Doc PR | ~
When writing the `ControllerArgumentValueResolverPass`, I needed a sorting on `priority`. I ended up copying a method from another class. I noticed this was done more often and 99% of the code was the same. I've moved the most common notation into the trait and "used" the trait in the priority aware passes. This increases horizontal re-use and means people can also use this in their bundles.
The cases that were slightly different, are still working completely. I had to fix some tests because they returned an invalid value from the mocked find method.
Commits
-------
778a70b
Created a trait to sort tagged services
This commit is contained in:
commit
f8dc4593a3
@ -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 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
*/
|
||||
class AddCacheWarmerPass implements CompilerPassInterface
|
||||
{
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -31,20 +33,12 @@ class AddCacheWarmerPass implements CompilerPassInterface
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
*/
|
||||
class ControllerArgumentValueResolverPass implements CompilerPassInterface
|
||||
{
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('argument_resolver')) {
|
||||
@ -32,34 +34,4 @@ class ControllerArgumentValueResolverPass implements CompilerPassInterface
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
*/
|
||||
class PropertyInfoPass implements CompilerPassInterface
|
||||
{
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -45,34 +47,4 @@ class PropertyInfoPass implements CompilerPassInterface
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
*/
|
||||
class SerializerPass implements CompilerPassInterface
|
||||
{
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('serializer')) {
|
||||
@ -31,42 +33,17 @@ class SerializerPass implements CompilerPassInterface
|
||||
|
||||
// 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);
|
||||
if (empty($encoders)) {
|
||||
throw new \RuntimeException('You must tag at least one service as "serializer.encoder" to use the Serializer service');
|
||||
}
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
krsort($sortedServices);
|
||||
|
||||
// Flatten the array
|
||||
return call_user_func_array('array_merge', $sortedServices);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class AddCacheWarmerPassTest extends \PHPUnit_Framework_TestCase
|
||||
$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');
|
||||
|
@ -21,7 +21,7 @@ class ConfigCachePassTest extends \PHPUnit_Framework_TestCase
|
||||
$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 @@ class ConfigCachePassTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testThatCheckersCanBeMissing()
|
||||
{
|
||||
$definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
|
||||
$container = $this->getMock(
|
||||
'Symfony\Component\DependencyInjection\ContainerBuilder',
|
||||
array('findTaggedServiceIds')
|
||||
|
@ -0,0 +1,47 @@
|
||||
<?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\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 <kjarli@gmail.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
<?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\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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user