feature #21327 [DI] Factorize compiler passes around new AbstractRecursivePass (nicolas-grekas)
This PR was merged into the 3.3-dev branch.
Discussion
----------
[DI] Factorize compiler passes around new AbstractRecursivePass
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
This PR introduces an AbstractRecursivePass that is able to visit the definition tree recursively everywhere a Definition or a Reference can be nested.
All existing compiler passes that need recursivity are updated to leverage this new class.
This remove a bunch of boilerplate that was previously repeated all over.
It also fixes compiler passes that eg missed looking at configurators for no reason (this "fix" is a new feature really).
It then applies recursivity to AutowirePass, that does not handle it today, but should.
I'm happy that the net result is a loss of 153 lines :)
Commits
-------
6acb80f48f
[DI] Factorize compiler passes around new AbstractRecursivePass
This commit is contained in:
commit
3f8aa7b220
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <p@tchwork.com>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
@ -25,14 +25,13 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,12 +22,11 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
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)
|
||||
|
@ -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 <schmittjoh@gmail.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 <schmittjoh@gmail.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 <schmittjoh@gmail.com>
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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 <schmittjoh@gmail.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 <schmittjoh@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 <schmittjoh@gmail.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}));
|
||||
|
Reference in New Issue
Block a user