Skip to content

Commit d85d9a8

Browse files
committed
[DependencyInjection] Implement lazy collection type using generators
1 parent 69dcf41 commit d85d9a8

File tree

15 files changed

+197
-0
lines changed

15 files changed

+197
-0
lines changed

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Dumper;
1313

14+
use Symfony\Component\DependencyInjection\LazyArgument;
1415
use Symfony\Component\DependencyInjection\Variable;
1516
use Symfony\Component\DependencyInjection\Definition;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -1348,6 +1349,13 @@ private function dumpValue($value, $interpolate = true)
13481349
}
13491350

13501351
return sprintf('array(%s)', implode(', ', $code));
1352+
} elseif ($value instanceof LazyArgument) {
1353+
$code = array();
1354+
foreach ($value->getValues() as $k => $v) {
1355+
$code[] = sprintf(' yield %s => %s;', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
1356+
}
1357+
1358+
return sprintf("(function(){\n%s\n })()", implode("\n", $code));
13511359
} elseif ($value instanceof Definition) {
13521360
if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
13531361
return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);

src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Dumper;
1313

1414
use Symfony\Component\DependencyInjection\ContainerInterface;
15+
use Symfony\Component\DependencyInjection\LazyArgument;
1516
use Symfony\Component\DependencyInjection\Parameter;
1617
use Symfony\Component\DependencyInjection\Reference;
1718
use Symfony\Component\DependencyInjection\Definition;
@@ -283,6 +284,9 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent
283284
if (is_array($value)) {
284285
$element->setAttribute('type', 'collection');
285286
$this->convertParameters($value, $type, $element, 'key');
287+
} elseif ($value instanceof LazyArgument) {
288+
$element->setAttribute('type', 'lazy');
289+
$this->convertParameters($value->getValues(), $type, $element, 'key');
286290
} elseif ($value instanceof Reference) {
287291
$element->setAttribute('type', 'service');
288292
$element->setAttribute('id', (string) $value);

src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Dumper;
1313

14+
use Symfony\Component\DependencyInjection\LazyArgument;
1415
use Symfony\Component\Yaml\Dumper as YmlDumper;
1516
use Symfony\Component\DependencyInjection\Alias;
1617
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -245,6 +246,10 @@ private function dumpCallable($callable)
245246
*/
246247
private function dumpValue($value)
247248
{
249+
if ($value instanceof LazyArgument) {
250+
$value = $value->getValues();
251+
}
252+
248253
if (is_array($value)) {
249254
$code = array();
250255
foreach ($value as $k => $v) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection;
13+
14+
/**
15+
* Represents a collection of values to lazily iterate over.
16+
*
17+
* @author Titouan Galopin <galopintitouan@gmail.com>
18+
*/
19+
class LazyArgument
20+
{
21+
private $values;
22+
23+
public function __construct(array $values)
24+
{
25+
$this->values = $values;
26+
}
27+
28+
/**
29+
* @return array The services to create a lazy collection for.
30+
*/
31+
public function getValues()
32+
{
33+
return $this->values;
34+
}
35+
}

src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\DependencyInjection\Alias;
1818
use Symfony\Component\DependencyInjection\Definition;
1919
use Symfony\Component\DependencyInjection\ChildDefinition;
20+
use Symfony\Component\DependencyInjection\LazyArgument;
2021
use Symfony\Component\DependencyInjection\Reference;
2122
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
2223
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@@ -401,6 +402,9 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true)
401402
case 'collection':
402403
$arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false);
403404
break;
405+
case 'lazy':
406+
$arguments[$key] = new LazyArgument($this->getArgumentsAsPhp($arg, $name, false));
407+
break;
404408
case 'string':
405409
$arguments[$key] = $arg->nodeValue;
406410
break;

src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd

+1
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@
189189
<xsd:enumeration value="expression" />
190190
<xsd:enumeration value="string" />
191191
<xsd:enumeration value="constant" />
192+
<xsd:enumeration value="lazy" />
192193
</xsd:restriction>
193194
</xsd:simpleType>
194195

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

+57
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Config\FileLocator;
1616
use Symfony\Component\DependencyInjection\ContainerBuilder;
1717
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
18+
use Symfony\Component\DependencyInjection\LazyArgument;
1819
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
1920
use Symfony\Component\DependencyInjection\Reference;
2021
use Symfony\Component\DependencyInjection\Definition;
@@ -391,4 +392,60 @@ public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServic
391392
$dumper->setProxyDumper(new DummyProxyDumper());
392393
$dumper->dump();
393394
}
395+
396+
public function testLazyArgumentProvideGenerator()
397+
{
398+
require_once self::$fixturesPath.'/includes/classes.php';
399+
400+
$container = new ContainerBuilder();
401+
$container->register('lazy_referenced', 'stdClass');
402+
$container
403+
->register('lazy_context', 'LazyContext')
404+
->setArguments(array(new LazyArgument(array('foo', new Reference('lazy_referenced'), 'k1' => array('foo' => 'bar'), true, 'k2' => new Reference('service_container')))))
405+
;
406+
$container->compile();
407+
408+
$dumper = new PhpDumper($container);
409+
eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator')));
410+
411+
$container = new \Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator();
412+
$lazyContext = $container->get('lazy_context');
413+
414+
$this->assertInstanceOf('\Generator', $lazyContext->lazyValues);
415+
416+
$i = -1;
417+
foreach ($lazyContext->lazyValues as $k => $v) {
418+
$i++;
419+
420+
if ($i === 0) {
421+
$this->assertEquals(0, $k);
422+
$this->assertEquals('foo', $v);
423+
continue;
424+
}
425+
426+
if ($i === 1) {
427+
$this->assertEquals(1, $k);
428+
$this->assertInstanceOf('\stdClass', $v);
429+
continue;
430+
}
431+
432+
if ($i === 2) {
433+
$this->assertEquals('k1', $k);
434+
$this->assertEquals(array('foo' => 'bar'), $v);
435+
continue;
436+
}
437+
438+
if ($i === 3) {
439+
$this->assertEquals(2, $k);
440+
$this->assertTrue($v);
441+
continue;
442+
}
443+
444+
if ($i === 4) {
445+
$this->assertEquals('k2', $k);
446+
$this->assertInstanceOf('\Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator', $v);
447+
continue;
448+
}
449+
}
450+
}
394451
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Symfony\Component\DependencyInjection\ContainerBuilder;
77
use Symfony\Component\DependencyInjection\Reference;
88
use Symfony\Component\DependencyInjection\Parameter;
9+
use Symfony\Component\DependencyInjection\LazyArgument;
910
use Symfony\Component\ExpressionLanguage\Expression;
1011

1112
$container = new ContainerBuilder();
@@ -129,5 +130,9 @@
129130
->register('factory_service_simple', 'Bar')
130131
->setFactory(array(new Reference('factory_simple'), 'getInstance'))
131132
;
133+
$container
134+
->register('lazy_context', 'LazyContext')
135+
->setArguments(array(new LazyArgument(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, new Reference('service_container')))))
136+
;
132137

133138
return $container;

src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ digraph sc {
2626
node_service_from_static_method [label="service_from_static_method\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
2727
node_factory_simple [label="factory_simple\nSimpleFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
2828
node_factory_service_simple [label="factory_service_simple\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"];
29+
node_lazy_context [label="lazy_context\nLazyContext\n", shape=record, fillcolor="#eeeeee", style="filled"];
2930
node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"];
3031
node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"];
3132
node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"];

src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php

+10
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,13 @@ public function getProxyCode(Definition $definition)
9797
return '';
9898
}
9999
}
100+
101+
class LazyContext
102+
{
103+
public $lazyValues;
104+
105+
public function __construct($lazyValues)
106+
{
107+
$this->lazyValues = $lazyValues;
108+
}
109+
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php

+20
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public function __construct()
4343
'foo_bar' => 'getFooBarService',
4444
'foo_with_inline' => 'getFooWithInlineService',
4545
'inlined' => 'getInlinedService',
46+
'lazy_context' => 'getLazyContextService',
4647
'method_call1' => 'getMethodCall1Service',
4748
'new_factory' => 'getNewFactoryService',
4849
'new_factory_service' => 'getNewFactoryServiceService',
@@ -284,6 +285,25 @@ protected function getFooWithInlineService()
284285
return $instance;
285286
}
286287

288+
/**
289+
* Gets the 'lazy_context' service.
290+
*
291+
* This service is shared.
292+
* This method always returns the same instance of the service.
293+
*
294+
* @return \LazyContext A LazyContext instance
295+
*/
296+
protected function getLazyContextService()
297+
{
298+
return $this->services['lazy_context'] = new \LazyContext((function(){
299+
yield 0 => 'foo';
300+
yield 1 => $this->get('foo.baz');
301+
yield 2 => array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo'));
302+
yield 3 => true;
303+
yield 4 => $this;
304+
})());
305+
}
306+
287307
/**
288308
* Gets the 'method_call1' service.
289309
*

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php

+20
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public function __construct()
4040
'foo.baz' => 'getFoo_BazService',
4141
'foo_bar' => 'getFooBarService',
4242
'foo_with_inline' => 'getFooWithInlineService',
43+
'lazy_context' => 'getLazyContextService',
4344
'method_call1' => 'getMethodCall1Service',
4445
'new_factory_service' => 'getNewFactoryServiceService',
4546
'request' => 'getRequestService',
@@ -283,6 +284,25 @@ protected function getFooWithInlineService()
283284
return $instance;
284285
}
285286

287+
/**
288+
* Gets the 'lazy_context' service.
289+
*
290+
* This service is shared.
291+
* This method always returns the same instance of the service.
292+
*
293+
* @return \LazyContext A LazyContext instance
294+
*/
295+
protected function getLazyContextService()
296+
{
297+
return $this->services['lazy_context'] = new \LazyContext((function(){
298+
yield 0 => 'foo';
299+
yield 1 => $this->get('foo.baz');
300+
yield 2 => array('bar' => 'foo is '.'bar'.'', 'foobar' => 'bar');
301+
yield 3 => true;
302+
yield 4 => $this;
303+
})());
304+
}
305+
286306
/**
287307
* Gets the 'method_call1' service.
288308
*

src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml

+12
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,18 @@
115115
<service id="factory_service_simple" class="Bar">
116116
<factory service="factory_simple" method="getInstance"/>
117117
</service>
118+
<service id="lazy_context" class="LazyContext">
119+
<argument type="lazy">
120+
<argument>foo</argument>
121+
<argument type="service" id="foo.baz"/>
122+
<argument type="collection">
123+
<argument key="%foo%">foo is %foo%</argument>
124+
<argument key="foobar">%foo%</argument>
125+
</argument>
126+
<argument>true</argument>
127+
<argument type="service" id="service_container"/>
128+
</argument>
129+
</service>
118130
<service id="alias_for_foo" alias="foo"/>
119131
<service id="alias_for_alias" alias="foo"/>
120132
</services>

src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml

+3
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,8 @@ services:
107107
factory_service_simple:
108108
class: Bar
109109
factory: ['@factory_simple', getInstance]
110+
lazy_context:
111+
class: LazyContext
112+
arguments: [[foo, '@foo.baz', { '%foo%': 'foo is %foo%', foobar: '%foo%' }, true, '@service_container']]
110113
alias_for_foo: '@foo'
111114
alias_for_alias: '@foo'

src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php

+12
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\DependencyInjection\ContainerInterface;
1515
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\LazyArgument;
1617
use Symfony\Component\DependencyInjection\Reference;
1718
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
1819
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
@@ -257,6 +258,17 @@ public function testLoadServices()
257258
$this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService());
258259
}
259260

261+
public function testParsesLazyArgument()
262+
{
263+
$container = new ContainerBuilder();
264+
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
265+
$loader->load('services9.xml');
266+
267+
$lazyDefinition = $container->getDefinition('lazy_context');
268+
269+
$this->assertEquals(array(new LazyArgument(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, new Reference('service_container')))), $lazyDefinition->getArguments(), '->load() parses lazy arguments');
270+
}
271+
260272
public function testParsesTags()
261273
{
262274
$container = new ContainerBuilder();

0 commit comments

Comments
 (0)