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;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
@ -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)) {
|
||||
|
@ -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)) {
|
||||
|
@ -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('<fg=cyan>%s</fg=cyan>', OutputFormatter::escape($serviceId));
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 [
|
||||
|
@ -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