diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 985f3a50e1392..ef8db76b358a1 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -29,6 +29,7 @@ class AutowirePass implements CompilerPassInterface private $definedTypes = array(); private $types; private $ambiguousServiceTypes = array(); + private $usedTypes = array(); /** * {@inheritdoc} @@ -45,6 +46,15 @@ public function process(ContainerBuilder $container) $this->completeDefinition($id, $definition); } } + + foreach ($this->usedTypes as $type => $id) { + if (isset($this->usedTypes[$type]) && isset($this->ambiguousServiceTypes[$type])) { + $classOrInterface = class_exists($type) ? 'class' : 'interface'; + $matchingServices = implode(', ', $this->ambiguousServiceTypes[$type]); + + throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $type, $id, $classOrInterface, $matchingServices)); + } + } } finally { spl_autoload_unregister($throwingAutoloader); @@ -54,6 +64,7 @@ public function process(ContainerBuilder $container) $this->definedTypes = array(); $this->types = null; $this->ambiguousServiceTypes = array(); + $this->usedTypes = array(); } } @@ -128,9 +139,11 @@ private function completeDefinition($id, Definition $definition) if (isset($this->types[$typeHint->name])) { $value = new Reference($this->types[$typeHint->name]); + $this->usedTypes[$typeHint->name] = $id; } else { try { $value = $this->createAutowiredDefinition($typeHint, $id); + $this->usedTypes[$typeHint->name] = $id; } catch (RuntimeException $e) { if ($parameter->allowsNull()) { $value = null; @@ -188,6 +201,7 @@ private function populateAvailableType($id, Definition $definition) foreach ($definition->getAutowiringTypes() as $type) { $this->definedTypes[$type] = true; $this->types[$type] = $id; + unset($this->ambiguousServiceTypes[$type]); } if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 212b93d9724f3..3d286483374fd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -191,7 +191,8 @@ public function testTypeNotGuessableWithTypeSet() $container = new ContainerBuilder(); $container->register('a1', __NAMESPACE__.'\Foo'); - $container->register('a2', __NAMESPACE__.'\Foo')->addAutowiringType(__NAMESPACE__.'\Foo'); + $container->register('a2', __NAMESPACE__.'\Foo'); + $container->register('a3', __NAMESPACE__.'\Foo')->addAutowiringType(__NAMESPACE__.'\Foo'); $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument'); $aDefinition->setAutowired(true); @@ -199,7 +200,7 @@ public function testTypeNotGuessableWithTypeSet() $pass->process($container); $this->assertCount(1, $container->getDefinition('a')->getArguments()); - $this->assertEquals('a2', (string) $container->getDefinition('a')->getArgument(0)); + $this->assertEquals('a3', (string) $container->getDefinition('a')->getArgument(0)); } public function testWithTypeSet() @@ -510,6 +511,31 @@ public function testEmptyStringIsKept() $this->assertEquals(array(new Reference('a'), '', new Reference('lille')), $container->getDefinition('foo')->getArguments()); } + + /** + * @dataProvider provideAutodiscoveredAutowiringOrder + * + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMEssage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". Multiple services exist for this interface (autowired.Symfony\Component\DependencyInjection\Tests\Compiler\CollisionA, autowired.Symfony\Component\DependencyInjection\Tests\Compiler\CollisionB). + */ + public function testAutodiscoveredAutowiringOrder($class) + { + $container = new ContainerBuilder(); + + $container->register('a', __NAMESPACE__.'\\'.$class) + ->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + public function provideAutodiscoveredAutowiringOrder() + { + return array( + array('CannotBeAutowiredForwardOrder'), + array('CannotBeAutowiredReverseOrder'), + ); + } } class Foo @@ -591,6 +617,20 @@ public function __construct(CollisionInterface $collision) } } +class CannotBeAutowiredForwardOrder +{ + public function __construct(CollisionA $a, CollisionInterface $b, CollisionB $c) + { + } +} + +class CannotBeAutowiredReverseOrder +{ + public function __construct(CollisionA $a, CollisionB $c, CollisionInterface $b) + { + } +} + class Lille { }