+ *
+ * @experimental in version 3.3
+ */
+class ServiceLocator implements PsrContainerInterface
+{
+ private $factories;
+ private $values = array();
+
+ /**
+ * @param callable[] $factories
+ */
+ public function __construct(array $factories)
+ {
+ $this->factories = $factories;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($id)
+ {
+ return isset($this->factories[$id]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($id)
+ {
+ if (!isset($this->factories[$id])) {
+ throw new ServiceNotFoundException($id, null, null, array_keys($this->factories));
+ }
+
+ if (true === $factory = $this->factories[$id]) {
+ throw new ServiceCircularReferenceException($id, array($id, $id));
+ }
+
+ if (false !== $factory) {
+ $this->factories[$id] = true;
+ $this->values[$id] = $factory();
+ $this->factories[$id] = false;
+ }
+
+ return $this->values[$id];
+ }
+
+ public function __invoke($id)
+ {
+ return isset($this->factories[$id]) ? $this->get($id) : null;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index 3fdf33b5cae9c..9749875f8446c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -21,6 +21,7 @@
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
+use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -33,6 +34,7 @@
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\ExpressionLanguage\Expression;
@@ -437,6 +439,24 @@ public function testCreateServiceWithIteratorArgument()
$this->assertEquals(1, $i);
}
+ public function testCreateServiceWithServiceLocatorArgument()
+ {
+ $builder = new ContainerBuilder();
+ $builder->register('bar', 'stdClass');
+ $builder
+ ->register('lazy_context', 'LazyContext')
+ ->setArguments(array(new ServiceLocatorArgument(array('bar' => new Reference('bar'), 'invalid' => new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))))
+ ;
+
+ $lazyContext = $builder->get('lazy_context');
+ $locator = $lazyContext->lazyValues;
+
+ $this->assertInstanceOf(ServiceLocator::class, $locator);
+ $this->assertInstanceOf('stdClass', $locator->get('bar'));
+ $this->assertNull($locator->get('invalid'));
+ $this->assertSame($locator->get('bar'), $locator('bar'), '->get() should be used when invoking ServiceLocator');
+ }
+
/**
* @expectedException \RuntimeException
*/
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index bf6b789c13fb5..3886e93a0c606 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -17,11 +17,13 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
+use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\Variable;
use Symfony\Component\ExpressionLanguage\Expression;
@@ -503,6 +505,35 @@ public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServic
$dumper->dump();
}
+ public function testServiceLocatorArgumentProvideServiceLocator()
+ {
+ require_once self::$fixturesPath.'/includes/classes.php';
+
+ $container = new ContainerBuilder();
+ $container->register('lazy_referenced', 'stdClass');
+ $container
+ ->register('lazy_context', 'LazyContext')
+ ->setArguments(array(new ServiceLocatorArgument(array('lazy1' => new Reference('lazy_referenced'), 'lazy2' => new Reference('lazy_referenced'), 'container' => new Reference('service_container')))))
+ ;
+ $container->compile();
+
+ $dumper = new PhpDumper($container);
+ $dump = $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Locator_Argument_Provide_Service_Locator'));
+ $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_locator_argument.php', $dump);
+
+ require_once self::$fixturesPath.'/php/services_locator_argument.php';
+
+ $container = new \Symfony_DI_PhpDumper_Test_Locator_Argument_Provide_Service_Locator();
+ $lazyContext = $container->get('lazy_context');
+
+ $this->assertInstanceOf(ServiceLocator::class, $lazyContext->lazyValues);
+ $this->assertSame($container, $lazyContext->lazyValues->get('container'));
+ $this->assertInstanceOf('stdClass', $lazy1 = $lazyContext->lazyValues->get('lazy1'));
+ $this->assertInstanceOf('stdClass', $lazy2 = $lazyContext->lazyValues->get('lazy2'));
+ $this->assertSame($lazy1, $lazy2);
+ $this->assertFalse($lazyContext->lazyValues->has('lazy_referenced'), '->has() returns false for the original service id, only the key can be used');
+ }
+
public function testLazyArgumentProvideGenerator()
{
require_once self::$fixturesPath.'/includes/classes.php';
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php
index 3a8c1199d80c8..96e8ff1ebb85c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php
@@ -7,6 +7,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php
index 42a78c86f653c..1924052a33992 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php
index 750d41a6ef7c2..a5c41acb6db75 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php
index 62d60c296911c..0f41657f53d27 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php
index 0bc71cab9c722..a666108136174 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php
index 86a6333760db2..2fe07e9664fa8 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php
index 2020bb0774dee..f74b11ca7cb20 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php
index f5f94217e90f4..9c4ca45b34a27 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services29.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services29.php
index 764b34b655419..3d2d0c08530a0 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services29.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services29.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php
index c6c1bb946b99f..f5f4200275351 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services32.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services32.php
index 6dda45ad08594..2fb0571099bfe 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services32.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services32.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php
index 1369dd25f0d6c..4a1a7853d1f5c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php
index 74fb6787506ac..bda14b746e613 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php
index 91afec33403cf..3cea6d7689a06 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
index acaebde4a77e6..97ce40f4eb133 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dump_overriden_getters_with_constructor.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dump_overriden_getters_with_constructor.php
index da02469bcfc6e..c15a126446e5c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dump_overriden_getters_with_constructor.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dump_overriden_getters_with_constructor.php
@@ -6,6 +6,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator_argument.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator_argument.php
new file mode 100644
index 0000000000000..064269c6f48e4
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator_argument.php
@@ -0,0 +1,84 @@
+services = array();
+ $this->methodMap = array(
+ 'lazy_context' => 'getLazyContextService',
+ 'lazy_referenced' => 'getLazyReferencedService',
+ );
+
+ $this->aliases = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function compile()
+ {
+ throw new LogicException('You cannot compile a dumped frozen container.');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isFrozen()
+ {
+ return true;
+ }
+
+ /**
+ * Gets the 'lazy_context' service.
+ *
+ * This service is shared.
+ * This method always returns the same instance of the service.
+ *
+ * @return \LazyContext A LazyContext instance
+ */
+ protected function getLazyContextService()
+ {
+ return $this->services['lazy_context'] = new \LazyContext(new ServiceLocator(array(
+ 'lazy1' => function () { return ${($_ = isset($this->services['lazy_referenced']) ? $this->services['lazy_referenced'] : $this->get('lazy_referenced')) && false ?: '_'}; },
+ 'lazy2' => function () { return ${($_ = isset($this->services['lazy_referenced']) ? $this->services['lazy_referenced'] : $this->get('lazy_referenced')) && false ?: '_'}; },
+ 'container' => function () { return $this; },
+ )));
+ }
+
+ /**
+ * Gets the 'lazy_referenced' service.
+ *
+ * This service is shared.
+ * This method always returns the same instance of the service.
+ *
+ * @return \stdClass A stdClass instance
+ */
+ protected function getLazyReferencedService()
+ {
+ return $this->services['lazy_referenced'] = new \stdClass();
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_locator_argument.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_locator_argument.xml
new file mode 100644
index 0000000000000..93dbef712ee43
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_locator_argument.xml
@@ -0,0 +1,25 @@
+
+