feature #33128 [FrameworkBundle] Sort tagged services (krome162504)
This PR was merged into the 4.4 branch.
Discussion
----------
[FrameworkBundle] Sort tagged services
| Q | A
| ------------- | ---
| Branch? | 4.4 <!-- see below -->
| Bug fix? | no
| New feature? | yes <!-- please update src/**/CHANGELOG.md files -->
| BC breaks? | no <!-- see https://symfony.com/bc -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass? | yes <!-- please add some, will be required by reviewers -->
| Fixed tickets | https://github.com/symfony/symfony/issues/32439 <!-- #-prefixed issue number(s), if any -->
| License | MIT
| Doc PR | -
Hi
This PR it's to improve DX when `debug:container` command is use with tag argument by sorting them by priority (More details in linked issue).
Currently they are sort by alphabetical order.
Commits
-------
54cef2a3a3
[FrameworkBundle] Sort tagged service by priority
This commit is contained in:
commit
098584a33c
@ -267,6 +267,25 @@ abstract class Descriptor implements DescriptorInterface
|
|||||||
return $serviceIds;
|
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.
|
* Gets class description from a docblock.
|
||||||
*/
|
*/
|
||||||
|
@ -100,7 +100,9 @@ class JsonDescriptor extends Descriptor
|
|||||||
*/
|
*/
|
||||||
protected function describeContainerServices(ContainerBuilder $builder, array $options = [])
|
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'];
|
$showHidden = isset($options['show_hidden']) && $options['show_hidden'];
|
||||||
$omitTags = isset($options['omit_tags']) && $options['omit_tags'];
|
$omitTags = isset($options['omit_tags']) && $options['omit_tags'];
|
||||||
$showArguments = isset($options['show_arguments']) && $options['show_arguments'];
|
$showArguments = isset($options['show_arguments']) && $options['show_arguments'];
|
||||||
@ -110,7 +112,7 @@ class JsonDescriptor extends Descriptor
|
|||||||
$serviceIds = array_filter($serviceIds, $options['filter']);
|
$serviceIds = array_filter($serviceIds, $options['filter']);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
|
foreach ($serviceIds as $serviceId) {
|
||||||
$service = $this->resolveServiceDefinition($builder, $serviceId);
|
$service = $this->resolveServiceDefinition($builder, $serviceId);
|
||||||
|
|
||||||
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
|
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
|
||||||
|
@ -132,7 +132,9 @@ class MarkdownDescriptor extends Descriptor
|
|||||||
}
|
}
|
||||||
$this->write($title."\n".str_repeat('=', \strlen($title)));
|
$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'];
|
$showArguments = isset($options['show_arguments']) && $options['show_arguments'];
|
||||||
$services = ['definitions' => [], 'aliases' => [], 'services' => []];
|
$services = ['definitions' => [], 'aliases' => [], 'services' => []];
|
||||||
|
|
||||||
@ -140,7 +142,7 @@ class MarkdownDescriptor extends Descriptor
|
|||||||
$serviceIds = array_filter($serviceIds, $options['filter']);
|
$serviceIds = array_filter($serviceIds, $options['filter']);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
|
foreach ($serviceIds as $serviceId) {
|
||||||
$service = $this->resolveServiceDefinition($builder, $serviceId);
|
$service = $this->resolveServiceDefinition($builder, $serviceId);
|
||||||
|
|
||||||
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
|
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
|
||||||
|
@ -191,7 +191,9 @@ class TextDescriptor extends Descriptor
|
|||||||
|
|
||||||
$options['output']->title($title);
|
$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 = [];
|
$maxTags = [];
|
||||||
|
|
||||||
if (isset($options['filter'])) {
|
if (isset($options['filter'])) {
|
||||||
@ -230,7 +232,7 @@ class TextDescriptor extends Descriptor
|
|||||||
$tableHeaders = array_merge(['Service ID'], $tagsNames, ['Class name']);
|
$tableHeaders = array_merge(['Service ID'], $tagsNames, ['Class name']);
|
||||||
$tableRows = [];
|
$tableRows = [];
|
||||||
$rawOutput = isset($options['raw_text']) && $options['raw_text'];
|
$rawOutput = isset($options['raw_text']) && $options['raw_text'];
|
||||||
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
|
foreach ($serviceIds as $serviceId) {
|
||||||
$definition = $this->resolveServiceDefinition($builder, $serviceId);
|
$definition = $this->resolveServiceDefinition($builder, $serviceId);
|
||||||
|
|
||||||
$styledServiceId = $rawOutput ? $serviceId : sprintf('<fg=cyan>%s</fg=cyan>', OutputFormatter::escape($serviceId));
|
$styledServiceId = $rawOutput ? $serviceId : sprintf('<fg=cyan>%s</fg=cyan>', OutputFormatter::escape($serviceId));
|
||||||
|
@ -289,13 +289,14 @@ class XmlDescriptor extends Descriptor
|
|||||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||||
$dom->appendChild($containerXML = $dom->createElement('container'));
|
$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) {
|
if ($filter) {
|
||||||
$serviceIds = array_filter($serviceIds, $filter);
|
$serviceIds = array_filter($serviceIds, $filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
|
foreach ($serviceIds as $serviceId) {
|
||||||
$service = $this->resolveServiceDefinition($builder, $serviceId);
|
$service = $this->resolveServiceDefinition($builder, $serviceId);
|
||||||
|
|
||||||
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
|
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
|
||||||
|
@ -287,4 +287,25 @@ abstract class AbstractDescriptorTest extends TestCase
|
|||||||
|
|
||||||
return $data;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
public static function getContainerAliases()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
@ -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": []
|
||||||
|
}
|
@ -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
|
@ -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
|
||||||
|
-------------- ------- ------- ---------- ------- -----------------------
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<container>
|
||||||
|
<definition id="definition_3" class="Full\Qualified\Class3" public="true" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
|
||||||
|
<tags>
|
||||||
|
<tag name="tag1">
|
||||||
|
<parameter name="attr1">val1</parameter>
|
||||||
|
<parameter name="attr2">val2</parameter>
|
||||||
|
<parameter name="priority">0</parameter>
|
||||||
|
</tag>
|
||||||
|
<tag name="tag1">
|
||||||
|
<parameter name="attr3">val3</parameter>
|
||||||
|
<parameter name="priority">40</parameter>
|
||||||
|
</tag>
|
||||||
|
</tags>
|
||||||
|
</definition>
|
||||||
|
<definition id="definition_1" class="Full\Qualified\Class1" public="true" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
|
||||||
|
<factory service="factory.service" method="get"/>
|
||||||
|
<calls>
|
||||||
|
<call method="setMailer"/>
|
||||||
|
</calls>
|
||||||
|
<tags>
|
||||||
|
<tag name="tag1">
|
||||||
|
<parameter name="attr1">val1</parameter>
|
||||||
|
<parameter name="priority">30</parameter>
|
||||||
|
</tag>
|
||||||
|
<tag name="tag1">
|
||||||
|
<parameter name="attr2">val2</parameter>
|
||||||
|
</tag>
|
||||||
|
<tag name="tag2"/>
|
||||||
|
</tags>
|
||||||
|
</definition>
|
||||||
|
<definition id="definition_2" class="Full\Qualified\Class2" public="true" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
|
||||||
|
<tags>
|
||||||
|
<tag name="tag1">
|
||||||
|
<parameter name="attr1">val1</parameter>
|
||||||
|
<parameter name="attr2">val2</parameter>
|
||||||
|
<parameter name="priority">-20</parameter>
|
||||||
|
</tag>
|
||||||
|
</tags>
|
||||||
|
</definition>
|
||||||
|
</container>
|
Reference in New Issue
Block a user