From afacff9fda6b50af847e922644c7083e3dbaeeb8 Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Wed, 28 Nov 2012 11:45:04 +0100 Subject: [PATCH] [DependencyInjection] allows references to be declared as "lazy" In contrast to regular references, a lazy references is not guaranteed to be fully initialized upon injection, but may instead be lazy initialized when it is first accessed by the consuming service. Lazy makes no implementation restrictions on how those referenced services are to be constructed but leave this up to a concrete implementor. --- .../DependencyInjection/Dumper/XmlDumper.php | 9 +++++++ .../DependencyInjection/Dumper/YamlDumper.php | 11 +++++++- .../Loader/XmlFileLoader.php | 1 + .../Loader/YamlFileLoader.php | 11 ++++++-- .../schema/dic/services/services-1.0.xsd | 2 ++ .../DependencyInjection/Reference.php | 26 +++++++++++++++++++ .../DependencyInjection/SimpleXMLElement.php | 5 ++++ .../Tests/Fixtures/xml/services6.xml | 4 +++ .../Tests/Fixtures/yaml/services6.yml | 2 ++ .../Tests/Loader/XmlFileLoaderTest.php | 4 +++ .../Tests/Loader/YamlFileLoaderTest.php | 4 +++ 11 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 38025ff3eac8c..463a3695c0f0b 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -22,6 +22,7 @@ * * @author Fabien Potencier * @author Martin HasoĊˆ + * @author Johannes M. Schmitt * * @api */ @@ -239,6 +240,14 @@ private function convertParameters($parameters, $type, \DOMElement $parent, $key } elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) { $element->setAttribute('on-invalid', 'ignore'); } + + if ( ! $value->isStrict()) { + $element->setAttribute('strict', 'false'); + } + + if ($value->isLazy()) { + $element->setAttribute('lazy', 'true'); + } } elseif ($value instanceof Definition) { $element->setAttribute('type', 'service'); $this->addService($value, null, $element); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 82d68008e0eeb..b4ffa323ac6fb 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -24,6 +24,7 @@ * YamlDumper dumps a service container as a YAML string. * * @author Fabien Potencier + * @author Johannes M. Schmitt * * @api */ @@ -212,7 +213,15 @@ private function dumpValue($value) return $code; } elseif ($value instanceof Reference) { - return $this->getServiceCall((string) $value, $value); + $call = $this->getServiceCall((string) $value, $value); + if ($value->isLazy()) { + $call .= '~'; + } + if ( ! $value->isStrict()) { + $call .= '='; + } + + return $call; } elseif ($value instanceof Parameter) { return $this->getParameterCall((string) $value); } elseif (is_object($value) || is_resource($value)) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 6f2c8eba1ada6..ebb0b28d5619c 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -25,6 +25,7 @@ * XmlFileLoader loads XML files service definitions. * * @author Fabien Potencier + * @author Johannes M. Schmitt */ class XmlFileLoader extends FileLoader { diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 71de4ac6592f6..482bab827913b 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -26,6 +26,7 @@ * The YAML format does not support anonymous services (cf. the XML loader). * * @author Fabien Potencier + * @author Johannes M. Schmitt */ class YamlFileLoader extends FileLoader { @@ -298,14 +299,20 @@ private function resolveServices($value) $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; } + $strict = true; if ('=' === substr($value, -1)) { $value = substr($value, 0, -1); $strict = false; - } else { - $strict = true; + } + + $lazy = false; + if ('~' === substr($value, -1)) { + $value = substr($value, 0, -1); + $lazy = true; } $value = new Reference($value, $invalidBehavior, $strict); + $value->setLazy($lazy); } return $value; 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 316f2d7596870..ab76a77a51227 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 @@ -126,6 +126,7 @@ + @@ -139,6 +140,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Reference.php b/src/Symfony/Component/DependencyInjection/Reference.php index 1517da29885a7..8663d7155f6d1 100644 --- a/src/Symfony/Component/DependencyInjection/Reference.php +++ b/src/Symfony/Component/DependencyInjection/Reference.php @@ -15,6 +15,7 @@ * Reference represents a service reference. * * @author Fabien Potencier + * @author Johannes M. Schmitt * * @api */ @@ -23,6 +24,7 @@ class Reference private $id; private $invalidBehavior; private $strict; + private $lazy = false; /** * Constructor. @@ -69,4 +71,28 @@ public function isStrict() { return $this->strict; } + + /** + * Whether this reference should be lazily initialized if available. + * + * @return Boolean + */ + public function isLazy() + { + return $this->lazy; + } + + /** + * Sets the lazily initialization status for this reference. + * + * @param Boolean $bool + * + * @return Reference + */ + public function setLazy($bool) + { + $this->lazy = $bool; + + return $this; + } } diff --git a/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php b/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php index d154602fd330d..9d80041fe1961 100644 --- a/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php +++ b/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php @@ -74,6 +74,11 @@ public function getArgumentsAsPhp($name, $lowercase = true) } $arguments[$key] = new Reference((string) $arg['id'], $invalidBehavior, $strict); + + if (isset($arg['lazy'])) { + $arguments[$key]->setLazy(self::phpize($arg['lazy'])); + } + break; case 'collection': $arguments[$key] = $arg->getArgumentsAsPhp($name, false); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml index 45bc042f61906..b3fbccec989df 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml @@ -46,5 +46,9 @@ + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml index eaa52bda62065..b94cffef3ae19 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml @@ -24,3 +24,5 @@ services: alias: foo public: false factory_service: { class: BazClass, factory_method: getInstance, factory_service: baz_factory } + lazy_deps_service: + arguments: ["@foo~", "@bar"] \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index b589ed95ac036..810855884acc8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -173,6 +173,10 @@ public function testLoadServices() $this->assertEquals('getInstance', $services['factory_service']->getFactoryMethod()); $this->assertEquals('baz_factory', $services['factory_service']->getFactoryService()); + $args = $services['lazy_deps_service']->getArguments(); + $this->assertTrue($args[0]->isLazy()); + $this->assertFalse($args[1]->isLazy()); + $aliases = $container->getAliases(); $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses elements'); $this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index e655d3bd8981a..2ecd5b2935b65 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -128,6 +128,10 @@ public function testLoadServices() $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag'); $this->assertEquals('baz_factory', $services['factory_service']->getFactoryService()); + $args = $services['lazy_deps_service']->getArguments(); + $this->assertTrue($args[0]->isLazy()); + $this->assertFalse($args[1]->isLazy()); + $aliases = $container->getAliases(); $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses aliases'); $this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases');