diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index 6c219e97b3..dc11dc3c82 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -267,6 +267,25 @@ abstract class Descriptor implements DescriptorInterface return $serviceIds; } + protected function sortTaggedServicesByPriority(array $services): array + { + $maxPriority = []; + foreach ($services as $service => $tags) { + $maxPriority[$service] = 0; + foreach ($tags as $tag) { + $currentPriority = $tag['priority'] ?? 0; + if ($maxPriority[$service] < $currentPriority) { + $maxPriority[$service] = $currentPriority; + } + } + } + uasort($maxPriority, function ($a, $b) { + return $b <=> $a; + }); + + return array_keys($maxPriority); + } + /** * Gets class description from a docblock. */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index a7f4f6954f..351a7093c8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -100,7 +100,9 @@ class JsonDescriptor extends Descriptor */ protected function describeContainerServices(ContainerBuilder $builder, array $options = []) { - $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); + $serviceIds = isset($options['tag']) && $options['tag'] + ? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($options['tag'])) + : $this->sortServiceIds($builder->getServiceIds()); $showHidden = isset($options['show_hidden']) && $options['show_hidden']; $omitTags = isset($options['omit_tags']) && $options['omit_tags']; $showArguments = isset($options['show_arguments']) && $options['show_arguments']; @@ -110,7 +112,7 @@ class JsonDescriptor extends Descriptor $serviceIds = array_filter($serviceIds, $options['filter']); } - foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + foreach ($serviceIds as $serviceId) { $service = $this->resolveServiceDefinition($builder, $serviceId); if ($showHidden xor '.' === ($serviceId[0] ?? null)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index 2a58f61d1b..326d656b2a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -132,7 +132,9 @@ class MarkdownDescriptor extends Descriptor } $this->write($title."\n".str_repeat('=', \strlen($title))); - $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); + $serviceIds = isset($options['tag']) && $options['tag'] + ? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($options['tag'])) + : $this->sortServiceIds($builder->getServiceIds()); $showArguments = isset($options['show_arguments']) && $options['show_arguments']; $services = ['definitions' => [], 'aliases' => [], 'services' => []]; @@ -140,7 +142,7 @@ class MarkdownDescriptor extends Descriptor $serviceIds = array_filter($serviceIds, $options['filter']); } - foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + foreach ($serviceIds as $serviceId) { $service = $this->resolveServiceDefinition($builder, $serviceId); if ($showHidden xor '.' === ($serviceId[0] ?? null)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index dfc6f86d5f..c5fd3ed400 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -191,7 +191,9 @@ class TextDescriptor extends Descriptor $options['output']->title($title); - $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); + $serviceIds = isset($options['tag']) && $options['tag'] + ? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($options['tag'])) + : $this->sortServiceIds($builder->getServiceIds()); $maxTags = []; if (isset($options['filter'])) { @@ -230,7 +232,7 @@ class TextDescriptor extends Descriptor $tableHeaders = array_merge(['Service ID'], $tagsNames, ['Class name']); $tableRows = []; $rawOutput = isset($options['raw_text']) && $options['raw_text']; - foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + foreach ($serviceIds as $serviceId) { $definition = $this->resolveServiceDefinition($builder, $serviceId); $styledServiceId = $rawOutput ? $serviceId : sprintf('%s', OutputFormatter::escape($serviceId)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index 0bd2b0252a..874ff9e45a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -289,13 +289,14 @@ class XmlDescriptor extends Descriptor $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($containerXML = $dom->createElement('container')); - $serviceIds = $tag ? array_keys($builder->findTaggedServiceIds($tag)) : $builder->getServiceIds(); - + $serviceIds = $tag + ? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($tag)) + : $this->sortServiceIds($builder->getServiceIds()); if ($filter) { $serviceIds = array_filter($serviceIds, $filter); } - foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + foreach ($serviceIds as $serviceId) { $service = $this->resolveServiceDefinition($builder, $serviceId); if ($showHidden xor '.' === ($serviceId[0] ?? null)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php index 83792e28da..ff4d0484db 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php @@ -287,4 +287,25 @@ abstract class AbstractDescriptorTest extends TestCase return $data; } + + /** @dataProvider getDescribeContainerBuilderWithPriorityTagsTestData */ + public function testDescribeContainerBuilderWithPriorityTags(ContainerBuilder $builder, $expectedDescription, array $options): void + { + $this->assertDescription($expectedDescription, $builder, $options); + } + + public function getDescribeContainerBuilderWithPriorityTagsTestData(): array + { + $variations = ['priority_tag' => ['tag' => 'tag1']]; + $data = []; + foreach (ObjectsProvider::getContainerBuildersWithPriorityTags() as $name => $object) { + foreach ($variations as $suffix => $options) { + $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, $this->getFormat()); + $description = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); + $data[] = [$object, $description, $options, $file]; + } + } + + return $data; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php index 2b8bb34317..84f05c6487 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php @@ -144,6 +144,50 @@ class ObjectsProvider ]; } + public static function getContainerBuildersWithPriorityTags() + { + $builder = new ContainerBuilder(); + $builder->setDefinitions(self::getContainerDefinitionsWithPriorityTags()); + + return ['builder' => $builder]; + } + + public static function getContainerDefinitionsWithPriorityTags() + { + $definition1 = new Definition('Full\\Qualified\\Class1'); + $definition2 = new Definition('Full\\Qualified\\Class2'); + $definition3 = new Definition('Full\\Qualified\\Class3'); + + return [ + 'definition_1' => $definition1 + ->setPublic(true) + ->setSynthetic(true) + ->setFile('/path/to/file') + ->setLazy(false) + ->setAbstract(false) + ->addTag('tag1', ['attr1' => 'val1', 'priority' => 30]) + ->addTag('tag1', ['attr2' => 'val2']) + ->addTag('tag2') + ->addMethodCall('setMailer', [new Reference('mailer')]) + ->setFactory([new Reference('factory.service'), 'get']), + 'definition_2' => $definition2 + ->setPublic(true) + ->setSynthetic(true) + ->setFile('/path/to/file') + ->setLazy(false) + ->setAbstract(false) + ->addTag('tag1', ['attr1' => 'val1', 'attr2' => 'val2', 'priority' => -20]), + 'definition_3' => $definition3 + ->setPublic(true) + ->setSynthetic(true) + ->setFile('/path/to/file') + ->setLazy(false) + ->setAbstract(false) + ->addTag('tag1', ['attr1' => 'val1', 'attr2' => 'val2', 'priority' => 0]) + ->addTag('tag1', ['attr3' => 'val3', 'priority' => 40]), + ]; + } + public static function getContainerAliases() { return [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.json new file mode 100644 index 0000000000..f4c9e8200e --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.json @@ -0,0 +1,90 @@ +{ + "definitions": { + "definition_3": { + "class": "Full\\Qualified\\Class3", + "public": true, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2", + "priority": 0 + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3", + "priority": 40 + } + } + ] + }, + "definition_1": { + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ], + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "priority": 30 + } + }, + { + "name": "tag1", + "parameters": { + "attr2": "val2" + } + }, + { + "name": "tag2", + "parameters": [] + } + ] + }, + "definition_2": { + "class": "Full\\Qualified\\Class2", + "public": true, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2", + "priority": -20 + } + } + ] + } + }, + "aliases": [], + "services": [] +} \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.md new file mode 100644 index 0000000000..0902e2a275 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.md @@ -0,0 +1,61 @@ +Services with tag `tag1` +======================== + +Definitions +----------- + +### definition_3 + +- Class: `Full\Qualified\Class3` +- Public: yes +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 + - Priority: 0 +- Tag: `tag1` + - Attr3: val3 + - Priority: 40 + +### definition_1 + +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` +- Tag: `tag1` + - Attr1: val1 + - Priority: 30 +- Tag: `tag1` + - Attr2: val2 +- Tag: `tag2` + +### definition_2 + +- Class: `Full\Qualified\Class2` +- Public: yes +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 + - Priority: -20 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.txt new file mode 100644 index 0000000000..d010caa978 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.txt @@ -0,0 +1,14 @@ + +Symfony Container Services Tagged with "tag1" Tag +================================================= + + -------------- ------- ------- ---------- ------- ----------------------- +  Service ID   attr1   attr2   priority   attr3   Class name  + -------------- ------- ------- ---------- ------- ----------------------- + definition_3 val1 val2 0 Full\Qualified\Class3 + " 40 val3 + definition_1 val1 30 Full\Qualified\Class1 + " val2 + definition_2 val1 val2 -20 Full\Qualified\Class2 + -------------- ------- ------- ---------- ------- ----------------------- + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.xml new file mode 100644 index 0000000000..2635ea9bb8 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.xml @@ -0,0 +1,41 @@ + + + + + + val1 + val2 + 0 + + + val3 + 40 + + + + + + + + + + + val1 + 30 + + + val2 + + + + + + + + val1 + val2 + -20 + + + +