Skip to content

Commit

Permalink
[DI] fix using bindings with locators of service subscribers
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed May 19, 2019
1 parent 0055284 commit 7146b95
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 1 deletion.
Expand Up @@ -48,6 +48,12 @@ protected function processValue($value, $isRoot = false)
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]);
Expand Down Expand Up @@ -91,7 +97,11 @@ public static function register(ContainerBuilder $container, array $refMap, $cal
->setPublic(false)
->addTag('container.service_locator');

if (!$container->has($id = 'service_locator.'.ContainerBuilder::hash($locator))) {
if (null !== $callerId && $container->hasDefinition($callerId)) {
$locator->setBindings($container->getDefinition($callerId)->getBindings());
}

if (!$container->hasDefinition($id = 'service_locator.'.ContainerBuilder::hash($locator))) {
$container->setDefinition($id, $locator);
}

Expand Down
@@ -0,0 +1,147 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\Argument\BoundArgument;
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([[
'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([[
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([[
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([[
'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')));
}

public function testBindingsAreCopied()
{
$container = new ContainerBuilder();

$container->register('foo')
->setBindings(['foo' => 'foo']);

$locator = ServiceLocatorTagPass::register($container, ['foo' => new Reference('foo')], 'foo');
$locator = $container->getDefinition($locator);
$locator = $container->getDefinition($locator->getFactory()[0]);

$this->assertSame(['foo'], array_keys($locator->getBindings()));
$this->assertInstanceOf(BoundArgument::class, $locator->getBindings()['foo']);
}
}
@@ -0,0 +1,9 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Component\DependencyInjection\Definition;

class TestDefinition2 extends Definition
{
}

0 comments on commit 7146b95

Please sign in to comment.