diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php index d7570ddc2c..b6a898736f 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php @@ -60,15 +60,20 @@ class CheckCircularReferencesPass implements CompilerPassInterface $id = $node->getId(); if (empty($this->checkedNodes[$id])) { - $searchKey = array_search($id, $this->currentPath); - $this->currentPath[] = $id; - if (false !== $searchKey) { - throw new ServiceCircularReferenceException($id, array_slice($this->currentPath, $searchKey)); + // * don't check circular dependencies in lazy services. + $isLazy = $node->getValue() && $node->getValue()->isLazy(); + if (!$isLazy) { + $searchKey = array_search($id, $this->currentPath); + $this->currentPath[] = $id; + + if (false !== $searchKey) { + throw new ServiceCircularReferenceException($id, array_slice($this->currentPath, $searchKey)); + } + + $this->checkOutEdges($node->getOutEdges()); } - $this->checkOutEdges($node->getOutEdges()); - $this->checkedNodes[$id] = true; array_pop($this->currentPath); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 2f7a01bc08..bf5956b394 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1293,6 +1293,13 @@ EOF; $visited[$argumentId] = true; $service = $this->container->getDefinition($argumentId); + + // if exists proxy dumper (proxy-manager) don't search references in lazy services. + // As these services will be instantiated lazily and don't have direct related references. + if ($service->isLazy() && !($this->getProxyDumper() instanceof NullDumper)) { + continue; + } + $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties()); if ($this->hasReference($id, $arguments, $deep, $visited)) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 72215e8c48..ab9d592610 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper; +use DummyProxyDumper; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -19,6 +20,8 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Variable; use Symfony\Component\ExpressionLanguage\Expression; +require_once __DIR__.'/../Fixtures/includes/classes.php'; + class PhpDumperTest extends \PHPUnit_Framework_TestCase { protected static $fixturesPath; @@ -286,4 +289,52 @@ class PhpDumperTest extends \PHPUnit_Framework_TestCase $container = new \Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls(); $this->assertTrue($container->get('bar')->callPassed(), '->dump() initializes properties before method calls'); } + + public function testCircularReferenceAllowanceForLazyServices() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->addArgument(new Reference('bar')); + $container->register('bar', 'stdClass')->setLazy(true)->addArgument(new Reference('foo')); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->dump(); + } + + public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServices() + { + /* + * test graph: + * [connection] -> [event_manager] --> [entity_manager](lazy) + * | + * --(call)- addEventListener ("@lazy_service") + * + * [lazy_service](lazy) -> [entity_manager](lazy) + * + */ + + $container = new ContainerBuilder(); + + $eventManagerDefinition = new Definition('stdClass'); + + $connectionDefinition = $container->register('connection', 'stdClass'); + $connectionDefinition->addArgument($eventManagerDefinition); + + $container->register('entity_manager', 'stdClass') + ->setLazy(true) + ->addArgument(new Reference('connection')); + + $lazyServiceDefinition = $container->register('lazy_service', 'stdClass'); + $lazyServiceDefinition->setLazy(true); + $lazyServiceDefinition->addArgument(new Reference('entity_manager')); + + $eventManagerDefinition->addMethodCall('addEventListener', array(new Reference('lazy_service'))); + + $container->compile(); + + $dumper = new PhpDumper($container); + + $dumper->setProxyDumper(new DummyProxyDumper()); + $dumper->dump(); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index 48b687c1f4..cdc89e42ab 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -1,5 +1,8 @@ configure(); @@ -76,3 +79,30 @@ class MethodCallClass return $this->callPassed; } } + +class DummyProxyDumper implements ProxyDumper +{ + /** + * {@inheritdoc} + */ + public function isProxyCandidate(Definition $definition) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function getProxyFactoryCode(Definition $definition, $id) + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function getProxyCode(Definition $definition) + { + return ''; + } +}