-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DependencyInjection] Fix ServiceLocatorTagPass
indexes handling
#60691
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Can you add a test covering the case that broke, to prevent regressions ? |
Added |
src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php
Show resolved
Hide resolved
I think I figured out a better fix. Can you please have a look and update the PR + tests potentially? diff --git a/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php b/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php
index 2e649bdeaa..e0a1fd3610 100644
--- a/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php
+++ b/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php
@@ -21,7 +21,7 @@ class AsTaggedItem
{
/**
* @param string|null $index The property or method to use to index the item in the locator
- * @param int|null $priority The priority of the item; the higher the number, the earlier the tagged service will be located in the locator
+ * @param int|null $priority The priority of the item; the higher the number, the earlier the tagged service will be located in the iterator/locator
*/
public function __construct(
public ?string $index = null,
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php
index 77a1d7ef8f..e3a4eba275 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php
@@ -87,8 +87,7 @@ trait PriorityTaggedServiceTrait
if (null === $index && null === $defaultIndex && $defaultPriorityMethod && $class) {
$defaultIndex = PriorityTaggedServiceUtil::getDefault($container, $serviceId, $class, $defaultIndexMethod ?? 'getDefaultName', $tagName, $indexAttribute, $checkTaggedItem);
}
- $decorated = $definition->getTag('container.decorator')[0]['id'] ?? null;
- $index = $index ?? $defaultIndex ?? $defaultIndex = $decorated ?? $serviceId;
+ $index ??= $defaultIndex ??= $definition->getTag('container.decorator')[0]['id'] ?? $serviceId;
$services[] = [$priority, ++$i, $index, $serviceId, $class];
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php
index 81c14ac5cc..6de3e5ed4d 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php
@@ -54,17 +54,41 @@ final class ServiceLocatorTagPass extends AbstractRecursivePass
$value->setClass(ServiceLocator::class);
}
- $services = $value->getArguments()[0] ?? null;
+ $values = $value->getArguments()[0] ?? null;
+ $services = [];
- if ($services instanceof TaggedIteratorArgument) {
- $services = $this->findAndSortTaggedServices($services, $this->container);
- }
-
- if (!\is_array($services)) {
+ if ($values instanceof TaggedIteratorArgument) {
+ foreach ($this->findAndSortTaggedServices($values, $this->container) as $k => $v) {
+ $services[$k] = new ServiceClosureArgument($v);
+ }
+ } elseif (!\is_array($values)) {
throw new InvalidArgumentException(\sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set.', $this->currentId));
+ } else {
+ $i = 0;
+
+ foreach ($values as $k => $v) {
+ if ($v instanceof ServiceClosureArgument) {
+ $services[$k] = $v;
+ continue;
+ }
+
+ if ($i === $k) {
+ if ($v instanceof Reference) {
+ $k = (string) $v;
+ }
+ ++$i;
+ } elseif (\is_int($k)) {
+ $i = null;
+ }
+
+ $services[$k] = new ServiceClosureArgument($v);
+ }
+ if (0 === $i) {
+ ksort($services);
+ }
}
- $value->setArgument(0, self::map($services));
+ $value->setArgument(0, $services);
$id = '.service_locator.'.ContainerBuilder::hash($value);
@@ -83,8 +107,12 @@ final class ServiceLocatorTagPass extends AbstractRecursivePass
public static function register(ContainerBuilder $container, array $map, ?string $callerId = null): Reference
{
+ foreach ($map as $k => $v) {
+ $map[$k] = new ServiceClosureArgument($v);
+ }
+
$locator = (new Definition(ServiceLocator::class))
- ->addArgument(self::map($map))
+ ->addArgument($map)
->addTag('container.service_locator');
if (null !== $callerId && $container->hasDefinition($callerId)) {
@@ -109,29 +137,4 @@ final class ServiceLocatorTagPass extends AbstractRecursivePass
return new Reference($id);
}
-
- public static function map(array $services): array
- {
- $i = 0;
-
- foreach ($services as $k => $v) {
- if ($v instanceof ServiceClosureArgument) {
- continue;
- }
-
- if ($i === $k) {
- if ($v instanceof Reference) {
- unset($services[$k]);
- $k = (string) $v;
- }
- ++$i;
- } elseif (\is_int($k)) {
- $i = null;
- }
-
- $services[$k] = new ServiceClosureArgument($v);
- }
-
- return $services;
- }
} |
It seems the only change is the if (0 === $i) {
ksort($services);
} bit? It doesn’t affect the tests though. |
There's also the foreach ($this->findAndSortTaggedServices being unprocessed in case the args are TaggedIteratorArgument (which means we respect the order managed by this call) |
Will add tests if needed. Not sure what “aggregating locators” means however? |
if one service defines it needs foo and bar, and another needs bar and foo |
ServiceLocatorTagPass::register
ServiceLocatorTagPass
indexes handling
Just noticed |
|
Thank you @MatTheCat. |
What about |
It'd say yes. Such lists of services are weird anyway... |
#50578 changed the
ServiceLocatorTagPass
’ behavior so that if the map is a list, its indexes always are replaced by the corresponding reference ID, even if they come from thePriorityTaggedServiceTrait
. That means if your service map ends up as a list because e.g. yourdefault_index_method
returned contiguous integer, its indexes will be replaced.This PR reverts this change so that it works the same than v6.