diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 13f94846b4..13b860ca28 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -17,6 +17,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -334,6 +335,8 @@ class TextDescriptor extends Descriptor $argumentsInformation[] = sprintf('Service(%s)', (string) $argument); } elseif ($argument instanceof IteratorArgument) { $argumentsInformation[] = sprintf('Iterator (%d element(s))', count($argument->getValues())); + } elseif ($argument instanceof ServiceLocatorArgument) { + $argumentsInformation[] = sprintf('Service locator (%d element(s))', count($argument->getValues())); } elseif ($argument instanceof Definition) { $argumentsInformation[] = 'Inlined Service'; } else { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index 8498318126..c3404ead51 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -14,6 +14,7 @@ namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -387,8 +388,8 @@ class XmlDescriptor extends Descriptor if ($argument instanceof Reference) { $argumentXML->setAttribute('type', 'service'); $argumentXML->setAttribute('id', (string) $argument); - } elseif ($argument instanceof IteratorArgument) { - $argumentXML->setAttribute('type', 'iterator'); + } elseif ($argument instanceof IteratorArgument || $argument instanceof ServiceLocatorArgument) { + $argumentXML->setAttribute('type', $argument instanceof IteratorArgument ? 'iterator' : 'service_locator'); foreach ($this->getArgumentNodes($argument->getValues(), $dom) as $childArgumentXML) { $argumentXML->appendChild($childArgumentXML); diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php index 060d234d38..a7d6986fa9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php @@ -11,8 +11,8 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -32,7 +32,7 @@ class TestServiceContainerWeakRefPass implements CompilerPassInterface foreach ($definitions as $id => $definition) { if ($id && '.' !== $id[0] && (!$definition->isPublic() || $definition->isPrivate()) && !$definition->getErrors() && !$definition->isAbstract()) { - $privateServices[$id] = new ServiceClosureArgument(new Reference($id, ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE)); + $privateServices[$id] = new Reference($id, ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE); } } @@ -44,13 +44,15 @@ class TestServiceContainerWeakRefPass implements CompilerPassInterface $alias = $aliases[$target]; } if (isset($definitions[$target]) && !$definitions[$target]->getErrors() && !$definitions[$target]->isAbstract()) { - $privateServices[$id] = new ServiceClosureArgument(new Reference($target, ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE)); + $privateServices[$id] = new Reference($target, ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE); } } } if ($privateServices) { - $definitions['test.private_services_locator']->replaceArgument(0, $privateServices); + $id = (string) ServiceLocatorTagPass::register($container, $privateServices); + $container->setDefinition('test.private_services_locator', $container->getDefinition($id))->setPublic(true); + $container->removeDefinition($id); } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml index 97d3b094b0..7f474bd132 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml @@ -58,14 +58,9 @@ - - - - - - - - + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml index 09df99cfc0..a3c2281c87 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml @@ -24,13 +24,8 @@ - - - - - - - + + diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index e0d4d5b299..fd4da97382 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -19,7 +19,7 @@ "php": "^7.1.3", "ext-xml": "*", "symfony/cache": "~3.4|~4.0", - "symfony/dependency-injection": "^4.1.1", + "symfony/dependency-injection": "^4.2", "symfony/config": "~4.2", "symfony/event-dispatcher": "^4.1", "symfony/http-foundation": "^4.1", diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index d9521e2d1a..9fbdae0bdd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -27,14 +27,9 @@ - - - - - - - - + + + diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 179876e186..ed648eb400 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -20,7 +20,7 @@ "ext-xml": "*", "symfony/config": "^4.2", "symfony/security": "~4.2", - "symfony/dependency-injection": "^3.4.3|^4.0.3", + "symfony/dependency-injection": "^4.2", "symfony/http-kernel": "^4.1" }, "require-dev": { diff --git a/src/Symfony/Component/DependencyInjection/Argument/IteratorArgument.php b/src/Symfony/Component/DependencyInjection/Argument/IteratorArgument.php index ab3a87900a..d413678a14 100644 --- a/src/Symfony/Component/DependencyInjection/Argument/IteratorArgument.php +++ b/src/Symfony/Component/DependencyInjection/Argument/IteratorArgument.php @@ -11,9 +11,6 @@ namespace Symfony\Component\DependencyInjection\Argument; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; -use Symfony\Component\DependencyInjection\Reference; - /** * Represents a collection of values to lazily iterate over. * @@ -21,35 +18,5 @@ use Symfony\Component\DependencyInjection\Reference; */ class IteratorArgument implements ArgumentInterface { - private $values; - - /** - * @param Reference[] $values - */ - public function __construct(array $values) - { - $this->setValues($values); - } - - /** - * @return array The values to lazily iterate over - */ - public function getValues() - { - return $this->values; - } - - /** - * @param Reference[] $values The service references to lazily iterate over - */ - public function setValues(array $values) - { - foreach ($values as $k => $v) { - if (null !== $v && !$v instanceof Reference) { - throw new InvalidArgumentException(sprintf('An IteratorArgument must hold only Reference instances, "%s" given.', is_object($v) ? get_class($v) : gettype($v))); - } - } - - $this->values = $values; - } + use ReferenceSetArgumentTrait; } diff --git a/src/Symfony/Component/DependencyInjection/Argument/ReferenceSetArgumentTrait.php b/src/Symfony/Component/DependencyInjection/Argument/ReferenceSetArgumentTrait.php new file mode 100644 index 0000000000..94ed23971b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Argument/ReferenceSetArgumentTrait.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Titouan Galopin + * @author Nicolas Grekas + */ +trait ReferenceSetArgumentTrait +{ + private $values; + + /** + * @param Reference[] $values + */ + public function __construct(array $values) + { + $this->setValues($values); + } + + /** + * @return Reference[] The values in the set + */ + public function getValues() + { + return $this->values; + } + + /** + * @param Reference[] $values The service references to put in the set + */ + public function setValues(array $values) + { + foreach ($values as $k => $v) { + if (null !== $v && !$v instanceof Reference) { + throw new InvalidArgumentException(sprintf('A %s must hold only Reference instances, "%s" given.', __CLASS__, is_object($v) ? get_class($v) : gettype($v))); + } + } + + $this->values = $values; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Argument/ServiceLocator.php b/src/Symfony/Component/DependencyInjection/Argument/ServiceLocator.php new file mode 100644 index 0000000000..fcb2b6a6b9 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Argument/ServiceLocator.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class ServiceLocator extends BaseServiceLocator +{ + private $factory; + private $serviceMap; + + public function __construct(\Closure $factory, array $serviceMap) + { + $this->factory = $factory; + $this->serviceMap = $serviceMap; + parent::__construct($serviceMap); + } + + /** + * {@inheritdoc} + */ + public function get($id) + { + return isset($this->serviceMap[$id]) ? ($this->factory)(...$this->serviceMap[$id]) : parent::get($id); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Argument/ServiceLocatorArgument.php b/src/Symfony/Component/DependencyInjection/Argument/ServiceLocatorArgument.php new file mode 100644 index 0000000000..8214f8bb3a --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Argument/ServiceLocatorArgument.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +/** + * Represents a closure acting as a service locator. + * + * @author Nicolas Grekas + */ +class ServiceLocatorArgument implements ArgumentInterface +{ + use ReferenceSetArgumentTrait; +} diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 4393b8c44c..de2421a60a 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG ----- * added `ServiceSubscriberTrait` + * added `ServiceLocatorArgument` for creating optimized service-locators 4.1.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RegisterEnvVarProcessorsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RegisterEnvVarProcessorsPass.php index b99c252fef..8ac301f192 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RegisterEnvVarProcessorsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RegisterEnvVarProcessorsPass.php @@ -11,13 +11,11 @@ namespace Symfony\Component\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\EnvVarProcessor; use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; -use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\DependencyInjection\Reference; /** @@ -41,7 +39,7 @@ class RegisterEnvVarProcessorsPass implements CompilerPassInterface throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EnvVarProcessorInterface::class)); } foreach ($class::getProvidedTypes() as $prefix => $type) { - $processors[$prefix] = new ServiceClosureArgument(new Reference($id)); + $processors[$prefix] = new Reference($id); $types[$prefix] = self::validateProvidedTypes($type, $class); } } @@ -56,9 +54,8 @@ class RegisterEnvVarProcessorsPass implements CompilerPassInterface } if ($processors) { - $container->register('container.env_var_processors_locator', ServiceLocator::class) + $container->setAlias('container.env_var_processors_locator', (string) ServiceLocatorTagPass::register($container, $processors)) ->setPublic(true) - ->setArguments(array($processors)) ; } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php index 3969c74fcb..1b66b55290 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php @@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; @@ -28,6 +29,9 @@ final class ServiceLocatorTagPass extends AbstractRecursivePass { protected function processValue($value, $isRoot = false) { + if ($value instanceof ServiceLocatorArgument) { + return self::register($this->container, $value->getValues()); + } if (!$value instanceof Definition || !$value->hasTag('container.service_locator')) { return parent::processValue($value, $isRoot); } diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 2b3ab61e42..2eaa278cdb 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -14,6 +14,7 @@ namespace Symfony\Component\DependencyInjection; use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; @@ -401,6 +402,30 @@ class Container implements ResettableContainerInterface } } + /** + * @internal + */ + final protected function getService($registry, $id, $method, $load) + { + if ('service_container' === $id) { + return $this; + } + if (\is_string($load)) { + throw new RuntimeException($load); + } + if (null === $method) { + return false !== $registry ? $this->{$registry}[$id] ?? null : null; + } + if (false !== $registry) { + return $this->{$registry}[$id] ?? $this->{$registry}[$id] = $load ? $this->load($method) : $this->{$method}(); + } + if (!$load) { + return $this->{$method}(); + } + + return ($factory = $this->factories[$id] ?? $this->factories['service_container'][$id] ?? null) ? $factory() : $this->load($method); + } + private function __clone() { } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 1c2c239869..3aa0e55af1 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -15,6 +15,8 @@ use Psr\Container\ContainerInterface as PsrContainerInterface; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocator; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Compiler\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PassConfig; @@ -1215,6 +1217,14 @@ class ContainerBuilder extends Container implements TaggedContainerInterface return $count; }); + } elseif ($value instanceof ServiceLocatorArgument) { + $refs = array(); + foreach ($value->getValues() as $k => $v) { + if ($v) { + $refs[$k] = array($v); + } + } + $value = new ServiceLocator(\Closure::fromCallable(array($this, 'resolveServices')), $refs); } elseif ($value instanceof Reference) { $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices); } elseif ($value instanceof Definition) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 3988bdf324..8dcd4829ec 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -14,6 +14,8 @@ namespace Symfony\Component\DependencyInjection\Dumper; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocator; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Variable; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; @@ -22,6 +24,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator; use Symfony\Component\DependencyInjection\TypedReference; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Exception\EnvParameterException; @@ -71,6 +74,8 @@ class PhpDumper extends Dumper private $circularReferences = array(); private $singleUsePrivateIds = array(); private $addThrow = false; + private $locatedIds = array(); + private $serviceLocatorTag; /** * @var ProxyDumper @@ -113,6 +118,7 @@ class PhpDumper extends Dumper */ public function dump(array $options = array()) { + $this->locatedIds = array(); $this->targetDirRegex = null; $this->inlinedRequires = array(); $options = array_merge(array( @@ -123,6 +129,7 @@ class PhpDumper extends Dumper 'debug' => true, 'hot_path_tag' => 'container.hot_path', 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader', + 'service_locator_tag' => 'container.service_locator', 'build_time' => time(), ), $options); @@ -131,6 +138,7 @@ class PhpDumper extends Dumper $this->asFiles = $options['as_files']; $this->hotPathTag = $options['hot_path_tag']; $this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']); + $this->serviceLocatorTag = $options['service_locator_tag']; if (0 !== strpos($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) { $baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass); @@ -269,6 +277,7 @@ EOF; $this->targetDirRegex = null; $this->inlinedRequires = array(); $this->circularReferences = array(); + $this->locatedIds = array(); $unusedEnvs = array(); foreach ($this->container->getEnvCounters() as $env => $use) { @@ -508,7 +517,7 @@ EOTXT throw new ServiceCircularReferenceException($id, array($id)); } - $code .= $this->addNewInstance($def, '$'.$name, ' = ', $id); + $code .= $this->addNewInstance($def, ' $'.$name.' = ', $id); if (!$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) { $code .= $this->addServiceProperties($def, $name); @@ -552,7 +561,7 @@ EOTXT $instantiation .= ' = '; } - $code = $this->addNewInstance($definition, $return, $instantiation, $id); + $code = $this->addNewInstance($definition, ' '.$return.$instantiation, $id); if (!$isSimpleInstance) { $code .= "\n"; @@ -816,7 +825,7 @@ EOF; } if ($definition->isPublic()) { $publicServices .= $this->addService($id, $definition); - } elseif (!$this->isTrivialInstance($definition)) { + } elseif (!$this->isTrivialInstance($definition) || isset($this->locatedIds[$id])) { $privateServices .= $this->addService($id, $definition); } } @@ -829,7 +838,7 @@ EOF; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { - if (!$definition->isSynthetic() && !$this->isHotPath($definition) && ($definition->isPublic() || !$this->isTrivialInstance($definition))) { + if (!$definition->isSynthetic() && !$this->isHotPath($definition) && ($definition->isPublic() || !$this->isTrivialInstance($definition) || isset($this->locatedIds[$id]))) { $code = $this->addService($id, $definition, $file); if (!$definition->isShared()) { @@ -850,10 +859,18 @@ EOF; } } - private function addNewInstance(Definition $definition, $return, $instantiation, $id) + private function addNewInstance(Definition $definition, string $return = '', string $id = null) { - $class = $this->dumpValue($definition->getClass()); - $return = ' '.$return.$instantiation; + $tail = $return ? ";\n" : ''; + + if (BaseServiceLocator::class === $definition->getClass() && $definition->hasTag($this->serviceLocatorTag)) { + $arguments = array(); + foreach ($definition->getArgument(0) as $k => $argument) { + $arguments[$k] = $argument->getValues()[0]; + } + + return $return.$this->dumpValue(new ServiceLocatorArgument($arguments)).$tail; + } $arguments = array(); foreach ($definition->getArguments() as $value) { @@ -862,6 +879,7 @@ EOF; if (null !== $definition->getFactory()) { $callable = $definition->getFactory(); + if (is_array($callable)) { if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) { throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a')); @@ -869,34 +887,34 @@ EOF; if ($callable[0] instanceof Reference || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { - return $return.sprintf("%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : ''); + return $return.sprintf('%s->%s(%s)', $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } $class = $this->dumpValue($callable[0]); // If the class is a string we can optimize away if (0 === strpos($class, "'") && false === strpos($class, '$')) { if ("''" === $class) { - throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id)); + throw new RuntimeException(sprintf('Cannot dump definition: %s service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id ? 'The "'.$id.'"' : 'inline')); } - return $return.sprintf("%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : ''); + return $return.sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } if (0 === strpos($class, 'new ')) { - return $return.sprintf("(%s)->%s(%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : ''); + return $return.sprintf('(%s)->%s(%s)', $class, $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } - return $return.sprintf("[%s, '%s'](%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : ''); + return $return.sprintf("[%s, '%s'](%s)", $class, $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } - return $return.sprintf("%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : ''); + return $return.sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '').$tail; } - if (false !== strpos($class, '$')) { - return sprintf(" \$class = %s;\n\n%snew \$class(%s);\n", $class, $return, implode(', ', $arguments)); + if (null === $class = $definition->getClass()) { + throw new RuntimeException('Cannot dump definitions which have no class nor factory.'); } - return $return.sprintf("new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments)); + return $return.sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)).$tail; } private function startClass(string $class, string $baseClass, string $baseClassWithNamespace): string @@ -1537,6 +1555,27 @@ EOF; return implode("\n", $code); } + + if ($value instanceof ServiceLocatorArgument) { + $serviceMap = ''; + foreach ($value->getValues() as $k => $v) { + if (!$v) { + continue; + } + $definition = $this->container->findDefinition($id = (string) $v); + $load = !($e = $definition->getErrors()) ? $this->asFiles && !$this->isHotPath($definition) : reset($e); + $serviceMap .= sprintf("\n %s => array(%s, %s, %s, %s),", + $this->export($k), + $this->export($definition->isShared() ? ($definition->isPublic() ? 'services' : 'privates') : false), + $this->export($id), + $this->export(ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $v->getInvalidBehavior() && !\is_string($load) ? $this->generateMethodName($id).($load ? '.php' : '') : null), + $this->export($load) + ); + $this->locatedIds[$id] = true; + } + + return sprintf('new \%s(\Closure::fromCallable(array($this, \'getService\')), array(%s%s))', ServiceLocator::class, $serviceMap, $serviceMap ? "\n " : ''); + } } finally { list($this->definitionVariables, $this->referenceVariables, $this->variableCount) = $scope; } @@ -1559,50 +1598,7 @@ EOF; throw new RuntimeException('Cannot dump definitions which have a configurator.'); } - $arguments = array(); - foreach ($value->getArguments() as $argument) { - $arguments[] = $this->dumpValue($argument); - } - - if (null !== $value->getFactory()) { - $factory = $value->getFactory(); - - if (is_string($factory)) { - return sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory)), implode(', ', $arguments)); - } - - if (is_array($factory)) { - if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) { - throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a')); - } - - $class = $this->dumpValue($factory[0]); - if (is_string($factory[0])) { - return sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $factory[1], implode(', ', $arguments)); - } - - if ($factory[0] instanceof Definition) { - if (0 === strpos($class, 'new ')) { - return sprintf('(%s)->%s(%s)', $class, $factory[1], implode(', ', $arguments)); - } - - return sprintf("[%s, '%s'](%s)", $class, $factory[1], implode(', ', $arguments)); - } - - if ($factory[0] instanceof Reference) { - return sprintf('%s->%s(%s)', $class, $factory[1], implode(', ', $arguments)); - } - } - - throw new RuntimeException('Cannot dump definition because of invalid factory'); - } - - $class = $value->getClass(); - if (null === $class) { - throw new RuntimeException('Cannot dump definitions which have no class nor factory.'); - } - - return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)); + return $this->addNewInstance($value); } elseif ($value instanceof Variable) { return '$'.$value; } elseif ($value instanceof Reference) { @@ -1696,7 +1692,7 @@ EOF; return sprintf('$this->throw(%s)', $this->export(reset($e))); } - $code = substr($this->addNewInstance($definition, '', '', $id), 8, -2); + $code = $this->addNewInstance($definition, '', $id); if ($definition->isShared() && !isset($this->singleUsePrivateIds[$id])) { $code = sprintf('$this->%s[\'%s\'] = %s', $definition->isPublic() ? 'services' : 'privates', $id, $code); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 664e58aa87..a644ff3fc2 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Dumper; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Parameter; @@ -280,6 +281,9 @@ class XmlDumper extends Dumper } elseif ($value instanceof IteratorArgument) { $element->setAttribute('type', 'iterator'); $this->convertParameters($value->getValues(), $type, $element, 'key'); + } elseif ($value instanceof ServiceLocatorArgument) { + $element->setAttribute('type', 'service_locator'); + $this->convertParameters($value->getValues(), $type, $element, 'key'); } elseif ($value instanceof Reference) { $element->setAttribute('type', 'service'); $element->setAttribute('id', (string) $value); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 73f29a6145..ce53c602db 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -19,6 +19,7 @@ use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; @@ -233,6 +234,8 @@ class YamlDumper extends Dumper } if ($value instanceof IteratorArgument) { $tag = 'iterator'; + } elseif ($value instanceof ServiceLocatorArgument) { + $tag = 'service_locator'; } else { throw new RuntimeException(sprintf('Unspecified Yaml tag for type "%s".', get_class($value))); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php index 8ea04119d8..f002ee2f38 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -92,6 +93,16 @@ function inline(string $class = null): InlineServiceConfigurator return new InlineServiceConfigurator(new Definition($class)); } +/** + * Creates a service locator. + * + * @param ReferenceConfigurator[] $values + */ +function service_locator(array $values): ServiceLocatorArgument +{ + return new ServiceLocatorArgument(AbstractConfigurator::processValue($values, true)); +} + /** * Creates a lazy iterator. * diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 9fbff021d6..71e991c9ff 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -510,7 +511,15 @@ class XmlFileLoader extends FileLoader try { $arguments[$key] = new IteratorArgument($arg); } catch (InvalidArgumentException $e) { - throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".', $name, $file)); + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="%s" only accepts collections of type="service" references in "%s".', $name, $file)); + } + break; + case 'service_locator': + $arg = $this->getArgumentsAsPhp($arg, $name, $file, false); + try { + $arguments[$key] = new ServiceLocatorArgument($arg); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="%s" only accepts maps of type="service" references in "%s".', $name, $file)); } break; case 'tagged': diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 6b9f12e515..11eb8e45c7 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -691,6 +692,17 @@ class YamlFileLoader extends FileLoader throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file)); } } + if ('service_locator' === $value->getTag()) { + if (!is_array($argument)) { + throw new InvalidArgumentException(sprintf('"!service_locator" tag only accepts maps in "%s".', $file)); + } + $argument = $this->resolveServices($argument, $file, $isParameter); + try { + return new ServiceLocatorArgument($argument); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('"!service_locator" tag only accepts maps of "@service" references in "%s".', $file)); + } + } if ('tagged' === $value->getTag()) { if (!is_string($argument) || !$argument) { throw new InvalidArgumentException(sprintf('"!tagged" tag only accepts non empty string in "%s".', $file)); diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 015f81f953..21e3b59310 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -259,6 +259,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index a840256990..3ba629bdb0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1431,6 +1431,33 @@ class ContainerBuilderTest extends TestCase $container->get('errored_definition'); } + + public function testServiceLocatorArgument() + { + $container = include __DIR__.'/Fixtures/containers/container_service_locator_argument.php'; + $container->compile(); + + $locator = $container->get('bar')->locator; + + $this->assertInstanceOf(ServiceLocator::class, $locator); + $this->assertSame($container->get('foo1'), $locator->get('foo1')); + $this->assertEquals(new \stdClass(), $locator->get('foo2')); + $this->assertSame($locator->get('foo2'), $locator->get('foo2')); + $this->assertEquals(new \stdClass(), $locator->get('foo3')); + $this->assertNotSame($locator->get('foo3'), $locator->get('foo3')); + + try { + $locator->get('foo4'); + $this->fail('RuntimeException expected.'); + } catch (RuntimeException $e) { + $this->assertSame('BOOM', $e->getMessage()); + } + + $this->assertNull($locator->get('foo5')); + + $container->set('foo5', $foo5 = new \stdClass()); + $this->assertSame($foo5, $locator->get('foo5')); + } } class FooClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index e92a0cbb4b..42fe478d8e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -17,10 +17,12 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocator as ArgumentServiceLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; @@ -1015,6 +1017,38 @@ class PhpDumperTest extends TestCase $container = new \Symfony_DI_PhpDumper_Errored_Definition(); $container->get('runtime_error'); } + + public function testServiceLocatorArgument() + { + $container = include self::$fixturesPath.'/containers/container_service_locator_argument.php'; + $container->compile(); + $dumper = new PhpDumper($container); + $dump = $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Service_Locator_Argument')); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_service_locator_argument.php', str_replace(str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), '%path%', $dump)); + eval('?>'.$dump); + + $container = new \Symfony_DI_PhpDumper_Service_Locator_Argument(); + $locator = $container->get('bar')->locator; + + $this->assertInstanceOf(ArgumentServiceLocator::class, $locator); + $this->assertSame($container->get('foo1'), $locator->get('foo1')); + $this->assertEquals(new \stdClass(), $locator->get('foo2')); + $this->assertSame($locator->get('foo2'), $locator->get('foo2')); + $this->assertEquals(new \stdClass(), $locator->get('foo3')); + $this->assertNotSame($locator->get('foo3'), $locator->get('foo3')); + + try { + $locator->get('foo4'); + $this->fail('RuntimeException expected.'); + } catch (RuntimeException $e) { + $this->assertSame('BOOM', $e->getMessage()); + } + + $this->assertNull($locator->get('foo5')); + + $container->set('foo5', $foo5 = new \stdClass()); + $this->assertSame($foo5, $locator->get('foo5')); + } } class Rot13EnvVarProcessor implements EnvVarProcessorInterface diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_service_locator_argument.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_service_locator_argument.php new file mode 100644 index 0000000000..525dbde1f2 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_service_locator_argument.php @@ -0,0 +1,46 @@ +register('foo1', 'stdClass') + ->setPublic(true) +; + +$container + ->register('foo2', 'stdClass') +; + +$container + ->register('foo3', 'stdClass') + ->setShared(false) +; + +$container + ->register('foo4', 'stdClass') + ->addError('BOOM') +; + +$container + ->register('foo5', 'stdClass') + ->setPublic(true) + ->setSynthetic(true) +; + +$container + ->register('bar', 'stdClass') + ->setProperty('locator', new ServiceLocatorArgument(array( + 'foo1' => new Reference('foo1'), + 'foo2' => new Reference('foo2'), + 'foo3' => new Reference('foo3'), + 'foo4' => new Reference('foo4', $container::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE), + 'foo5' => new Reference('foo5', $container::IGNORE_ON_UNINITIALIZED_REFERENCE), + ))) + ->setPublic(true) +; + +return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php index 942eb0eb72..f6e8d69184 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php @@ -82,9 +82,7 @@ class Symfony_DI_PhpDumper_Test_EnvParameters extends Container */ protected function getTestService() { - $class = $this->getEnv('FOO'); - - return $this->services['test'] = new $class($this->getEnv('Bar'), 'foo'.$this->getEnv('string:FOO').'baz', $this->getEnv('int:Baz')); + return $this->services['test'] = new ${($_ = $this->getEnv('FOO')) && false ?: "_"}($this->getEnv('Bar'), 'foo'.$this->getEnv('string:FOO').'baz', $this->getEnv('int:Baz')); } public function getParameter($name) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php index 012a36023b..7a334c41a6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php @@ -56,6 +56,7 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container public function getRemovedIds() { return array( + '.service_locator.GU08LT9' => true, 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, ); @@ -78,9 +79,9 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container */ protected function getContainer_EnvVarProcessorsLocatorService() { - return $this->services['container.env_var_processors_locator'] = new \Symfony\Component\DependencyInjection\ServiceLocator(array('rot13' => function () { - return ($this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] = new \Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor()); - })); + return $this->services['container.env_var_processors_locator'] = new \Symfony\Component\DependencyInjection\Argument\ServiceLocator(\Closure::fromCallable(array($this, 'getService')), array( + 'rot13' => array('services', 'Symfony\\Component\\DependencyInjection\\Tests\\Dumper\\Rot13EnvVarProcessor', 'getRot13EnvVarProcessorService', false), + )); } public function getParameter($name) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_service_locator_argument.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_service_locator_argument.php new file mode 100644 index 0000000000..eeeb0ce9b0 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_service_locator_argument.php @@ -0,0 +1,128 @@ +services = $this->privates = array(); + $this->syntheticIds = array( + 'foo5' => true, + ); + $this->methodMap = array( + 'bar' => 'getBarService', + 'foo1' => 'getFoo1Service', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + '.service_locator.38dy3OH' => true, + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'foo2' => true, + 'foo3' => true, + 'foo4' => true, + ); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \stdClass + */ + protected function getBarService() + { + $this->services['bar'] = $instance = new \stdClass(); + + $instance->locator = new \Symfony\Component\DependencyInjection\Argument\ServiceLocator(\Closure::fromCallable(array($this, 'getService')), array( + 'foo1' => array('services', 'foo1', 'getFoo1Service', false), + 'foo2' => array('privates', 'foo2', 'getFoo2Service', false), + 'foo3' => array(false, 'foo3', 'getFoo3Service', false), + 'foo4' => array('privates', 'foo4', NULL, 'BOOM'), + 'foo5' => array('services', 'foo5', NULL, false), + )); + + return $instance; + } + + /** + * Gets the public 'foo1' shared service. + * + * @return \stdClass + */ + protected function getFoo1Service() + { + return $this->services['foo1'] = new \stdClass(); + } + + /** + * Gets the private 'foo2' shared service. + * + * @return \stdClass + */ + protected function getFoo2Service() + { + return $this->privates['foo2'] = new \stdClass(); + } + + /** + * Gets the private 'foo3' service. + * + * @return \stdClass + */ + protected function getFoo3Service() + { + return new \stdClass(); + } + + /** + * Gets the private 'foo4' shared service. + * + * @return \stdClass + */ + protected function getFoo4Service() + { + return $this->privates['foo4'] = new \stdClass(); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php index 2c887e0e21..013cb60ea4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php @@ -79,14 +79,11 @@ class ProjectServiceContainer extends Container */ protected function getFooServiceService() { - return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber((new \Symfony\Component\DependencyInjection\ServiceLocator(array('Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => function (): ?\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition { - return ($this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? $this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()); - }, 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber { - return ($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()); - }, 'bar' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition { - return ($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()); - }, 'baz' => function (): ?\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition { - return ($this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? $this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()); - })))->withContext('foo_service', $this)); + return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber((new \Symfony\Component\DependencyInjection\Argument\ServiceLocator(\Closure::fromCallable(array($this, 'getService')), array( + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => array('privates', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'getCustomDefinitionService', false), + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => array('services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false), + 'bar' => array('services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false), + 'baz' => array('privates', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'getCustomDefinitionService', false), + )))->withContext('foo_service', $this)); } }