merged branch fabpot/container-dumper-fixes (PR #6565)

This PR was merged into the 2.0 branch.

Commits
-------

e0923ae [DependencyInjection] fixed PhpDumper optimizations when an inlined service depends on the current one indirectly
cd15390 [DependencyInjection] fixed PhpDumper when an inlined service definition has some properties
e939a42 [DependencyInjection] added some tests for PhpDumper when the container is compiled
3827e3e [DependencyInjection] fixed CS

Discussion
----------

[DependencyInjection] Fixed PhpDumper when compiling inlined services
This commit is contained in:
Fabien Potencier 2013-01-05 16:22:59 +01:00
commit 5258edbe9e
9 changed files with 408 additions and 31 deletions

View File

@ -201,7 +201,7 @@ class PhpDumper extends Dumper
$nbOccurrences->offsetSet($definition, 1);
} else {
$i = $nbOccurrences->offsetGet($definition);
$nbOccurrences->offsetSet($definition, $i+1);
$nbOccurrences->offsetSet($definition, $i + 1);
}
}
@ -212,7 +212,7 @@ class PhpDumper extends Dumper
$processed->offsetSet($sDefinition);
$class = $this->dumpValue($sDefinition->getClass());
if ($nbOccurrences->offsetGet($sDefinition) > 1 || count($sDefinition->getMethodCalls()) > 0 || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
$name = $this->getNextVariableName();
$variableMap->offsetSet($sDefinition, new Variable($name));
@ -246,7 +246,7 @@ class PhpDumper extends Dumper
$code .= sprintf(" \$%s = new \\%s(%s);\n", $name, substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
}
if (!$this->hasReference($id, $sDefinition->getMethodCalls()) && !$this->hasReference($id, $sDefinition->getProperties())) {
if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) {
$code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
$code .= $this->addServiceProperties(null, $sDefinition, $name);
$code .= $this->addServiceConfigurator(null, $sDefinition, $name);
@ -415,16 +415,14 @@ class PhpDumper extends Dumper
}
$processed->offsetSet($iDefinition);
if (!$this->hasReference($id, $iDefinition->getMethodCalls())) {
if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) {
continue;
}
if ($iDefinition->getMethodCalls()) {
$code .= $this->addServiceMethodCalls(null, $iDefinition, (string) $this->definitionVariables->offsetGet($iDefinition));
}
if ($iDefinition->getConfigurator()) {
$code .= $this->addServiceConfigurator(null, $iDefinition, (string) $this->definitionVariables->offsetGet($iDefinition));
}
$name = (string) $this->definitionVariables->offsetGet($iDefinition);
$code .= $this->addServiceMethodCalls(null, $iDefinition, $name);
$code .= $this->addServiceProperties(null, $iDefinition, $name);
$code .= $this->addServiceConfigurator(null, $iDefinition, $name);
}
if ('' !== $code) {
@ -960,17 +958,26 @@ EOF;
*
* @return Boolean
*/
private function hasReference($id, array $arguments)
private function hasReference($id, array $arguments, $deep = false)
{
foreach ($arguments as $argument) {
if (is_array($argument)) {
if ($this->hasReference($id, $argument)) {
if ($this->hasReference($id, $argument, $deep)) {
return true;
}
} elseif ($argument instanceof Reference) {
if ($id === (string) $argument) {
return true;
}
if ($deep) {
$service = $this->container->getDefinition((string) $argument);
$arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties());
if ($this->hasReference($id, $arguments, $deep)) {
return true;
}
}
}
}

View File

@ -96,10 +96,17 @@ class PhpDumperTest extends \PHPUnit_Framework_TestCase
public function testAddService()
{
// without compilation
$container = include self::$fixturesPath.'/containers/container9.php';
$dumper = new PhpDumper($container);
$this->assertEquals(str_replace('%path%', str_replace('\\','\\\\',self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9.php')), $dumper->dump(), '->dump() dumps services');
// with compilation
$container = include self::$fixturesPath.'/containers/container9.php';
$container->compile();
$dumper = new PhpDumper($container);
$this->assertEquals(str_replace('%path%', str_replace('\\','\\\\',self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9_compiled.php')), $dumper->dump(), '->dump() dumps services');
$dumper = new PhpDumper($container = new ContainerBuilder());
$container->register('foo', 'FooClass')->addArgument(new \stdClass());
try {

View File

@ -14,9 +14,8 @@ $container->
addTag('foo', array('bar' => 'bar'))->
setFactoryClass('FooClass')->
setFactoryMethod('getInstance')->
setArguments(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, new Reference('service_container')))->
setArguments(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, new Reference('service_container')))->
setProperties(array('foo' => 'bar', 'moo' => new Reference('foo.baz')))->
setScope('prototype')->
addMethodCall('setBar', array(new Reference('bar')))->
addMethodCall('initialize')->
setConfigurator('sc_configure')
@ -33,7 +32,10 @@ $container->
setFactoryMethod('getInstance')->
setConfigurator(array('%baz_class%', 'configureStatic1'))
;
$container->register('foo_bar', '%foo_class%');
$container->
register('foo_bar', '%foo_class%')->
setScope('prototype')
;
$container->getParameterBag()->clear();
$container->getParameterBag()->add(array(
'baz_class' => 'BazClass',
@ -50,9 +52,24 @@ $container->
addMethodCall('setBar', array(new Reference('foobaz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))
;
$container->
register('factory_service')->
register('factory_service', 'Bar')->
setFactoryService('foo.baz')->
setFactoryMethod('getInstance')
;
$container
->register('foo_with_inline', 'Foo')
->addMethodCall('setBar', array(new Reference('inlined')))
;
$container
->register('inlined', 'Bar')
->setProperty('pub', 'pub')
->addMethodCall('setBaz', array(new Reference('baz')))
->setPublic(false)
;
$container
->register('baz', 'Baz')
->addMethodCall('setFoo', array(new Reference('foo_with_inline')))
;
return $container;

View File

@ -3,12 +3,15 @@ digraph sc {
node [fontsize="11" fontname="Arial" shape="record"];
edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"];
node_foo [label="foo (alias_for_foo)\nFooClass\n", shape=record, fillcolor="#eeeeee", style="dotted"];
node_foo [label="foo (alias_for_foo)\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_bar [label="bar\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_foo_baz [label="foo.baz\nBazClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_foo_bar [label="foo_bar\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_foo_bar [label="foo_bar\nFooClass\n", shape=record, fillcolor="#eeeeee", style="dotted"];
node_method_call1 [label="method_call1\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_factory_service [label="factory_service\n\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_factory_service [label="factory_service\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_foo_with_inline [label="foo_with_inline\nFoo\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_inlined [label="inlined\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_baz [label="baz\nBaz\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"];
node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"];
@ -22,4 +25,7 @@ digraph sc {
node_method_call1 -> node_foo2 [label="setBar()" style="dashed"];
node_method_call1 -> node_foo3 [label="setBar()" style="dashed"];
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"];
}

View File

@ -7,10 +7,23 @@ function sc_configure($instance)
class BarClass
{
protected $baz;
public function setBaz(BazClass $baz)
{
$this->baz = $baz;
}
}
class BazClass
{
protected $foo;
public function setFoo(Foo $foo)
{
$this->foo = $foo;
}
public function configure($instance)
{
$instance->configure();

View File

@ -40,13 +40,30 @@ class ProjectServiceContainer extends Container
return $instance;
}
/**
* Gets the 'baz' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return Baz A Baz instance.
*/
protected function getBazService()
{
$this->services['baz'] = $instance = new \Baz();
$instance->setFoo($this->get('foo_with_inline'));
return $instance;
}
/**
* Gets the 'factory_service' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return Object An instance returned by foo.baz::getInstance().
* @return Bar A Bar instance.
*/
protected function getFactoryServiceService()
{
@ -56,13 +73,16 @@ class ProjectServiceContainer extends Container
/**
* Gets the 'foo' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return FooClass A FooClass instance.
*/
protected function getFooService()
{
$a = $this->get('foo.baz');
$instance = call_user_func(array('FooClass', 'getInstance'), 'foo', $a, array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo'), 'bar' => $this->getParameter('foo')), true, $this);
$this->services['foo'] = $instance = call_user_func(array('FooClass', 'getInstance'), 'foo', $a, array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo'), 'foobar' => $this->getParameter('foo')), true, $this);
$instance->setBar($this->get('bar'));
$instance->initialize();
@ -93,15 +113,29 @@ class ProjectServiceContainer extends Container
/**
* Gets the 'foo_bar' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return Object A %foo_class% instance.
*/
protected function getFooBarService()
{
$class = $this->getParameter('foo_class');
return $this->services['foo_bar'] = new $class();
return new $class();
}
/**
* Gets the 'foo_with_inline' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return Foo A Foo instance.
*/
protected function getFooWithInlineService()
{
$this->services['foo_with_inline'] = $instance = new \Foo();
$instance->setBar($this->get('inlined'));
return $instance;
}
/**
@ -140,6 +174,28 @@ class ProjectServiceContainer extends Container
return $this->get('foo');
}
/**
* Gets the 'inlined' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* This service is private.
* If you want to be able to request this service from the container directly,
* make it public, otherwise you might end up with broken code.
*
* @return Bar A Bar instance.
*/
protected function getInlinedService()
{
$this->services['inlined'] = $instance = new \Bar();
$instance->setBaz($this->get('baz'));
$instance->pub = 'pub';
return $instance;
}
/**
* Gets the default parameters.
*

View File

@ -0,0 +1,238 @@
<?php
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
* ProjectServiceContainer
*
* This class has been auto-generated
* by the Symfony Dependency Injection Component.
*/
class ProjectServiceContainer extends Container
{
/**
* Constructor.
*/
public function __construct()
{
$this->parameters = $this->getDefaultParameters();
$this->services =
$this->scopedServices =
$this->scopeStacks = array();
$this->set('service_container', $this);
$this->scopes = array();
$this->scopeChildren = array();
}
/**
* Gets the 'bar' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return FooClass A FooClass instance.
*/
protected function getBarService()
{
$this->services['bar'] = $instance = new \FooClass('foo', $this->get('foo.baz'), $this->getParameter('foo_bar'));
$this->get('foo.baz')->configure($instance);
return $instance;
}
/**
* Gets the 'baz' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return Baz A Baz instance.
*/
protected function getBazService()
{
$this->services['baz'] = $instance = new \Baz();
$instance->setFoo($this->get('foo_with_inline'));
return $instance;
}
/**
* Gets the 'factory_service' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return Bar A Bar instance.
*/
protected function getFactoryServiceService()
{
return $this->services['factory_service'] = $this->get('foo.baz')->getInstance();
}
/**
* Gets the 'foo' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return FooClass A FooClass instance.
*/
protected function getFooService()
{
$a = $this->get('foo.baz');
$this->services['foo'] = $instance = call_user_func(array('FooClass', 'getInstance'), 'foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this);
$instance->setBar($this->get('bar'));
$instance->initialize();
$instance->foo = 'bar';
$instance->moo = $a;
sc_configure($instance);
return $instance;
}
/**
* Gets the 'foo.baz' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return BazClass A BazClass instance.
*/
protected function getFoo_BazService()
{
$this->services['foo.baz'] = $instance = call_user_func(array('BazClass', 'getInstance'));
call_user_func(array('BazClass', 'configureStatic1'), $instance);
return $instance;
}
/**
* Gets the 'foo_bar' service.
*
* @return FooClass A FooClass instance.
*/
protected function getFooBarService()
{
return new \FooClass();
}
/**
* Gets the 'foo_with_inline' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return Foo A Foo instance.
*/
protected function getFooWithInlineService()
{
$a = new \Bar();
$this->services['foo_with_inline'] = $instance = new \Foo();
$a->setBaz($this->get('baz'));
$a->pub = 'pub';
$instance->setBar($a);
return $instance;
}
/**
* Gets the 'method_call1' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return FooClass A FooClass instance.
*/
protected function getMethodCall1Service()
{
require_once '%path%foo.php';
$this->services['method_call1'] = $instance = new \FooClass();
$instance->setBar($this->get('foo'));
$instance->setBar(NULL);
return $instance;
}
/**
* Gets the alias_for_foo service alias.
*
* @return FooClass An instance of the foo service
*/
protected function getAliasForFooService()
{
return $this->get('foo');
}
/**
* {@inheritdoc}
*/
public function getParameter($name)
{
$name = strtolower($name);
if (!array_key_exists($name, $this->parameters)) {
throw new \InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
return $this->parameters[$name];
}
/**
* {@inheritdoc}
*/
public function hasParameter($name)
{
return array_key_exists(strtolower($name), $this->parameters);
}
/**
* {@inheritdoc}
*/
public function setParameter($name, $value)
{
throw new \LogicException('Impossible to call set() on a frozen ParameterBag.');
}
/**
* {@inheritDoc}
*/
public function getParameterBag()
{
if (null === $this->parameterBag) {
$this->parameterBag = new FrozenParameterBag($this->parameters);
}
return $this->parameterBag;
}
/**
* Gets the default parameters.
*
* @return array An array of the default parameters
*/
protected function getDefaultParameters()
{
return array(
'baz_class' => 'BazClass',
'foo_class' => 'FooClass',
'foo' => 'bar',
);
}
}

View File

@ -6,14 +6,14 @@
<parameter key="foo">bar</parameter>
</parameters>
<services>
<service id="foo" class="FooClass" factory-method="getInstance" scope="prototype">
<service id="foo" class="FooClass" factory-method="getInstance">
<tag name="foo" foo="foo"/>
<tag name="foo" bar="bar"/>
<argument>foo</argument>
<argument type="service" id="foo.baz"/>
<argument type="collection">
<argument key="%foo%">foo is %foo%</argument>
<argument key="bar">%foo%</argument>
<argument key="foobar">%foo%</argument>
</argument>
<argument>true</argument>
<argument type="service" id="service_container"/>
@ -34,7 +34,7 @@
<service id="foo.baz" class="%baz_class%" factory-method="getInstance">
<configurator class="%baz_class%" method="configureStatic1"/>
</service>
<service id="foo_bar" class="%foo_class%"/>
<service id="foo_bar" class="%foo_class%" scope="prototype"/>
<service id="method_call1" class="FooClass">
<file>%path%foo.php</file>
<call method="setBar">
@ -50,7 +50,23 @@
<argument type="service" id="foobaz" on-invalid="ignore"/>
</call>
</service>
<service id="factory_service" factory-method="getInstance" factory-service="foo.baz"/>
<service id="factory_service" class="Bar" factory-method="getInstance" factory-service="foo.baz"/>
<service id="foo_with_inline" class="Foo">
<call method="setBar">
<argument type="service" id="inlined"/>
</call>
</service>
<service id="inlined" class="Bar" public="false">
<property name="pub">pub</property>
<call method="setBaz">
<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="alias_for_foo" alias="foo"/>
</services>
</container>

View File

@ -10,13 +10,12 @@ services:
- { name: foo, foo: foo }
- { name: foo, bar: bar }
factory_method: getInstance
arguments: [foo, '@foo.baz', { '%foo%': 'foo is %foo%', bar: '%foo%' }, true, '@service_container']
arguments: [foo, '@foo.baz', { '%foo%': 'foo is %foo%', foobar: '%foo%' }, true, '@service_container']
properties: { foo: bar, moo: '@foo.baz' }
calls:
- [setBar, ['@bar']]
- [initialize, { }]
scope: prototype
configurator: sc_configure
bar:
class: FooClass
@ -28,6 +27,7 @@ services:
configurator: ['%baz_class%', configureStatic1]
foo_bar:
class: %foo_class%
scope: prototype
method_call1:
class: FooClass
file: %path%foo.php
@ -38,6 +38,23 @@ services:
- [setBar, ['@?foobaz']]
factory_service:
class: Bar
factory_method: getInstance
factory_service: foo.baz
foo_with_inline:
class: Foo
calls:
- [setBar, ['@inlined']]
inlined:
class: Bar
properties: { pub: pub }
calls:
- [setBaz, ['@baz']]
baz:
class: Baz
calls:
- [setFoo, ['@foo_with_inline']]
alias_for_foo: @foo