DI container optimization
This commit is contained in:
parent
4b78c4376f
commit
c5ef113b18
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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))
|
||||
;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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')
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ class TemplatingExtension extends \Twig_Extension
|
||||
|
||||
public function getTemplating()
|
||||
{
|
||||
return $this->container->get('templating.engine');
|
||||
return $this->container->get('templating');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -31,10 +31,13 @@ class PassConfig
|
||||
|
||||
$this->optimizationPasses = array(
|
||||
new ResolveParameterPlaceHoldersPass(),
|
||||
new ResolveReferencesToAliasesPass(),
|
||||
new ResolveInterfaceInjectorsPass(),
|
||||
new ResolveInvalidReferencesPass(),
|
||||
);
|
||||
|
||||
$this->removingPasses = array(
|
||||
new InlineServiceDefinitionsPass(),
|
||||
new RemoveUnusedDefinitionsPass(),
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/Symfony/Component/DependencyInjection/Variable.php
Normal file
18
src/Symfony/Component/DependencyInjection/Variable.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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->
|
||||
|
@ -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"];
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -34,6 +34,7 @@ class ProjectServiceContainer extends Container implements TaggedContainerInterf
|
||||
protected function getFooService()
|
||||
{
|
||||
$this->services['foo'] = $instance = new \FooClass();
|
||||
|
||||
$instance->setBar('someValue');
|
||||
|
||||
return $instance;
|
||||
|
@ -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" />
|
||||
|
@ -32,8 +32,8 @@ services:
|
||||
file: %path%foo.php
|
||||
calls:
|
||||
- [setBar, ['@foo']]
|
||||
- [setBar, ['@?foo']]
|
||||
- [setBar, ['@?foo']]
|
||||
- [setBar, ['@?foo2']]
|
||||
- [setBar, ['@?foo3']]
|
||||
- [setBar, ['@?foobaz']]
|
||||
|
||||
factory_service:
|
||||
|
Reference in New Issue
Block a user