diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
new file mode 100644
index 0000000000..11a2197d04
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * @author Nicolas Grekas
+ */
+abstract class AbstractRecursivePass implements CompilerPassInterface
+{
+ protected $container;
+ protected $currentId;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function process(ContainerBuilder $container)
+ {
+ $this->container = $container;
+
+ try {
+ $this->processValue($container->getDefinitions(), true);
+ } finally {
+ $this->container = null;
+ }
+ }
+
+ /**
+ * Processes a value found in a definition tree.
+ *
+ * @param mixed $value
+ * @param bool $isRoot
+ *
+ * @return mixed The processed value
+ */
+ protected function processValue($value, $isRoot = false)
+ {
+ if (is_array($value)) {
+ foreach ($value as $k => $v) {
+ if ($isRoot) {
+ $this->currentId = $k;
+ }
+ if ($v !== $processedValue = $this->processValue($v, $isRoot)) {
+ $value[$k] = $processedValue;
+ }
+ }
+ } elseif ($value instanceof ArgumentInterface) {
+ $value->setValues($this->processValue($value->getValues()));
+ } elseif ($value instanceof Definition) {
+ $value->setArguments($this->processValue($value->getArguments()));
+ $value->setProperties($this->processValue($value->getProperties()));
+ $value->setMethodCalls($this->processValue($value->getMethodCalls()));
+
+ if ($v = $value->getFactory()) {
+ $value->setFactory($this->processValue($v));
+ }
+ if ($v = $value->getConfigurator()) {
+ $value->setConfigurator($this->processValue($v));
+ }
+ }
+
+ return $value;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php
index aa7dccfd64..e6333c9133 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php
@@ -25,14 +25,13 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
*
* @author Johannes M. Schmitt
*/
-class AnalyzeServiceReferencesPass implements RepeatablePassInterface
+class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements RepeatablePassInterface
{
private $graph;
- private $container;
- private $currentId;
private $currentDefinition;
private $repeatedPass;
private $onlyConstructorArguments;
+ private $lazy;
/**
* @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls
@@ -60,68 +59,60 @@ class AnalyzeServiceReferencesPass implements RepeatablePassInterface
$this->container = $container;
$this->graph = $container->getCompiler()->getServiceReferenceGraph();
$this->graph->clear();
-
- foreach ($container->getDefinitions() as $id => $definition) {
- if ($definition->isSynthetic() || $definition->isAbstract()) {
- continue;
- }
-
- $this->currentId = $id;
- $this->currentDefinition = $definition;
-
- $this->processArguments($definition->getArguments());
- if (is_array($definition->getFactory())) {
- $this->processArguments($definition->getFactory());
- }
-
- if (!$this->onlyConstructorArguments) {
- $this->processArguments($definition->getMethodCalls());
- $this->processArguments($definition->getProperties());
- if ($definition->getConfigurator()) {
- $this->processArguments(array($definition->getConfigurator()));
- }
- }
- }
+ $this->lazy = false;
foreach ($container->getAliases() as $id => $alias) {
$this->graph->connect($id, $alias, (string) $alias, $this->getDefinition((string) $alias), null);
}
+
+ parent::process($container);
}
- /**
- * Processes service definitions for arguments to find relationships for the service graph.
- *
- * @param array $arguments An array of Reference or Definition objects relating to service definitions
- * @param bool $lazy Whether the references nested in the arguments should be considered lazy or not
- */
- private function processArguments(array $arguments, $lazy = false)
+ protected function processValue($value, $isRoot = false)
{
- foreach ($arguments as $argument) {
- if (is_array($argument)) {
- $this->processArguments($argument, $lazy);
- } elseif ($argument instanceof ArgumentInterface) {
- $this->processArguments($argument->getValues(), true);
- } elseif ($argument instanceof Reference) {
- $targetDefinition = $this->getDefinition((string) $argument);
+ $lazy = $this->lazy;
- $this->graph->connect(
- $this->currentId,
- $this->currentDefinition,
- $this->getDefinitionId((string) $argument),
- $targetDefinition,
- $argument,
- $lazy || ($targetDefinition && $targetDefinition->isLazy())
- );
- } elseif ($argument instanceof Definition) {
- $this->processArguments($argument->getArguments());
- $this->processArguments($argument->getMethodCalls());
- $this->processArguments($argument->getProperties());
+ if ($value instanceof ArgumentInterface) {
+ $this->lazy = true;
+ parent::processValue($value);
+ $this->lazy = $lazy;
- if (is_array($argument->getFactory())) {
- $this->processArguments($argument->getFactory());
- }
- }
+ return $value;
}
+ if ($value instanceof Reference) {
+ $targetDefinition = $this->getDefinition((string) $value);
+
+ $this->graph->connect(
+ $this->currentId,
+ $this->currentDefinition,
+ $this->getDefinitionId((string) $value),
+ $targetDefinition,
+ $value,
+ $this->lazy || ($targetDefinition && $targetDefinition->isLazy())
+ );
+
+ return $value;
+ }
+ if (!$value instanceof Definition) {
+ return parent::processValue($value, $isRoot);
+ }
+ if ($isRoot) {
+ if ($value->isSynthetic() || $value->isAbstract()) {
+ return $value;
+ }
+ $this->currentDefinition = $value;
+ }
+ $this->lazy = false;
+
+ if ($this->onlyConstructorArguments) {
+ $this->processValue($value->getFactory());
+ $this->processValue($value->getArguments());
+ } else {
+ parent::processValue($value, $isRoot);
+ }
+ $this->lazy = $lazy;
+
+ return $value;
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
index 348e8917db..2e97afc0b8 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
@@ -22,12 +22,11 @@ use Symfony\Component\DependencyInjection\Reference;
*
* @author Kévin Dunglas
*/
-class AutowirePass implements CompilerPassInterface
+class AutowirePass extends AbstractRecursivePass implements CompilerPassInterface
{
/**
* @var ContainerBuilder
*/
- private $container;
private $reflectionClasses = array();
private $definedTypes = array();
private $types;
@@ -42,17 +41,11 @@ class AutowirePass implements CompilerPassInterface
spl_autoload_register($throwingAutoloader);
try {
- $this->container = $container;
- foreach ($container->getDefinitions() as $id => $definition) {
- if ($autowiredMethods = $definition->getAutowiredMethods()) {
- $this->completeDefinition($id, $definition, $autowiredMethods);
- }
- }
+ parent::process($container);
} finally {
spl_autoload_unregister($throwingAutoloader);
// Free memory and remove circular reference to container
- $this->container = null;
$this->reflectionClasses = array();
$this->definedTypes = array();
$this->types = null;
@@ -85,18 +78,16 @@ class AutowirePass implements CompilerPassInterface
}
/**
- * Wires the given definition.
- *
- * @param string $id
- * @param Definition $definition
- * @param string[] $autowiredMethods
- *
- * @throws RuntimeException
+ * {@inheritdoc}
*/
- private function completeDefinition($id, Definition $definition, array $autowiredMethods)
+ protected function processValue($value, $isRoot = false)
{
- if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
- return;
+ if (!$value instanceof Definition || !$autowiredMethods = $value->getAutowiredMethods()) {
+ return parent::processValue($value, $isRoot);
+ }
+
+ if (!$reflectionClass = $this->getReflectionClass($isRoot ? $this->currentId : null, $value)) {
+ return parent::processValue($value, $isRoot);
}
if ($this->container->isTrackingResources()) {
@@ -104,27 +95,28 @@ class AutowirePass implements CompilerPassInterface
}
$methodsCalled = array();
- foreach ($definition->getMethodCalls() as $methodCall) {
- $methodsCalled[$methodCall[0]] = true;
+ foreach ($value->getMethodCalls() as $methodCall) {
+ $methodsCalled[strtolower($methodCall[0])] = true;
}
- foreach ($this->getMethodsToAutowire($id, $reflectionClass, $autowiredMethods) as $reflectionMethod) {
- if (!isset($methodsCalled[$reflectionMethod->name])) {
- $this->autowireMethod($id, $definition, $reflectionMethod);
+ foreach ($this->getMethodsToAutowire($reflectionClass, $autowiredMethods) as $reflectionMethod) {
+ if (!isset($methodsCalled[strtolower($reflectionMethod->name)])) {
+ $this->autowireMethod($value, $reflectionMethod);
}
}
+
+ return parent::processValue($value, $isRoot);
}
/**
* Gets the list of methods to autowire.
*
- * @param string $id
* @param \ReflectionClass $reflectionClass
* @param string[] $configuredAutowiredMethods
*
* @return \ReflectionMethod[]
*/
- private function getMethodsToAutowire($id, \ReflectionClass $reflectionClass, array $configuredAutowiredMethods)
+ private function getMethodsToAutowire(\ReflectionClass $reflectionClass, array $configuredAutowiredMethods)
{
$found = array();
$regexList = array();
@@ -157,20 +149,19 @@ class AutowirePass implements CompilerPassInterface
if ($notFound = array_diff($configuredAutowiredMethods, $found)) {
$compiler = $this->container->getCompiler();
- $compiler->addLogMessage($compiler->getLoggingFormatter()->formatUnusedAutowiringPatterns($this, $id, $notFound));
+ $compiler->addLogMessage($compiler->getLoggingFormatter()->formatUnusedAutowiringPatterns($this, $this->currentId, $notFound));
}
}
/**
* Autowires the constructor or a setter.
*
- * @param string $id
* @param Definition $definition
* @param \ReflectionMethod $reflectionMethod
*
* @throws RuntimeException
*/
- private function autowireMethod($id, Definition $definition, \ReflectionMethod $reflectionMethod)
+ private function autowireMethod(Definition $definition, \ReflectionMethod $reflectionMethod)
{
if ($isConstructor = $reflectionMethod->isConstructor()) {
$arguments = $definition->getArguments();
@@ -189,7 +180,7 @@ class AutowirePass implements CompilerPassInterface
// no default value? Then fail
if (!$parameter->isOptional()) {
if ($isConstructor) {
- throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id));
+ throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $this->currentId));
}
return;
@@ -210,7 +201,7 @@ class AutowirePass implements CompilerPassInterface
$addMethodCall = true;
} else {
try {
- $value = $this->createAutowiredDefinition($typeHint, $id);
+ $value = $this->createAutowiredDefinition($typeHint);
$addMethodCall = true;
} catch (RuntimeException $e) {
if ($parameter->allowsNull()) {
@@ -338,38 +329,40 @@ class AutowirePass implements CompilerPassInterface
* Registers a definition for the type if possible or throws an exception.
*
* @param \ReflectionClass $typeHint
- * @param string $id
*
* @return Reference A reference to the registered definition
*
* @throws RuntimeException
*/
- private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
+ private function createAutowiredDefinition(\ReflectionClass $typeHint)
{
if (isset($this->ambiguousServiceTypes[$typeHint->name])) {
$classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
$matchingServices = implode(', ', $this->ambiguousServiceTypes[$typeHint->name]);
- throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices), 1);
+ throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $this->currentId, $classOrInterface, $matchingServices), 1);
}
if (!$typeHint->isInstantiable()) {
$classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
- throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface));
+ throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $this->currentId, $classOrInterface));
}
- $argumentId = sprintf('autowired.%s', $typeHint->name);
+ $currentId = $this->currentId;
+ $this->currentId = $argumentId = sprintf('autowired.%s', $typeHint->name);
$argumentDefinition = $this->container->register($argumentId, $typeHint->name);
$argumentDefinition->setPublic(false);
+ $argumentDefinition->setAutowired(true);
$this->populateAvailableType($argumentId, $argumentDefinition);
try {
- $this->completeDefinition($argumentId, $argumentDefinition, array('__construct'));
+ $this->processValue($argumentDefinition, true);
+ $this->currentId = $currentId;
} catch (RuntimeException $e) {
$classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
- $message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface);
+ $message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $this->currentId, $classOrInterface);
throw new RuntimeException($message, 0, $e);
}
@@ -379,14 +372,14 @@ class AutowirePass implements CompilerPassInterface
/**
* Retrieves the reflection class associated with the given service.
*
- * @param string $id
- * @param Definition $definition
+ * @param string|null $id
+ * @param Definition $definition
*
* @return \ReflectionClass|false
*/
private function getReflectionClass($id, Definition $definition)
{
- if (isset($this->reflectionClasses[$id])) {
+ if (null !== $id && isset($this->reflectionClasses[$id])) {
return $this->reflectionClasses[$id];
}
@@ -403,7 +396,11 @@ class AutowirePass implements CompilerPassInterface
$reflector = false;
}
- return $this->reflectionClasses[$id] = $reflector;
+ if (null !== $id) {
+ $this->reflectionClasses[$id] = $reflector;
+ }
+
+ return $reflector;
}
private function addServiceToAmbiguousType($id, $type)
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
index 0e4040740d..1da5100786 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
@@ -11,56 +11,27 @@
namespace Symfony\Component\DependencyInjection\Compiler;
-use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
-use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Checks that all references are pointing to a valid service.
*
* @author Johannes M. Schmitt
*/
-class CheckExceptionOnInvalidReferenceBehaviorPass implements CompilerPassInterface
+class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass implements CompilerPassInterface
{
- private $container;
- private $sourceId;
-
- public function process(ContainerBuilder $container)
+ protected function processValue($value, $isRoot = false)
{
- $this->container = $container;
+ if ($value instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
+ $destId = (string) $value;
- foreach ($container->getDefinitions() as $id => $definition) {
- $this->sourceId = $id;
- $this->processDefinition($definition);
- }
- }
-
- private function processDefinition(Definition $definition)
- {
- $this->processReferences($definition->getArguments());
- $this->processReferences($definition->getMethodCalls());
- $this->processReferences($definition->getProperties());
- }
-
- private function processReferences(array $arguments)
- {
- foreach ($arguments as $argument) {
- if (is_array($argument)) {
- $this->processReferences($argument);
- } elseif ($argument instanceof ArgumentInterface) {
- $this->processReferences($argument->getValues());
- } elseif ($argument instanceof Definition) {
- $this->processDefinition($argument);
- } elseif ($argument instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $argument->getInvalidBehavior()) {
- $destId = (string) $argument;
-
- if (!$this->container->has($destId)) {
- throw new ServiceNotFoundException($destId, $this->sourceId);
- }
+ if (!$this->container->has($destId)) {
+ throw new ServiceNotFoundException($destId, $this->currentId);
}
}
+
+ return parent::processValue($value, $isRoot);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php
index cd23246e7d..2a8a00eda3 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php
@@ -11,10 +11,8 @@
namespace Symfony\Component\DependencyInjection\Compiler;
-use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
@@ -25,75 +23,26 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
*
* @author Johannes M. Schmitt
*/
-class CheckReferenceValidityPass implements CompilerPassInterface
+class CheckReferenceValidityPass extends AbstractRecursivePass implements CompilerPassInterface
{
- private $container;
- private $currentId;
-
- /**
- * Processes the ContainerBuilder to validate References.
- *
- * @param ContainerBuilder $container
- */
- public function process(ContainerBuilder $container)
+ protected function processValue($value, $isRoot = false)
{
- $this->container = $container;
-
- foreach ($container->getDefinitions() as $id => $definition) {
- if ($definition->isSynthetic() || $definition->isAbstract()) {
- continue;
- }
-
- $this->currentId = $id;
-
- $this->validateReferences($definition->getArguments());
- $this->validateReferences($definition->getMethodCalls());
- $this->validateReferences($definition->getProperties());
+ if ($isRoot && $value instanceof Definition && ($value->isSynthetic() || $value->isAbstract())) {
+ return $value;
}
- }
+ if ($value instanceof Reference && $this->container->hasDefinition((string) $value)) {
+ $targetDefinition = $this->container->getDefinition((string) $value);
- /**
- * Validates an array of References.
- *
- * @param array $arguments An array of Reference objects
- *
- * @throws RuntimeException when there is a reference to an abstract definition.
- */
- private function validateReferences(array $arguments)
- {
- foreach ($arguments as $argument) {
- if (is_array($argument)) {
- $this->validateReferences($argument);
- } elseif ($argument instanceof ArgumentInterface) {
- $this->validateReferences($argument->getValues());
- } elseif ($argument instanceof Reference) {
- $targetDefinition = $this->getDefinition((string) $argument);
-
- if (null !== $targetDefinition && $targetDefinition->isAbstract()) {
- throw new RuntimeException(sprintf(
- 'The definition "%s" has a reference to an abstract definition "%s". '
- .'Abstract definitions cannot be the target of references.',
- $this->currentId,
- $argument
- ));
- }
+ if ($targetDefinition->isAbstract()) {
+ throw new RuntimeException(sprintf(
+ 'The definition "%s" has a reference to an abstract definition "%s". '
+ .'Abstract definitions cannot be the target of references.',
+ $this->currentId,
+ $value
+ ));
}
}
- }
- /**
- * Returns the Definition given an id.
- *
- * @param string $id Definition identifier
- *
- * @return Definition
- */
- private function getDefinition($id)
- {
- if (!$this->container->hasDefinition($id)) {
- return;
- }
-
- return $this->container->getDefinition($id);
+ return parent::processValue($value, $isRoot);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
index 8ba4547e99..367a2f0104 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
@@ -14,20 +14,15 @@ namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Inline service definitions where this is possible.
*
* @author Johannes M. Schmitt
*/
-class InlineServiceDefinitionsPass implements RepeatablePassInterface
+class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface
{
private $repeatedPass;
- private $graph;
- private $compiler;
- private $formatter;
- private $currentId;
/**
* {@inheritdoc}
@@ -38,77 +33,38 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
}
/**
- * Processes the ContainerBuilder for inline service definitions.
- *
- * @param ContainerBuilder $container
+ * {@inheritdoc}
*/
- public function process(ContainerBuilder $container)
+ protected function processValue($value, $isRoot = false)
{
- $this->compiler = $container->getCompiler();
- $this->formatter = $this->compiler->getLoggingFormatter();
- $this->graph = $this->compiler->getServiceReferenceGraph();
+ if ($value instanceof ArgumentInterface) {
+ $this->processValue($value->getValues());
- $container->setDefinitions($this->inlineArguments($container, $container->getDefinitions(), true));
- }
+ return $value;
+ }
+ if ($value instanceof Reference && $this->container->hasDefinition($id = (string) $value)) {
+ $compiler = $this->container->getCompiler();
+ $definition = $this->container->getDefinition($id);
- /**
- * Processes inline arguments.
- *
- * @param ContainerBuilder $container The ContainerBuilder
- * @param array $arguments An array of arguments
- * @param bool $isRoot If we are processing the root definitions or not
- *
- * @return array
- */
- private function inlineArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
- {
- foreach ($arguments as $k => $argument) {
- if ($isRoot) {
- $this->currentId = $k;
- }
- if (is_array($argument)) {
- $arguments[$k] = $this->inlineArguments($container, $argument);
- } elseif ($argument instanceof ArgumentInterface) {
- $this->inlineArguments($container, $argument->getValues());
- } elseif ($argument instanceof Reference) {
- if (!$container->hasDefinition($id = (string) $argument)) {
- continue;
+ if ($this->isInlineableDefinition($id, $definition, $compiler->getServiceReferenceGraph())) {
+ $compiler->addLogMessage($compiler->getLoggingFormatter()->formatInlineService($this, $id, $this->currentId));
+
+ if ($definition->isShared()) {
+ return $definition;
}
-
- if ($this->isInlineableDefinition($id, $definition = $container->getDefinition($id))) {
- $this->compiler->addLogMessage($this->formatter->formatInlineService($this, $id, $this->currentId));
-
- if ($definition->isShared()) {
- $arguments[$k] = $definition;
- } else {
- $arguments[$k] = clone $definition;
- }
- }
- } elseif ($argument instanceof Definition) {
- $argument->setArguments($this->inlineArguments($container, $argument->getArguments()));
- $argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls()));
- $argument->setProperties($this->inlineArguments($container, $argument->getProperties()));
-
- $configurator = $this->inlineArguments($container, array($argument->getConfigurator()));
- $argument->setConfigurator($configurator[0]);
-
- $factory = $this->inlineArguments($container, array($argument->getFactory()));
- $argument->setFactory($factory[0]);
+ $value = clone $definition;
}
}
- return $arguments;
+ return parent::processValue($value, $isRoot);
}
/**
* Checks if the definition is inlineable.
*
- * @param string $id
- * @param Definition $definition
- *
* @return bool If the definition is inlineable
*/
- private function isInlineableDefinition($id, Definition $definition)
+ private function isInlineableDefinition($id, Definition $definition, ServiceReferenceGraph $graph)
{
if (!$definition->isShared()) {
return true;
@@ -118,7 +74,7 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
return false;
}
- if (!$this->graph->hasNode($id)) {
+ if (!$graph->hasNode($id)) {
return true;
}
@@ -127,7 +83,7 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
}
$ids = array();
- foreach ($this->graph->getNode($id)->getInEdges() as $edge) {
+ foreach ($graph->getNode($id)->getInEdges() as $edge) {
$ids[] = $edge->getSourceNode()->getId();
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php
index c36e88b22a..a871e92dec 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\DependencyInjection\Compiler;
-use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
@@ -22,10 +21,9 @@ use Symfony\Component\DependencyInjection\Reference;
*
* @author Johannes M. Schmitt
*/
-class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface
+class ReplaceAliasByActualDefinitionPass extends AbstractRecursivePass implements CompilerPassInterface
{
- private $compiler;
- private $formatter;
+ private $replacements;
/**
* Process the Container to replace aliases with service definitions.
@@ -36,9 +34,6 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface
*/
public function process(ContainerBuilder $container)
{
- // Setup
- $this->compiler = $container->getCompiler();
- $this->formatter = $this->compiler->getLoggingFormatter();
// First collect all alias targets that need to be replaced
$seenAliasTargets = array();
$replacements = array();
@@ -72,60 +67,25 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface
$container->removeDefinition($targetId);
$replacements[$targetId] = $definitionId;
}
+ $this->replacements = $replacements;
- // Now replace target instances in all definitions
- foreach ($container->getDefinitions() as $definitionId => $definition) {
- $definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments()));
- $definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls()));
- $definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties()));
- $definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory()));
- }
+ parent::process($container);
+ $this->replacements = array();
}
/**
- * Recursively updates references in an array.
- *
- * @param array $replacements Table of aliases to replace
- * @param string $definitionId Identifier of this definition
- * @param array $arguments Where to replace the aliases
- *
- * @return array
+ * {@inheritdoc}
*/
- private function updateArgumentReferences(array $replacements, $definitionId, array $arguments)
+ protected function processValue($value, $isRoot = false)
{
- foreach ($arguments as $k => $argument) {
- // Handle recursion step
- if (is_array($argument)) {
- $arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument);
- continue;
- }
- if ($argument instanceof ArgumentInterface) {
- $argument->setValues($this->updateArgumentReferences($replacements, $definitionId, $argument->getValues()));
- continue;
- }
- // Skip arguments that don't need replacement
- if (!$argument instanceof Reference) {
- continue;
- }
- $referenceId = (string) $argument;
- if (!isset($replacements[$referenceId])) {
- continue;
- }
+ if ($value instanceof Reference && isset($this->replacements[$referenceId = (string) $value])) {
// Perform the replacement
- $newId = $replacements[$referenceId];
- $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior());
- $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $definitionId, $referenceId, $newId));
+ $newId = $this->replacements[$referenceId];
+ $value = new Reference($newId, $value->getInvalidBehavior());
+ $compiler = $this->container->getCompiler();
+ $compiler->addLogMessage($compiler->getLoggingFormatter()->formatUpdateReference($this, $this->currentId, $referenceId, $newId));
}
- return $arguments;
- }
-
- private function updateFactoryReference(array $replacements, $factory)
- {
- if (is_array($factory) && $factory[0] instanceof Reference && isset($replacements[$referenceId = (string) $factory[0]])) {
- $factory[0] = new Reference($replacements[$referenceId], $factory[0]->getInvalidBehavior());
- }
-
- return $factory;
+ return parent::processValue($value, $isRoot);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php
index f8b1ab6464..a05acece53 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php
@@ -11,10 +11,8 @@
namespace Symfony\Component\DependencyInjection\Compiler;
-use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Definition;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\ExceptionInterface;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@@ -25,83 +23,39 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
* @author Johannes M. Schmitt
* @author Nicolas Grekas
*/
-class ResolveDefinitionTemplatesPass implements CompilerPassInterface
+class ResolveDefinitionTemplatesPass extends AbstractRecursivePass implements CompilerPassInterface
{
- private $compiler;
- private $formatter;
- private $currentId;
-
- /**
- * Process the ContainerBuilder to replace ChildDefinition instances with their real Definition instances.
- *
- * @param ContainerBuilder $container
- */
- public function process(ContainerBuilder $container)
+ protected function processValue($value, $isRoot = false)
{
- $this->compiler = $container->getCompiler();
- $this->formatter = $this->compiler->getLoggingFormatter();
-
- $container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true));
- }
-
- /**
- * Resolves definition decorator arguments.
- *
- * @param ContainerBuilder $container The ContainerBuilder
- * @param array $arguments An array of arguments
- * @param bool $isRoot If we are processing the root definitions or not
- *
- * @return array
- */
- private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
- {
- foreach ($arguments as $k => $argument) {
+ if (!$value instanceof Definition) {
+ return parent::processValue($value, $isRoot);
+ }
+ if ($isRoot) {
+ // yes, we are specifically fetching the definition from the
+ // container to ensure we are not operating on stale data
+ $value = $this->container->getDefinition($this->currentId);
+ }
+ if ($value instanceof ChildDefinition) {
+ $value = $this->resolveDefinition($value);
if ($isRoot) {
- // yes, we are specifically fetching the definition from the
- // container to ensure we are not operating on stale data
- $arguments[$k] = $argument = $container->getDefinition($k);
- $this->currentId = $k;
- }
- if (is_array($argument)) {
- $arguments[$k] = $this->resolveArguments($container, $argument);
- } elseif ($argument instanceof ArgumentInterface) {
- $argument->setValues($this->resolveArguments($container, $argument->getValues()));
- } elseif ($argument instanceof Definition) {
- if ($argument instanceof ChildDefinition) {
- $arguments[$k] = $argument = $this->resolveDefinition($container, $argument);
- if ($isRoot) {
- $container->setDefinition($k, $argument);
- }
- }
- $argument->setArguments($this->resolveArguments($container, $argument->getArguments()));
- $argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls()));
- $argument->setProperties($this->resolveArguments($container, $argument->getProperties()));
-
- $configurator = $this->resolveArguments($container, array($argument->getConfigurator()));
- $argument->setConfigurator($configurator[0]);
-
- $factory = $this->resolveArguments($container, array($argument->getFactory()));
- $argument->setFactory($factory[0]);
+ $this->container->setDefinition($this->currentId, $value);
}
}
- return $arguments;
+ return parent::processValue($value, $isRoot);
}
/**
* Resolves the definition.
*
- * @param ContainerBuilder $container The ContainerBuilder
- * @param ChildDefinition $definition
- *
* @return Definition
*
- * @throws \RuntimeException When the definition is invalid
+ * @throws RuntimeException When the definition is invalid
*/
- private function resolveDefinition(ContainerBuilder $container, ChildDefinition $definition)
+ private function resolveDefinition(ChildDefinition $definition)
{
try {
- return $this->doResolveDefinition($container, $definition);
+ return $this->doResolveDefinition($definition);
} catch (ExceptionInterface $e) {
$r = new \ReflectionProperty($e, 'message');
$r->setAccessible(true);
@@ -111,22 +65,23 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface
}
}
- private function doResolveDefinition(ContainerBuilder $container, ChildDefinition $definition)
+ private function doResolveDefinition(ChildDefinition $definition)
{
- if (!$container->has($parent = $definition->getParent())) {
+ if (!$this->container->has($parent = $definition->getParent())) {
throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent));
}
- $parentDef = $container->findDefinition($parent);
+ $parentDef = $this->container->findDefinition($parent);
if ($parentDef instanceof ChildDefinition) {
$id = $this->currentId;
$this->currentId = $parent;
- $parentDef = $this->resolveDefinition($container, $parentDef);
- $container->setDefinition($parent, $parentDef);
+ $parentDef = $this->resolveDefinition($parentDef);
+ $this->container->setDefinition($parent, $parentDef);
$this->currentId = $id;
}
- $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent));
+ $compiler = $this->container->getCompiler();
+ $compiler->addLogMessage($compiler->getLoggingFormatter()->formatResolveInheritance($this, $this->currentId, $parent));
$def = new Definition();
// merge in parent definition
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php
index dc6e44e051..7e31cdaafe 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php
@@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@@ -26,6 +27,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
class ResolveInvalidReferencesPass implements CompilerPassInterface
{
private $container;
+ private $signalingException;
/**
* Process the ContainerBuilder to resolve invalid references.
@@ -35,86 +37,76 @@ class ResolveInvalidReferencesPass implements CompilerPassInterface
public function process(ContainerBuilder $container)
{
$this->container = $container;
- foreach ($container->getDefinitions() as $definition) {
- if ($definition->isSynthetic() || $definition->isAbstract()) {
- continue;
- }
+ $this->signalingException = new RuntimeException('Invalid reference.');
- $definition->setArguments(
- $this->processArguments($definition->getArguments())
- );
-
- $calls = array();
- foreach ($definition->getMethodCalls() as $call) {
- try {
- $calls[] = array($call[0], $this->processArguments($call[1], true));
- } catch (RuntimeException $e) {
- // this call is simply removed
- }
- }
- $definition->setMethodCalls($calls);
-
- $properties = array();
- foreach ($definition->getProperties() as $name => $value) {
- try {
- $value = $this->processArguments(array($value), true);
- $properties[$name] = reset($value);
- } catch (RuntimeException $e) {
- // ignore property
- }
- }
- $definition->setProperties($properties);
+ try {
+ $this->processValue($container->getDefinitions(), 1);
+ } finally {
+ $this->container = $this->signalingException = null;
}
}
/**
* Processes arguments to determine invalid references.
*
- * @param array $arguments An array of Reference objects
- * @param bool $inMethodCall
- * @param bool $inCollection
- *
- * @return array
- *
- * @throws RuntimeException When the config is invalid
+ * @throws RuntimeException When an invalid reference is found
*/
- private function processArguments(array $arguments, $inMethodCall = false, $inCollection = false)
+ private function processValue($value, $rootLevel = 0, $level = 0)
{
- $isNumeric = array_keys($arguments) === range(0, count($arguments) - 1);
+ if ($value instanceof Definition) {
+ if ($value->isSynthetic() || $value->isAbstract()) {
+ return $value;
+ }
+ $value->setArguments($this->processValue($value->getArguments(), 0));
+ $value->setProperties($this->processValue($value->getProperties(), 1));
+ $value->setMethodCalls($this->processValue($value->getMethodCalls(), 2));
+ } elseif (is_array($value)) {
+ $i = 0;
- foreach ($arguments as $k => $argument) {
- if (is_array($argument)) {
- $arguments[$k] = $this->processArguments($argument, $inMethodCall, true);
- } elseif ($argument instanceof ArgumentInterface) {
- $argument->setValues($this->processArguments($argument->getValues(), $inMethodCall, true));
- } elseif ($argument instanceof Reference) {
- $id = (string) $argument;
-
- $invalidBehavior = $argument->getInvalidBehavior();
- $exists = $this->container->has($id);
-
- // resolve invalid behavior
- if (!$exists && ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) {
- $arguments[$k] = null;
- } elseif (!$exists && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) {
- if ($inCollection) {
- unset($arguments[$k]);
- continue;
+ foreach ($value as $k => $v) {
+ try {
+ if (false !== $i && $k !== $i++) {
+ $i = false;
}
- if ($inMethodCall) {
- throw new RuntimeException('Method shouldn\'t be called.');
+ if ($v !== $processedValue = $this->processValue($v, $rootLevel, 1 + $level)) {
+ $value[$k] = $processedValue;
+ }
+ } catch (RuntimeException $e) {
+ if ($rootLevel < $level || ($rootLevel && !$level)) {
+ unset($value[$k]);
+ } elseif ($rootLevel) {
+ throw $e;
+ } else {
+ $value[$k] = null;
}
-
- $arguments[$k] = null;
}
}
+
+ // Ensure numerically indexed arguments have sequential numeric keys.
+ if (false !== $i) {
+ $value = array_values($value);
+ }
+ } elseif ($value instanceof ArgumentInterface) {
+ $value->setValues($this->processValue($value->getValues(), $rootLevel, 1 + $level));
+ } elseif ($value instanceof Reference) {
+ $id = (string) $value;
+
+ if ($this->container->has($id)) {
+ return $value;
+ }
+ $invalidBehavior = $value->getInvalidBehavior();
+
+ // resolve invalid behavior
+ if (ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) {
+ $value = null;
+ } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) {
+ if (0 < $level || $rootLevel) {
+ throw $this->signalingException;
+ }
+ $value = null;
+ }
}
- // Ensure numerically indexed arguments have sequential numeric keys.
- if ($isNumeric) {
- $arguments = array_values($arguments);
- }
-
- return $arguments;
+ return $value;
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php
index 0c5963cc11..a0fa3ae1a5 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
/**
@@ -19,52 +20,50 @@ use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
*
* @author Johannes M. Schmitt
*/
-class ResolveParameterPlaceHoldersPass implements CompilerPassInterface
+class ResolveParameterPlaceHoldersPass extends AbstractRecursivePass implements CompilerPassInterface
{
+ private $bag;
+
/**
- * Processes the ContainerBuilder to resolve parameter placeholders.
- *
- * @param ContainerBuilder $container
+ * {@inheritdoc}
*
* @throws ParameterNotFoundException
*/
public function process(ContainerBuilder $container)
{
- $parameterBag = $container->getParameterBag();
+ $this->bag = $container->getParameterBag();
- foreach ($container->getDefinitions() as $id => $definition) {
- try {
- $definition->setClass($parameterBag->resolveValue($definition->getClass()));
- $definition->setFile($parameterBag->resolveValue($definition->getFile()));
- $definition->setArguments($parameterBag->resolveValue($definition->getArguments()));
+ try {
+ parent::process($container);
- $factory = $definition->getFactory();
-
- if (is_array($factory) && isset($factory[0])) {
- $factory[0] = $parameterBag->resolveValue($factory[0]);
- $definition->setFactory($factory);
- }
-
- $calls = array();
- foreach ($definition->getMethodCalls() as $name => $arguments) {
- $calls[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($arguments);
- }
- $definition->setMethodCalls($calls);
-
- $definition->setProperties($parameterBag->resolveValue($definition->getProperties()));
- } catch (ParameterNotFoundException $e) {
- $e->setSourceId($id);
-
- throw $e;
+ $aliases = array();
+ foreach ($container->getAliases() as $name => $target) {
+ $this->currentId = $name;
+ $aliases[$this->bag->resolveValue($name)] = $this->bag->resolveValue($target);
}
+ $container->setAliases($aliases);
+ } catch (ParameterNotFoundException $e) {
+ $e->setSourceId($this->currentId);
+
+ throw $e;
}
- $aliases = array();
- foreach ($container->getAliases() as $name => $target) {
- $aliases[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($target);
- }
- $container->setAliases($aliases);
+ $this->bag->resolve();
+ $this->bag = null;
+ }
- $parameterBag->resolve();
+ protected function processValue($value, $isRoot = false)
+ {
+ if (is_string($value)) {
+ return $this->bag->resolveValue($value);
+ }
+ if ($value instanceof Definition) {
+ $value->setClass($this->bag->resolveValue($value->getClass()));
+ $value->setFile($this->bag->resolveValue($value->getFile()));
+ $value->setProperties($this->bag->resolveValue($value->getProperties()));
+ $value->setMethodCalls($this->bag->resolveValue($value->getMethodCalls()));
+ }
+
+ return parent::processValue($value, $isRoot);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
index 975e612083..c861ec1abf 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
@@ -316,7 +316,7 @@ class ProjectServiceContainer extends Container
return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function() {
yield 0 => 'foo';
yield 1 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->get('foo.baz')) && false ?: '_'};
- yield 2 => array('bar' => 'foo is '.'bar'.'', 'foobar' => 'bar');
+ yield 2 => array('bar' => 'foo is bar', 'foobar' => 'bar');
yield 3 => true;
yield 4 => $this;
}));