From 1c1210a3e8a4f354ebe47907baf557404ec06c1e Mon Sep 17 00:00:00 2001 From: Tim Goudriaan Date: Mon, 24 Sep 2018 19:50:22 +0200 Subject: [PATCH] [DependencyInjection] Improve ServiceLocatorTagPass service matching --- .../Compiler/ServiceLocatorTagPass.php | 6 + .../Compiler/ServiceLocatorTagPassTest.php | 131 ++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php index 023ff552a1..acf8403225 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php @@ -52,6 +52,12 @@ final class ServiceLocatorTagPass extends AbstractRecursivePass if (!$v instanceof Reference) { 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, "%s" found for key "%s".', $this->currentId, \is_object($v) ? \get_class($v) : \gettype($v), $k)); } + + if (\is_int($k)) { + unset($arguments[0][$k]); + + $k = (string) $v; + } $arguments[0][$k] = new ServiceClosureArgument($v); } ksort($arguments[0]); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php new file mode 100644 index 0000000000..5157e84acf --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +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; + +require_once __DIR__.'/../Fixtures/includes/classes.php'; + +class ServiceLocatorTagPassTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid definition for service "foo": an array of references is expected as first argument when the "container.service_locator" tag is set. + */ + public function testNoServices() + { + $container = new ContainerBuilder(); + + $container->register('foo', ServiceLocator::class) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid definition for service "foo": an array of references is expected as first argument when the "container.service_locator" tag is set, "string" found for key "0". + */ + public function testInvalidServices() + { + $container = new ContainerBuilder(); + + $container->register('foo', ServiceLocator::class) + ->setArguments(array(array( + 'dummy', + ))) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + } + + public function testProcessValue() + { + $container = new ContainerBuilder(); + + $container->register('bar', CustomDefinition::class); + $container->register('baz', CustomDefinition::class); + + $container->register('foo', ServiceLocator::class) + ->setArguments(array(array( + new Reference('bar'), + new Reference('baz'), + 'some.service' => new Reference('bar'), + ))) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + + /** @var ServiceLocator $locator */ + $locator = $container->get('foo'); + + $this->assertSame(CustomDefinition::class, \get_class($locator('bar'))); + $this->assertSame(CustomDefinition::class, \get_class($locator('baz'))); + $this->assertSame(CustomDefinition::class, \get_class($locator('some.service'))); + } + + public function testServiceWithKeyOverwritesPreviousInheritedKey() + { + $container = new ContainerBuilder(); + + $container->register('bar', TestDefinition1::class); + $container->register('baz', TestDefinition2::class); + + $container->register('foo', ServiceLocator::class) + ->setArguments(array(array( + new Reference('bar'), + 'bar' => new Reference('baz'), + ))) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + + /** @var ServiceLocator $locator */ + $locator = $container->get('foo'); + + $this->assertSame(TestDefinition2::class, \get_class($locator('bar'))); + } + + public function testInheritedKeyOverwritesPreviousServiceWithKey() + { + $container = new ContainerBuilder(); + + $container->register('bar', TestDefinition1::class); + $container->register('baz', TestDefinition2::class); + + $container->register('foo', ServiceLocator::class) + ->setArguments(array(array( + 'bar' => new Reference('baz'), + new Reference('bar'), + ))) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + + /** @var ServiceLocator $locator */ + $locator = $container->get('foo'); + + $this->assertSame(TestDefinition1::class, \get_class($locator('bar'))); + } +}