DI container optimization

This commit is contained in:
Johannes M. Schmitt 2011-01-05 12:13:27 +01:00 committed by Fabien Potencier
parent 4b78c4376f
commit c5ef113b18
31 changed files with 1108 additions and 108 deletions

View File

@ -439,7 +439,7 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
new Reference('doctrine.orm.default_annotation_metadata_driver'),
'DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles\XmlBundle\Entity'
));
$configDef = $container->getDefinition('doctrine.orm.default_configuration');
$this->assertDICDefinitionMethodCallOnce($configDef, 'setAutoGenerateProxyClasses', array(false));
}
@ -505,12 +505,13 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
public function testSingleEntityManagerMultipleMappingBundleDefinitions()
{
$container = $this->getContainer(array('YamlBundle', 'AnnotationsBundle', 'XmlBundle'));
$loader = new DoctrineExtension();
$container->registerExtension($loader);
$this->loadFromFile($container, 'orm_single_em_bundle_mappings');
$container->getCompilerPassConfig()->setRemovingPasses(array());
$container->freeze();
$definition = $container->getDefinition('doctrine.orm.default_metadata_driver');
@ -556,6 +557,7 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
$this->loadFromFile($container, 'orm_multiple_em_bundle_mappings');
$container->getCompilerPassConfig()->setRemovingPasses(array());
$container->freeze();
$def1 = $container->getDefinition('doctrine.orm.em1_metadata_driver');
@ -628,7 +630,7 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
/**
* Assertion on the Class of a DIC Service Definition.
*
*
* @param \Symfony\Component\DependencyInjection\Definition $definition
* @param string $expectedClass
*/

View File

@ -0,0 +1,28 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class AddConstraintValidatorsPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('validator.validator_factory')) {
return;
}
$validators = array();
foreach ($container->findTaggedServiceIds('validator.constraint_validator') as $id => $attributes) {
if (isset($attributes[0]['alias'])) {
$validators[$attributes[0]['alias']] = $id;
}
}
$definition = $container->getDefinition('validator.validator_factory');
$arguments = $definition->getArguments();
$arguments[1] = $validators;
$definition->setArguments($arguments);
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class AddTemplatingRenderersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('templating.engine')) {
return;
}
$renderers = array();
foreach ($container->findTaggedServiceIds('templating.renderer') as $id => $attributes) {
if (isset($attributes[0]['alias'])) {
$renderers[$attributes[0]['alias']] = new Reference($id);
$container->getDefinition($id)->addMethodCall('setEngine', array(new Reference('templating.engine')));
}
}
$helpers = array();
foreach ($container->findTaggedServiceIds('templating.helper') as $id => $attributes) {
if (isset($attributes[0]['alias'])) {
$helpers[$attributes[0]['alias']] = $id;
}
}
$definition = $container->getDefinition('templating.engine');
$arguments = $definition->getArguments();
$arguments[2] = $renderers;
$definition->setArguments($arguments);
if (count($helpers) > 0) {
$definition->addMethodCall('setHelpers', array($helpers));
}
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class RegisterKernelListenersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('event_dispatcher')) {
return;
}
$listeners = array();
foreach ($container->findTaggedServiceIds('kernel.listener') as $id => $attributes) {
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
if (!isset($listeners[$priority])) {
$listeners[$priority] = array();
}
$listeners[$priority][] = new Reference($id);
}
$container
->getDefinition('event_dispatcher')
->addMethodCall('registerKernelListeners', array($listeners))
;
}
}

View File

@ -3,7 +3,6 @@
namespace Symfony\Bundle\FrameworkBundle;
use Symfony\Component\EventDispatcher\EventDispatcher as BaseEventDispatcher;
use Symfony\Component\DependencyInjection\ContainerInterface;
/*
* This file is part of the Symfony package.
@ -15,18 +14,18 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
/**
* This EventDispatcher implementation uses a DependencyInjection container to load listeners.
* This EventDispatcher automatically gets the kernel listeners injected
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class EventDispatcher extends BaseEventDispatcher
{
public function setContainer(ContainerInterface $container)
public function registerKernelListeners(array $kernelListeners)
{
foreach ($container->findTaggedServiceIds('kernel.listener') as $id => $attributes) {
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
$container->get($id)->register($this, $priority);
foreach ($kernelListeners as $priority => $listeners) {
foreach ($listeners as $listener) {
$listener->register($this, $priority);
}
}
}
}

View File

@ -2,6 +2,8 @@
namespace Symfony\Bundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddTemplatingRenderersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddSecurityVotersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConverterManagerPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
@ -60,5 +62,7 @@ class FrameworkBundle extends Bundle
$container->addCompilerPass(new ConverterManagerPass());
$container->addCompilerPass(new RoutingResolverPass());
$container->addCompilerPass(new ProfilerPass());
$container->addCompilerPass(new RegisterKernelListenersPass());
$container->addCompilerPass(new AddTemplatingRenderersPass());
}
}

View File

@ -11,9 +11,6 @@
<services>
<service id="debug.event_dispatcher" class="%debug.event_dispatcher.class%">
<argument type="service" id="logger" on-invalid="null" />
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
</services>
</container>

View File

@ -14,12 +14,9 @@
<services>
<service id="event_dispatcher" class="%event_dispatcher.class%">
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
<service id="error_handler" class="%error_handler.class%">
<service id="error_handler" class="%error_handler.class%" public="false">
<argument>%error_handler.level%</argument>
</service>

View File

@ -27,18 +27,15 @@
<argument type="service" id="validator.validator_factory" />
</service>
<service id="validator.mapping.class_metadata_factory" class="%validator.mapping.class_metadata_factory.class%">
<service id="validator.mapping.class_metadata_factory" class="%validator.mapping.class_metadata_factory.class%" public="false">
<argument type="service" id="validator.mapping.loader.loader_chain" />
</service>
<service id="validator.validator_factory" class="%validator.validator_factory.class%">
<service id="validator.validator_factory" class="%validator.validator_factory.class%" public="false">
<argument type="service" id="service_container" />
<call method="loadTaggedServiceIds">
<argument type="service" id="service_container" />
</call>
</service>
<service id="validator.mapping.loader.loader_chain" class="%validator.mapping.loader.loader_chain.class%">
<service id="validator.mapping.loader.loader_chain" class="%validator.mapping.loader.loader_chain.class%" public="false">
<argument type="collection">
<argument type="service" id="validator.mapping.loader.static_method_loader" />
<argument type="service" id="validator.mapping.loader.xml_files_loader" />
@ -46,7 +43,7 @@
</argument>
</service>
<service id="validator.mapping.loader.static_method_loader" class="%validator.mapping.loader.static_method_loader.class%">
<service id="validator.mapping.loader.static_method_loader" class="%validator.mapping.loader.static_method_loader.class%" public="false">
<argument>%validator.mapping.loader.static_method_loader.method_name%</argument>
</service>

View File

@ -28,28 +28,15 @@ class Engine extends BaseEngine
/**
* Constructor.
*
* @param ContainerInterface $container A ContainerInterface instance
* @param ContainerInterface $container The DI container
* @param LoaderInterface $loader A loader instance
* @param array $renderers All templating renderers
*/
public function __construct(ContainerInterface $container, LoaderInterface $loader)
public function __construct(ContainerInterface $container, LoaderInterface $loader, array $renderers)
{
$this->container = $container;
parent::__construct($loader);
foreach ($this->container->findTaggedServiceIds('templating.renderer') as $id => $attributes) {
if (isset($attributes[0]['alias'])) {
$this->renderers[$attributes[0]['alias']] = $this->container->get($id);
$this->renderers[$attributes[0]['alias']]->setEngine($this);
}
}
$this->helpers = array();
foreach ($this->container->findTaggedServiceIds('templating.helper') as $id => $attributes) {
if (isset($attributes[0]['alias'])) {
$this->helpers[$attributes[0]['alias']] = $id;
}
}
parent::__construct($loader, $renderers);
}
public function getContainer()
@ -99,6 +86,11 @@ class Engine extends BaseEngine
return $this->helpers[$name];
}
public function setHelpers(array $helpers)
{
$this->helpers = $helpers;
}
// parses template names following the following pattern:
// bundle:section:template(.format).renderer
public function splitTemplateName($name, array $defaults = array())

View File

@ -30,7 +30,7 @@ class EngineTest extends TestCase
*/
public function testSplitTemplateName($name, $parameters)
{
$engine = new Engine($this->getContainerMock(), $this->getLoaderMock());
$engine = new Engine($this->getContainerMock(), $this->getLoaderMock(), array());
$this->assertEquals($parameters, $engine->splitTemplateName($name));
}
@ -50,7 +50,7 @@ class EngineTest extends TestCase
*/
public function testSplitTemplateNameInvalid($name)
{
$engine = new Engine($this->getContainerMock(), $this->getLoaderMock());
$engine = new Engine($this->getContainerMock(), $this->getLoaderMock(), array());
$engine->splitTemplateName($name);
}
@ -75,11 +75,6 @@ class EngineTest extends TestCase
;
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
$container
->expects($this->exactly(2))
->method('findTaggedServiceIds')
->will($this->returnValue(array()))
;
$container
->expects($this->any())
->method('get')

View File

@ -47,13 +47,6 @@ class ConstraintValidatorFactoryTest extends \PHPUnit_Framework_TestCase
// mock ContainerBuilder b/c it implements TaggedContainerInterface
$container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerBuilder');
$container
->expects($this->once())
->method('findTaggedServiceIds')
->with('validator.constraint_validator')
->will($this->returnValue(array(
$service => array(array('alias' => $alias)),
)));
$container
->expects($this->once())
->method('get')
@ -66,8 +59,7 @@ class ConstraintValidatorFactoryTest extends \PHPUnit_Framework_TestCase
->method('validatedBy')
->will($this->returnValue($alias));
$factory = new ConstraintValidatorFactory($container);
$factory->loadTaggedServiceIds($container);
$factory = new ConstraintValidatorFactory($container, array('validator_constraint_alias' => 'validator_constraint_service'));
$this->assertSame($validator, $factory->getInstance($constraint));
}
}

View File

@ -39,30 +39,17 @@ use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
{
protected $container;
protected $validators = array();
protected $validators;
/**
* Constructor.
*
* @param ContainerInterface $container The service container
*/
public function __construct(ContainerInterface $container)
public function __construct(ContainerInterface $container, array $validators = array())
{
$this->container = $container;
}
/**
* Loads ids for services tagged as constraint validators.
*
* @param TaggedContainerInterface $container The tagged service container
*/
public function loadTaggedServiceIds(TaggedContainerInterface $container)
{
foreach ($container->findTaggedServiceIds('validator.constraint_validator') as $id => $attributes) {
if (isset($attributes[0]['alias'])) {
$this->validators[$attributes[0]['alias']] = $id;
}
}
$this->validators = $validators;
}
/**

View File

@ -38,7 +38,7 @@ class TemplatingExtension extends \Twig_Extension
public function getTemplating()
{
return $this->container->get('templating.engine');
return $this->container->get('templating');
}
/**

View File

@ -0,0 +1,107 @@
<?php
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Inline service definitions where this is possible.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class InlineServiceDefinitionsPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
foreach ($container->getDefinitions() as $id => $definition) {
$definition->setArguments(
$this->inlineArguments($container, $definition->getArguments())
);
$definition->setMethodCalls(
$this->inlineArguments($container, $definition->getMethodCalls())
);
}
}
protected function inlineArguments(ContainerBuilder $container, array $arguments)
{
foreach ($arguments as $k => $argument) {
if (is_array($argument)) {
$arguments[$k] = $this->inlineArguments($container, $argument);
} else if ($argument instanceof Reference) {
if (!$container->hasDefinition($id = (string) $argument)) {
continue;
}
if ($this->isInlinableDefinition($container, $id, $definition = $container->getDefinition($id))) {
$arguments[$k] = $definition;
}
}
}
return $arguments;
}
protected function isInlinableDefinition(ContainerBuilder $container, $id, Definition $definition)
{
if (!$definition->isShared()) {
return true;
}
if ($definition->isPublic()) {
return false;
}
$references = count(array_keys($container->getAliases(), $id, true));
foreach ($container->getDefinitions() as $cDefinition)
{
if ($references > 1) {
break;
}
if ($this->isReferencedByArgument($id, $cDefinition->getArguments())) {
$references += 1;
continue;
}
foreach ($cDefinition->getMethodCalls() as $call) {
if ($this->isReferencedByArgument($id, $call[1])) {
$references += 1;
continue 2;
}
}
}
return $references <= 1;
}
protected function isReferencedByArgument($id, $argument)
{
if (is_array($argument)) {
foreach ($argument as $arg) {
if ($this->isReferencedByArgument($id, $arg)) {
return true;
}
}
} else if ($argument instanceof Reference) {
if ($id === (string) $argument) {
return true;
}
}
return false;
}
}

View File

@ -31,10 +31,13 @@ class PassConfig
$this->optimizationPasses = array(
new ResolveParameterPlaceHoldersPass(),
new ResolveReferencesToAliasesPass(),
new ResolveInterfaceInjectorsPass(),
new ResolveInvalidReferencesPass(),
);
$this->removingPasses = array(
new InlineServiceDefinitionsPass(),
new RemoveUnusedDefinitionsPass(),
);
}

View File

@ -6,8 +6,17 @@ use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Removes unused service definitions from the container
* Removes unused service definitions from the container.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
@ -22,7 +31,14 @@ class RemoveUnusedDefinitionsPass implements CompilerPassInterface
continue;
}
if (!in_array($id, $aliases, true) && !$this->isReferenced($container, $id)) {
$referencingAliases = array_keys($aliases, $id, true);
$isReferenced = $this->isReferenced($container, $id);
if (1 === count($referencingAliases) && false === $isReferenced) {
$container->setDefinition(reset($referencingAliases), $definition);
$definition->setPublic(true);
$container->remove($id);
} else if (0 === count($referencingAliases) && false === $isReferenced) {
$container->remove($id);
$hasChanged = true;
}
@ -40,11 +56,8 @@ class RemoveUnusedDefinitionsPass implements CompilerPassInterface
return true;
}
foreach ($definition->getMethodCalls() as $arguments)
{
if ($this->isReferencedByArgument($id, $arguments)) {
return true;
}
if ($this->isReferencedByArgument($id, $definition->getMethodCalls())) {
return true;
}
}
@ -63,6 +76,14 @@ class RemoveUnusedDefinitionsPass implements CompilerPassInterface
if ($id === (string) $argument) {
return true;
}
} else if ($argument instanceof Definition) {
if ($this->isReferencedByArgument($id, $argument->getArguments())) {
return true;
}
if ($this->isReferencedByArgument($id, $argument->getMethodCalls())) {
return true;
}
}
return false;

View File

@ -0,0 +1,90 @@
<?php
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Emulates the invalid behavior if the reference is not found within the
* container.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ResolveInvalidReferencesPass implements CompilerPassInterface
{
protected $container;
protected $exceptions;
public function __construct(array $exceptions = array('kernel', 'service_container', 'templating.loader.wrapped', 'pdo_connection'))
{
$this->exceptions = $exceptions;
}
public function addException($id)
{
$this->exceptions[] = $id;
}
public function process(ContainerBuilder $container)
{
$this->container = $container;
foreach ($container->getDefinitions() as $definition) {
$definition->setArguments(
$this->processArguments($definition->getArguments())
);
$calls = array();
foreach ($definition->getMethodCalls() as $k => $call) {
try {
$calls[$k] = $this->processArguments($call, true);
} catch (\RuntimeException $ignore) {
// this call is simply removed
}
}
$definition->setMethodCalls($calls);
}
}
protected function processArguments(array $arguments, $inMethodCall = false)
{
foreach ($arguments as $k => $argument) {
if (is_array($argument)) {
$arguments[$k] = $this->processArguments($argument, $inMethodCall);
} else if ($argument instanceof Reference) {
$id = (string) $argument;
if (in_array($id, $this->exceptions, true)) {
continue;
}
$invalidBehavior = $argument->getInvalidBehavior();
$exists = $this->container->has($id);
if ($exists && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
$arguments[$k] = new Reference($id);
} else if (!$exists && ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) {
$arguments[$k] = null;
} else if (!$exists && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) {
if ($inMethodCall) {
throw new \RuntimeException('Method shouldn\'t be called.');
}
$arguments[$k] = null;
}
}
}
return $arguments;
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Replaces all references to aliases with references to the actual service.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ResolveReferencesToAliasesPass implements CompilerPassInterface
{
protected $container;
public function process(ContainerBuilder $container)
{
$this->container = $container;
foreach ($container->getDefinitions() as $id => $definition)
{
$definition->setArguments($this->processArguments($definition->getArguments()));
$definition->setMethodCalls($this->processArguments($definition->getMethodCalls()));
}
}
protected function processArguments(array $arguments)
{
foreach ($arguments as $k => $argument) {
if (is_array($argument)) {
$arguments[$k] = $this->processArguments($argument);
} else if ($argument instanceof Reference) {
$defId = $this->getDefinitionId($id = (string) $argument);
if ($defId !== $id) {
$arguments[$k] = new Reference($defId, $argument->getInvalidBehavior());
}
}
}
return $arguments;
}
protected function getDefinitionId($id)
{
if ($this->container->hasAlias($id)) {
return $this->getDefinitionId($this->container->getAlias($id));
}
return $id;
}
}

View File

@ -2,6 +2,8 @@
namespace Symfony\Component\DependencyInjection\Dumper;
use Symfony\Component\DependencyInjection\Variable;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerInterface;
@ -21,9 +23,35 @@ use Symfony\Component\DependencyInjection\Parameter;
* PhpDumper dumps a service container as a PHP class.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class PhpDumper extends Dumper
{
/**
* Characters that might appear in the generated variable name as first character
* @var string
*/
const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz';
/**
* Characters that might appear in the generated variable name as any but the first character
* @var string
*/
const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
protected $inlinedDefinitions;
protected $definitionVariables;
protected $referenceVariables;
protected $variableCount;
protected $reservedVariables = array('instance');
public function __construct(ContainerBuilder $container)
{
parent::__construct($container);
$this->inlinedDefinitions = new \SplObjectStorage;
}
/**
* Dumps the service container as a PHP class.
*
@ -88,16 +116,123 @@ EOF;
return $code;
}
protected function addServiceLocalTempVariables($cId, $definition)
{
static $template = " \$%s = %s;\n";
$localDefinitions = array_merge(
array($definition),
$this->getInlinedDefinitions($definition)
);
$calls = $behavior = array();
foreach ($localDefinitions as $iDefinition) {
$this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior);
$this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
}
$code = '';
foreach ($calls as $id => $callCount) {
if ('service_container' === $id || $id === $cId) {
continue;
}
if ($callCount > 1) {
$name = $this->getNextVariableName();
$this->referenceVariables[$id] = new Variable($name);
if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
$code .= sprintf($template, $name, $this->getServiceCall($id));
} else {
$code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
}
}
}
if ('' !== $code) {
$code .= "\n";
}
return $code;
}
protected function addServiceInclude($id, $definition)
{
if (null !== $definition->getFile()) {
return sprintf(" require_once %s;\n\n", $this->dumpValue($definition->getFile()));
$template = " require_once %s;\n";
$code = '';
if (null !== $file = $definition->getFile()) {
$code .= sprintf($template, $this->dumpValue($file));
}
foreach ($this->getInlinedDefinitions($definition) as $definition) {
if (null !== $file = $definition->getFile()) {
$code .= sprintf($template, $this->dumpValue($file));
}
}
if ('' !== $code) {
$code .= "\n";
}
return $code;
}
protected function addServiceInlinedDefinitions($id, $definition)
{
$code = '';
$variableMap = $this->definitionVariables;
$c = 1;
foreach ($this->getInlinedDefinitions($definition) as $sDefinition) {
$class = $this->dumpValue($sDefinition->getClass());
if (count($sDefinition->getMethodCalls()) > 0 || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
$name = $this->getNextVariableName();
$variableMap->offsetSet($sDefinition, new Variable($name));
// a construct like:
// $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
// this is an indication for a wrong implementation, you can circumvent this problem
// by setting up your service structure like this:
// $b = new ServiceB();
// $a = new ServiceA(ServiceB $b);
// $b->setServiceA(ServiceA $a);
if ($this->hasReference($id, $sDefinition->getArguments())) {
throw new \RuntimeException('Unresolvable reference detected in service definition for '.$id);
}
$arguments = array();
foreach ($sDefinition->getArguments() as $argument) {
$arguments[] = $this->dumpValue($argument);
}
if (null !== $sDefinition->getFactoryMethod()) {
if (null !== $sDefinition->getFactoryService()) {
$code .= sprintf(" \$%s = %s->%s(%s);\n", $name, $this->getServiceCall($sDefinition->getFactoryService()), $sDefinition->getFactoryMethod(), implode(', ', $arguments));
} else {
$code .= sprintf(" \$%s = call_user_func(array(%s, '%s')%s);\n", $name, $class, $sDefinition->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
}
} elseif (false !== strpos($class, '$')) {
$code .= sprintf(" \$class = %s;\n \$%s = new \$class(%s);\n", $class, $name, implode(', ', $arguments));
} else {
$code .= sprintf(" \$%s = new \\%s(%s);\n", $name, substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
}
if (!$this->hasReference($id, $sDefinition->getMethodCalls())) {
$code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
$code .= $this->addServiceConfigurator(null, $sDefinition, $name);
}
$code .= "\n";
}
}
return $code;
}
protected function addServiceReturn($id, $definition)
{
if (!$definition->getMethodCalls() && !$definition->getConfigurator()) {
if ($this->isSimpleInstance($id, $definition)) {
return " }\n";
}
@ -117,7 +252,7 @@ EOF;
$arguments[] = $this->dumpValue($value);
}
$simple = !$definition->getMethodCalls() && !$definition->getConfigurator();
$simple = $this->isSimpleInstance($id, $definition);
$instantiation = '';
if ($definition->isShared()) {
@ -145,10 +280,29 @@ EOF;
$code = sprintf(" $return{$instantiation}new \\%s(%s);\n", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
}
if (!$simple) {
$code .= "\n";
}
return $code;
}
protected function addServiceMethodCalls($id, $definition)
protected function isSimpleInstance($id, $definition)
{
foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
continue;
}
if ($sDefinition->getMethodCalls() || $sDefinition->getConfigurator()) {
return false;
}
}
return true;
}
protected function addServiceMethodCalls($id, $definition, $variableName = 'instance')
{
$calls = '';
foreach ($definition->getMethodCalls() as $call) {
@ -157,17 +311,42 @@ EOF;
$arguments[] = $this->dumpValue($value);
}
$calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$instance->%s(%s);\n", $call[0], implode(', ', $arguments)));
$calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
}
if (!$this->container->isFrozen() && count($this->container->getInterfaceInjectors()) > 0) {
$calls = sprintf("\n \$this->applyInterfaceInjectors(\$instance);\n");
$calls = sprintf("\n \$this->applyInterfaceInjectors(\$%s);\n", $variableName);
}
return $calls;
}
protected function addServiceConfigurator($id, $definition)
protected function addServiceInlinedDefinitionsSetup($id, $definition)
{
$this->referenceVariables[$id] = new Variable('instance');
$code = '';
foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
if (!$this->hasReference($id, $iDefinition->getMethodCalls())) {
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));
}
}
if ('' !== $code) {
$code .= "\n";
}
return $code;
}
protected function addServiceConfigurator($id, $definition, $variableName = 'instance')
{
if (!$callable = $definition->getConfigurator()) {
return '';
@ -175,18 +354,21 @@ EOF;
if (is_array($callable)) {
if (is_object($callable[0]) && $callable[0] instanceof Reference) {
return sprintf(" %s->%s(\$instance);\n", $this->getServiceCall((string) $callable[0]), $callable[1]);
return sprintf(" %s->%s(\$%s);\n", $this->getServiceCall((string) $callable[0]), $callable[1], $variableName);
} else {
return sprintf(" call_user_func(array(%s, '%s'), \$instance);\n", $this->dumpValue($callable[0]), $callable[1]);
return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
}
} else {
return sprintf(" %s(\$instance);\n", $callable);
return sprintf(" %s(\$%s);\n", $callable, $variableName);
}
}
protected function addService($id, $definition)
{
$name = Container::camelize($id);
$this->definitionVariables = new \SplObjectStorage();
$this->referenceVariables = array();
$this->variableCount = 0;
$return = '';
if ($class = $definition->getClass()) {
@ -197,7 +379,7 @@ EOF;
$doc = '';
if ($definition->isShared()) {
$doc = <<<EOF
$doc .= <<<EOF
*
* This service is shared.
@ -205,6 +387,16 @@ EOF;
EOF;
}
if (!$definition->isPublic()) {
$doc .= <<<EOF
*
* 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.
EOF;
}
$code = <<<EOF
/**
@ -219,12 +411,18 @@ EOF;
$code .=
$this->addServiceInclude($id, $definition).
$this->addServiceLocalTempVariables($id, $definition).
$this->addServiceInlinedDefinitions($id, $definition).
$this->addServiceInstance($id, $definition).
$this->addServiceInlinedDefinitionsSetup($id, $definition).
$this->addServiceMethodCalls($id, $definition).
$this->addServiceConfigurator($id, $definition).
$this->addServiceReturn($id, $definition)
;
$this->definitionVariables = null;
$this->referenceVariables = null;
return $code;
}
@ -255,16 +453,20 @@ EOF;
protected function addServices()
{
$code = '';
$publicServices = $privateServices = $aliasServices = '';
foreach ($this->container->getDefinitions() as $id => $definition) {
$code .= $this->addService($id, $definition);
if ($definition->isPublic()) {
$publicServices .= $this->addService($id, $definition);
} else {
$privateServices .= $this->addService($id, $definition);
}
}
foreach ($this->container->getAliases() as $alias => $id) {
$code .= $this->addServiceAlias($alias, $id);
$aliasServices .= $this->addServiceAlias($alias, $id);
}
return $code;
return $publicServices.$aliasServices.$privateServices;
}
protected function addTags()
@ -371,6 +573,10 @@ EOF;
foreach ($parameters as $key => $value) {
if (is_array($value)) {
$value = $this->exportParameters($value, $indent + 4);
} elseif ($value instanceof Variable) {
throw new \InvalidArgumentException(sprintf('you cannot dump a container with parameters that contain variable references. Variable "%s" found.', $variable));
} elseif ($value instanceof Definition) {
throw new \InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain service definitions. Definition for "%s" found.', $value->getClass()));
} elseif ($value instanceof Reference) {
throw new \InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain references to other services (reference to service %s found).', $value));
} else {
@ -408,6 +614,80 @@ EOF;
return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code);
}
protected function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
{
foreach ($arguments as $argument) {
if (is_array($argument)) {
$this->getServiceCallsFromArguments($argument, $calls, $behavior);
} else if ($argument instanceof Reference) {
$id = (string) $argument;
if (!isset($calls[$id])) {
$calls[$id] = 0;
}
if (!isset($behavior[$id])) {
$behavior[$id] = $argument->getInvalidBehavior();
} else if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
$behavior[$id] = $argument->getInvalidBehavior();
}
$calls[$id] += 1;
}
}
}
protected function getInlinedDefinitions(Definition $definition)
{
if (false === $this->inlinedDefinitions->contains($definition)) {
$definitions = $this->getDefinitionsFromArguments($definition->getArguments());
foreach ($definition->getMethodCalls() as $arguments) {
$definitions = array_merge($definitions, $this->getDefinitionsFromArguments($arguments));
}
$this->inlinedDefinitions->offsetSet($definition, $definitions);
return $definitions;
}
return $this->inlinedDefinitions->offsetGet($definition);
}
protected function getDefinitionsFromArguments(array $arguments)
{
$definitions = array();
foreach ($arguments as $argument) {
if (is_array($argument)) {
$definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument));
} else if ($argument instanceof Definition) {
$definitions = array_merge(
$definitions,
$this->getInlinedDefinitions($argument),
array($argument)
);
}
}
return $definitions;
}
protected function hasReference($id, array $arguments)
{
foreach ($arguments as $argument) {
if (is_array($argument)) {
if ($this->hasReference($id, $argument)) {
return true;
}
} else if ($argument instanceof Reference) {
if ($id === (string) $argument) {
return true;
}
}
}
return false;
}
protected function dumpValue($value, $interpolate = true)
{
if (is_array($value)) {
@ -417,7 +697,43 @@ EOF;
}
return sprintf('array(%s)', implode(', ', $code));
} elseif (is_object($value) && $value instanceof Definition) {
if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);
}
if (count($value->getMethodCalls()) > 0) {
throw new \RuntimeException('Cannot dump definitions which have method calls.');
}
if (null !== $value->getConfigurator()) {
throw new \RuntimeException('Cannot dump definitions which have a configurator.');
}
$arguments = array();
foreach ($value->getArguments() as $argument) {
$arguments[] = $this->dumpValue($argument);
}
$class = $this->dumpValue($value->getClass());
if (false !== strpos($class, '$')) {
throw new \RuntimeException('Cannot dump definitions which have a variable class name.');
}
if (null !== $value->getFactoryMethod()) {
if (null !== $value->getFactoryService()) {
return sprintf("%s->%s(%s)", $this->getServiceCall($value->getFactoryService()), $value->getFactoryMethod(), implode(', ', $arguments));
} else {
return sprintf("call_user_func(array(%s, '%s')%s)", $class, $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
}
}
return sprintf("new \\%s(%s)", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
} elseif (is_object($value) && $value instanceof Variable) {
return '$'.$value;
} elseif (is_object($value) && $value instanceof Reference) {
if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) {
return $this->dumpValue($this->referenceVariables[$id], $interpolate);
}
return $this->getServiceCall((string) $value, $value);
} elseif (is_object($value) && $value instanceof Parameter) {
return $this->dumpParameter($value);
@ -472,4 +788,45 @@ EOF;
return sprintf('$this->get(\'%s\')', $id);
}
}
/**
* Returns the next name to use
*
* @return string
*/
protected function getNextVariableName()
{
$firstChars = self::FIRST_CHARS;
$firstCharsLength = strlen($firstChars);
$nonFirstChars = self::NON_FIRST_CHARS;
$nonFirstCharsLength = strlen($nonFirstChars);
while (true)
{
$name = '';
$i = $this->variableCount;
if ('' === $name)
{
$name .= $firstChars[$i%$firstCharsLength];
$i = intval($i/$firstCharsLength);
}
while ($i > 0)
{
$i -= 1;
$name .= $nonFirstChars[$i%$nonFirstCharsLength];
$i = intval($i/$nonFirstCharsLength);
}
$this->variableCount += 1;
// check that the name is not reserved
if (in_array($name, $this->reservedVariables, true)) {
continue;
}
return $name;
}
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Symfony\Component\DependencyInjection;
class Variable
{
protected $name;
public function __construct($name)
{
$this->name = $name;
}
public function __toString()
{
return $this->name;
}
}

View File

@ -0,0 +1,83 @@
<?php
namespace Symfony\Tests\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class InlineServiceDefinitionsPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container
->register('inlinable.service')
->setPublic(false)
;
$container
->register('service')
->setArguments(array(new Reference('inlinable.service')))
;
$this->process($container);
$arguments = $container->getDefinition('service')->getArguments();
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $arguments[0]);
$this->assertSame($container->getDefinition('inlinable.service'), $arguments[0]);
}
public function testProcessDoesNotInlinesWhenAliasedServiceIsNotShared()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
;
$container->setAlias('moo', 'foo');
$container
->register('service')
->setArguments(array($ref = new Reference('foo')))
;
$this->process($container);
$arguments = $container->getDefinition('service')->getArguments();
$this->assertSame($ref, $arguments[0]);
}
public function testProcessDoesInlineNonSharedService()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setShared(false)
;
$container
->register('bar')
->setPublic(false)
->setShared(false)
;
$container->setAlias('moo', 'bar');
$container
->register('service')
->setArguments(array(new Reference('foo'), $ref = new Reference('moo'), new Reference('bar')))
;
$this->process($container);
$arguments = $container->getDefinition('service')->getArguments();
$this->assertSame($container->getDefinition('foo'), $arguments[0]);
$this->assertSame($ref, $arguments[1]);
$this->assertSame($container->getDefinition('bar'), $arguments[2]);
}
protected function process(ContainerBuilder $container)
{
$pass = new InlineServiceDefinitionsPass();
$pass->process($container);
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace Symfony\Tests\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class RemoveUnusedDefinitionsPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
;
$container
->register('bar')
->setPublic(false)
;
$container
->register('moo')
->setArguments(array(new Reference('bar')))
;
$this->process($container);
$this->assertFalse($container->hasDefinition('foo'));
$this->assertTrue($container->hasDefinition('bar'));
$this->assertTrue($container->hasDefinition('moo'));
}
public function testProcessRemovesUnusedDefinitionsRecursively()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
;
$container
->register('bar')
->setArguments(array(new Reference('foo')))
->setPublic(false)
;
$this->process($container);
$this->assertFalse($container->hasDefinition('foo'));
$this->assertFalse($container->hasDefinition('bar'));
}
public function testProcessWorksWithInlinedDefinitions()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
;
$container
->register('bar')
->setArguments(array(new Definition(null, array(new Reference('foo')))))
;
$this->process($container);
$this->assertTrue($container->hasDefinition('foo'));
$this->assertTrue($container->hasDefinition('bar'));
}
protected function process(ContainerBuilder $container)
{
$pass = new RemoveUnusedDefinitionsPass();
$pass->process($container);
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace Symfony\Tests\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Compiler\ResolveInvalidReferencesPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ResolveInvalidReferencesPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$def = $container
->register('foo')
->setArguments(array(new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)))
->addMethodCall('foo', array(new Reference('moo', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))
;
$this->process($container);
$arguments = $def->getArguments();
$this->assertNull($arguments[0]);
$this->assertEquals(0, count($def->getMethodCalls()));
}
public function testProcessIgnoreNonExistentServices()
{
$container = new ContainerBuilder();
$def = $container
->register('foo')
->setArguments(array(new Reference('bar')))
;
$this->process($container);
$arguments = $def->getArguments();
$this->assertEquals('bar', (string) $arguments[0]);
}
public function testProcessIgnoresExceptions()
{
$container = new ContainerBuilder();
$def = $container
->register('foo')
->setArguments(array(new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)))
;
$this->process($container, array('bar'));
$arguments = $def->getArguments();
$this->assertEquals('bar', (string) $arguments[0]);
}
protected function process(ContainerBuilder $container, array $exceptions = array())
{
$pass = new ResolveInvalidReferencesPass($exceptions);
$pass->process($container);
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace Symfony\Tests\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Compiler\ResolveReferencesToAliasesPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ResolveReferencesToAliasesPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container->setAlias('bar', 'foo');
$def = $container
->register('moo')
->setArguments(array(new Reference('bar')))
;
$this->process($container);
$arguments = $def->getArguments();
$this->assertEquals('foo', (string) $arguments[0]);
}
public function testProcessRecursively()
{
$container = new ContainerBuilder();
$container->setAlias('bar', 'foo');
$container->setAlias('moo', 'bar');
$def = $container
->register('foobar')
->setArguments(array(new Reference('moo')))
;
$this->process($container);
$arguments = $def->getArguments();
$this->assertEquals('foo', (string) $arguments[0]);
}
protected function process(ContainerBuilder $container)
{
$pass = new ResolveReferencesToAliasesPass();
$pass->process($container);
}
}

View File

@ -42,8 +42,8 @@ $container->
register('method_call1', 'FooClass')->
setFile(realpath(__DIR__.'/../includes/foo.php'))->
addMethodCall('setBar', array(new Reference('foo')))->
addMethodCall('setBar', array(new Reference('foo', ContainerInterface::NULL_ON_INVALID_REFERENCE)))->
addMethodCall('setBar', array(new Reference('foo', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))->
addMethodCall('setBar', array(new Reference('foo2', ContainerInterface::NULL_ON_INVALID_REFERENCE)))->
addMethodCall('setBar', array(new Reference('foo3', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))->
addMethodCall('setBar', array(new Reference('foobaz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))
;
$container->

View File

@ -10,13 +10,15 @@ digraph sc {
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_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"];
node_foobaz [label="foobaz\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foo -> node_foo_baz [label="" style="filled"];
node_foo -> node_service_container [label="" style="filled"];
node_foo -> node_bar [label="setBar()" style="dashed"];
node_bar -> node_foo_baz [label="" style="filled"];
node_method_call1 -> node_foo [label="setBar()" style="dashed"];
node_method_call1 -> node_foo [label="setBar()" style="dashed"];
node_method_call1 -> node_foo [label="setBar()" style="dashed"];
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"];
}

View File

@ -31,6 +31,7 @@ class ProjectServiceContainer extends Container implements TaggedContainerInterf
protected function getFooService()
{
$instance = call_user_func(array('FooClass', 'getInstance'), 'foo', $this->get('foo.baz'), array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo'), 'bar' => $this->getParameter('foo')), true, $this);
$instance->setBar($this->get('bar'));
$instance->initialize();
sc_configure($instance);
@ -49,6 +50,7 @@ class ProjectServiceContainer extends Container implements TaggedContainerInterf
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;
@ -65,6 +67,7 @@ class ProjectServiceContainer extends Container implements TaggedContainerInterf
protected function getFoo_BazService()
{
$this->services['foo.baz'] = $instance = call_user_func(array($this->getParameter('baz_class'), 'getInstance'));
call_user_func(array($this->getParameter('baz_class'), 'configureStatic1'), $instance);
return $instance;
@ -97,10 +100,11 @@ class ProjectServiceContainer extends Container implements TaggedContainerInterf
require_once '%path%foo.php';
$this->services['method_call1'] = $instance = new \FooClass();
$instance->setBar($this->get('foo'));
$instance->setBar($this->get('foo', ContainerInterface::NULL_ON_INVALID_REFERENCE));
if ($this->has('foo')) {
$instance->setBar($this->get('foo', ContainerInterface::NULL_ON_INVALID_REFERENCE));
$instance->setBar($this->get('foo2', ContainerInterface::NULL_ON_INVALID_REFERENCE));
if ($this->has('foo3')) {
$instance->setBar($this->get('foo3', ContainerInterface::NULL_ON_INVALID_REFERENCE));
}
if ($this->has('foobaz')) {
$instance->setBar($this->get('foobaz', ContainerInterface::NULL_ON_INVALID_REFERENCE));

View File

@ -34,6 +34,7 @@ class ProjectServiceContainer extends Container implements TaggedContainerInterf
protected function getFooService()
{
$this->services['foo'] = $instance = new \FooClass();
$instance->setBar('someValue');
return $instance;

View File

@ -43,10 +43,10 @@
<argument type="service" id="foo" />
</call>
<call method="setBar">
<argument type="service" id="foo" on-invalid="null" />
<argument type="service" id="foo2" on-invalid="null" />
</call>
<call method="setBar">
<argument type="service" id="foo" on-invalid="ignore" />
<argument type="service" id="foo3" on-invalid="ignore" />
</call>
<call method="setBar">
<argument type="service" id="foobaz" on-invalid="ignore" />

View File

@ -32,8 +32,8 @@ services:
file: %path%foo.php
calls:
- [setBar, ['@foo']]
- [setBar, ['@?foo']]
- [setBar, ['@?foo']]
- [setBar, ['@?foo2']]
- [setBar, ['@?foo3']]
- [setBar, ['@?foobaz']]
factory_service: