From 064bee15c91359c316dfba3af2d4f24dd7ab39cb Mon Sep 17 00:00:00 2001 From: Sergey Rabochiy Date: Thu, 15 Dec 2022 00:45:11 +0700 Subject: [PATCH] Fix IDs in ServiceLocator --- .../Compiler/ServiceLocatorTagPass.php | 48 ++++++++++++----- .../Compiler/ServiceLocatorTagPassTest.php | 53 +++++++++++++------ 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php index 67103a78522d3..1babc8f90381d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php @@ -37,7 +37,7 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed $value->setValues($this->findAndSortTaggedServices($value->getTaggedIteratorArgument(), $this->container)); } - return self::register($this->container, $value->getValues()); + return self::register($this->container, $this->replaceNumericServiceLocatorKeys($value->getValues())); } if ($value instanceof Definition) { @@ -62,26 +62,15 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed 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)); } - $i = 0; + $services = $this->replaceNumericServiceLocatorKeys($services); 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); } - ksort($services); $value->setArgument(0, $services); @@ -100,6 +89,39 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed return new Reference($id); } + /** + * Replaces numeric service IDs with service names. + * + * @param array $services + * + * @return array + */ + private function replaceNumericServiceLocatorKeys(array $services): array + { + $i = 0; + + foreach ($services as $k => $v) { + if ($i === $k && $v instanceof Reference) { + unset($services[$k]); + $k = (string) $v; + ++$i; + } elseif (\is_int($k)) { + /** + * Not consecutive numbers, so stop replacing names but continue the loop. + * + * @see ServiceLocatorTagPassTest::testInheritedKeyOverwritesPreviousServiceWithKey + */ + $i = null; + } + + $services[$k] = $v; + } + + ksort($services); + + return $services; + } + public static function register(ContainerBuilder $container, array $map, string $callerId = null): Reference { foreach ($map as $k => $v) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php index 7c787b0f5a263..d5b0214436cee 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -21,7 +21,6 @@ use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; -use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1; use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition2; @@ -62,8 +61,8 @@ public function testProcessValue() { $container = new ContainerBuilder(); - $container->register('bar', CustomDefinition::class); - $container->register('baz', CustomDefinition::class); + $container->register('bar', TestDefinition1::class); + $container->register('baz', TestDefinition2::class); $container->register('foo', ServiceLocator::class) ->setArguments([[ @@ -79,9 +78,33 @@ public function testProcessValue() /** @var ServiceLocator $locator */ $locator = $container->get('foo'); - $this->assertSame(CustomDefinition::class, $locator('bar')::class); - $this->assertSame(CustomDefinition::class, $locator('baz')::class); - $this->assertSame(CustomDefinition::class, $locator('some.service')::class); + $this->assertInstanceOf(TestDefinition1::class, $locator('bar')); + $this->assertInstanceOf(TestDefinition2::class, $locator('baz')); + $this->assertInstanceOf(TestDefinition1::class, $locator('some.service')); + } + + public function testProcessLocatorArgumentValue() + { + $container = new ContainerBuilder(); + + $container->register('bar', TestDefinition1::class); + $container->register('baz', TestDefinition2::class); + + $container->register('foo', Locator::class) + ->addArgument(new ServiceLocatorArgument([ + new Reference('bar'), + 'some.service' => new Reference('bar'), + new Reference('baz'), + ])); + + (new ServiceLocatorTagPass())->process($container); + + /** @var ServiceLocator $locator */ + $locator = $container->get('foo')->locator; + + $this->assertInstanceOf(TestDefinition1::class, $locator('bar')); + $this->assertInstanceOf(TestDefinition1::class, $locator('some.service')); + $this->assertInstanceOf(TestDefinition2::class, $locator('baz')); } public function testServiceWithKeyOverwritesPreviousInheritedKey() @@ -104,7 +127,7 @@ public function testServiceWithKeyOverwritesPreviousInheritedKey() /** @var ServiceLocator $locator */ $locator = $container->get('foo'); - $this->assertSame(TestDefinition2::class, $locator('bar')::class); + $this->assertInstanceOf(TestDefinition2::class, $locator('bar')); } public function testInheritedKeyOverwritesPreviousServiceWithKey() @@ -128,8 +151,8 @@ public function testInheritedKeyOverwritesPreviousServiceWithKey() /** @var ServiceLocator $locator */ $locator = $container->get('foo'); - $this->assertSame(TestDefinition1::class, $locator('bar')::class); - $this->assertSame(TestDefinition2::class, $locator(16)::class); + $this->assertInstanceOf(TestDefinition1::class, $locator('bar')); + $this->assertInstanceOf(TestDefinition2::class, $locator(16)); } public function testBindingsAreCopied() @@ -164,8 +187,8 @@ public function testTaggedServices() /** @var ServiceLocator $locator */ $locator = $container->get('foo'); - $this->assertSame(TestDefinition1::class, $locator('bar')::class); - $this->assertSame(TestDefinition2::class, $locator('baz')::class); + $this->assertInstanceOf(TestDefinition1::class, $locator('bar')); + $this->assertInstanceOf(TestDefinition2::class, $locator('baz')); } public function testIndexedByServiceIdWithDecoration() @@ -184,19 +207,19 @@ public function testIndexedByServiceIdWithDecoration() $container->setDefinition(Service::class, $service); - $decorated = new Definition(Decorated::class); + $decorated = new Definition(DecoratedService::class); $decorated->setPublic(true); $decorated->setDecoratedService(Service::class); - $container->setDefinition(Decorated::class, $decorated); + $container->setDefinition(DecoratedService::class, $decorated); $container->compile(); /** @var ServiceLocator $locator */ $locator = $container->get(Locator::class)->locator; static::assertTrue($locator->has(Service::class)); - static::assertFalse($locator->has(Decorated::class)); - static::assertInstanceOf(Decorated::class, $locator->get(Service::class)); + static::assertFalse($locator->has(DecoratedService::class)); + static::assertInstanceOf(DecoratedService::class, $locator->get(Service::class)); } public function testDefinitionOrderIsTheSame()