From 8eb5053c87d2326106c7d6e41b9aec46c49a6726 Mon Sep 17 00:00:00 2001 From: Jordan Samouh Date: Tue, 28 Mar 2017 22:31:42 +0200 Subject: [PATCH] [DI] [CompilerPass] [AutoWire] : fix order service definition problem --- .../Compiler/AutowirePass.php | 42 ++++++++- .../Tests/Compiler/AutowirePassTest.php | 88 +++++++++++++++++++ 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 9454d1bd4b1ad..628670c51ff90 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 $types; private $notGuessableTypes = array(); private $usedTypes = array(); + private $secondPassDefinitions = array(); /** * {@inheritdoc} @@ -46,6 +47,10 @@ public function process(ContainerBuilder $container) } } + foreach ($this->secondPassDefinitions as $id => $definition) { + $this->completeDefinition($id, $definition); + } + foreach ($this->usedTypes as $type => $id) { if (isset($this->usedTypes[$type]) && isset($this->notGuessableTypes[$type])) { $classOrInterface = class_exists($type) ? 'class' : 'interface'; @@ -67,6 +72,7 @@ public function process(ContainerBuilder $container) $this->types = null; $this->notGuessableTypes = array(); $this->usedTypes = array(); + $this->secondPassDefinitions = array(); if (isset($e)) { throw $e; @@ -94,7 +100,8 @@ private function completeDefinition($id, Definition $definition) } $arguments = $definition->getArguments(); - foreach ($constructor->getParameters() as $index => $parameter) { + $parameters = $this->getPriorityToInstantiableParameters($constructor->getParameters()); + foreach ($parameters as $index => $parameter) { if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) { continue; } @@ -131,7 +138,12 @@ private function completeDefinition($id, Definition $definition) } elseif ($parameter->isDefaultValueAvailable()) { $value = $parameter->getDefaultValue(); } else { - throw $e; + if (isset($this->secondPassDefinitions[$id])) { + throw $e; + } + $this->secondPassDefinitions[$id] = $definition; + + return; } } } @@ -299,4 +311,30 @@ private function getReflectionClass($id, Definition $definition) return $this->reflectionClasses[$id] = $reflector; } + + /** + * Priority for Instantiable parameters. + * + * @param array $parameters + * + * @return array + */ + private function getPriorityToInstantiableParameters(array $parameters) + { + $result = array(); + + foreach ($parameters as $index => $parameter) { + try { + if (($typeHint = $parameter->getClass()) && $typeHint->isInstantiable()) { + $result = array($index => $parameter) + $result; + } else { + $result = $result + array($index => $parameter); + } + } catch (\Exception $e) { + $result = $result + array($index => $parameter); + } + } + + return $result; + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 047801ceba8ea..e0499a9d19c1c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -486,6 +486,75 @@ public function provideAutodiscoveredAutowiringOrder() array('CannotBeAutowiredReverseOrder'), ); } + + public function testAutoDiscoverWithAnyOrderParameters() + { + $container = new ContainerBuilder(); + $container->register('a', __NAMESPACE__.'\\'.'Jordan') + ->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertTrue($container->hasDefinition('a')); + + $this->assertEquals( + array( + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\i'), + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\i'), + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\d'), + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\j'), + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\d'), + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\j'), + ), + $container->getDefinition('a')->getArguments() + ); + } + + public function testAutoDiscoverWithAnyOrderServices() + { + $container = new ContainerBuilder(); + $container->register('montreal', __NAMESPACE__.'\\'.'Montreal') + ->setAutowired(true); + $container->register('paris', __NAMESPACE__.'\\'.'Paris') + ->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertTrue($container->hasDefinition('montreal')); + $this->assertTrue($container->hasDefinition('paris')); + + $this->assertEquals( + array( + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\j'), + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\d'), + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\d'), + ), + $container->getDefinition('montreal')->getArguments() + ); + + $this->assertEquals( + array( + new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\j'), + ), + $container->getDefinition('paris')->getArguments() + ); + } +} + +class Montreal +{ + public function __construct(JInterface $j, DInterface $d, D $d2) + { + } +} + +class Paris +{ + public function __construct(J $j) + { + } } class Foo @@ -522,6 +591,10 @@ interface EInterface extends DInterface { } +interface JInterface +{ +} + interface IInterface { } @@ -548,6 +621,21 @@ public function __construct(B $b, DInterface $d) } } +class D implements DInterface +{ +} + +class J implements JInterface +{ +} + +class Jordan +{ + public function __construct(IInterface $i, I $i2, DInterface $d, JInterface $j, D $d2, J $j2) + { + } +} + interface CollisionInterface { }