bug #21436 [DependencyInjection] check for circular refs caused by method calls (xabbuh)

This PR was merged into the 2.7 branch.

Discussion
----------

[DependencyInjection] check for circular refs caused by method calls

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #19362
| License       | MIT
| Doc PR        |

Before we check for circular references, dependencies coming from method calls are not part of the dependency graph. That why the pass is not able to detect circular references like the one described in #19362 during compilation of the container.

If we add another check after all the optimisation passes have been processed, we should be able to detect these circular references too.

Commits
-------

fe4f7eccf7 check for circular refs caused by method calls
This commit is contained in:
Fabien Potencier 2017-02-16 05:23:11 -08:00
commit 3441b1586f
8 changed files with 32 additions and 22 deletions

View File

@ -63,6 +63,7 @@ class PassConfig
new RemoveUnusedDefinitionsPass(),
)),
new CheckExceptionOnInvalidReferenceBehaviorPass(),
new CheckCircularReferencesPass(),
);
}

View File

@ -113,4 +113,30 @@ class IntegrationTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($container->hasDefinition('b'));
$this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.');
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
*/
public function testCircularReferencesCausedByMethodCallsAreDetectedDuringCompilation()
{
$container = new ContainerBuilder();
$container->setResourceTracking(false);
$container
->register('foobar', '\stdClass')
->addArgument(new Reference('foo'))
;
$container
->register('foo', '\stdClass')
->addArgument(new Reference('bar'))
;
$container
->register('foo', '\stdClass')
->addMethodCall('addFoobar', array(new Reference('foobar')))
;
$container->compile();
}
}

View File

@ -64,7 +64,6 @@ $container
;
$container
->register('baz', 'Baz')
->addMethodCall('setFoo', array(new Reference('foo_with_inline')))
;
$container
->register('request', 'Request')

View File

@ -36,6 +36,5 @@ digraph sc {
node_method_call1 -> node_foobaz [label="setBar()" style="dashed"];
node_foo_with_inline -> node_inlined [label="setBar()" style="dashed"];
node_inlined -> node_baz [label="setBaz()" style="dashed"];
node_baz -> node_foo_with_inline [label="setFoo()" style="dashed"];
node_configurator_service -> node_baz [label="setFoo()" style="dashed"];
}

View File

@ -80,11 +80,7 @@ class ProjectServiceContainer extends Container
*/
protected function getBazService()
{
$this->services['baz'] = $instance = new \Baz();
$instance->setFoo($this->get('foo_with_inline'));
return $instance;
return $this->services['baz'] = new \Baz();
}
/**

View File

@ -99,11 +99,7 @@ class ProjectServiceContainer extends Container
*/
protected function getBazService()
{
$this->services['baz'] = $instance = new \Baz();
$instance->setFoo($this->get('foo_with_inline'));
return $instance;
return $this->services['baz'] = new \Baz();
}
/**
@ -227,12 +223,11 @@ class ProjectServiceContainer extends Container
protected function getFooWithInlineService()
{
$a = new \Bar();
$this->services['foo_with_inline'] = $instance = new \Foo();
$a->pub = 'pub';
$a->setBaz($this->get('baz'));
$this->services['foo_with_inline'] = $instance = new \Foo();
$instance->setBar($a);
return $instance;

View File

@ -70,11 +70,7 @@
<argument type="service" id="baz"/>
</call>
</service>
<service id="baz" class="Baz">
<call method="setFoo">
<argument type="service" id="foo_with_inline"/>
</call>
</service>
<service id="baz" class="Baz"/>
<service id="request" class="Request" synthetic="true"/>
<service id="configurator_service" class="ConfClass" public="false">
<call method="setFoo">

View File

@ -52,8 +52,6 @@ services:
baz:
class: Baz
calls:
- [setFoo, ['@foo_with_inline']]
request:
class: Request