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 @@
+
+[33mSymfony Container Services Tagged with "tag1" Tag[39m
+[33m=================================================[39m
+
+ -------------- ------- ------- ---------- ------- -----------------------
+ [32m Service ID [39m [32m attr1 [39m [32m attr2 [39m [32m priority [39m [32m attr3 [39m [32m Class name [39m
+ -------------- ------- ------- ---------- ------- -----------------------
+ 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
+
+
+
+