Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0f50b4d

Browse filesBrowse files
committed
feature #20907 [DependencyInjection] Implement lazy collection type using generators (tgalopin, nicolas-grekas)
This PR was merged into the 3.3-dev branch. Discussion ---------- [DependencyInjection] Implement lazy collection type using generators | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | - | Fixed tickets | #20875 | License | MIT | Doc PR | - Following the RFC #20875, this PR aims to implement lazy services collections using generators internally. For now, I suggest to support only XML and PHP definitions. Supporting YAML means adding another convention to the language and this is not the aim of this PR. - [x] value object holding the semantic (IteratorArgument) - [x] iterator dumping - [x] lazyness awareness in CheckCircularReferencesPass & GraphvizDumper - [x] rewindable iterators - [x] `*_ON_INVALID_REFERENCE` behavior - [x] ContainerBuilder::createService - [x] ArgumentInterface handling in compiler passes Commits ------- 1dbf52a [DI] Add "=iterator" arguments to Yaml loader 5313943 [DependencyInjection] Implement lazy collection type using generators
2 parents 65d73f5 + 1dbf52a commit 0f50b4d
Copy full SHA for 0f50b4d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner

45 files changed

+557
-15
lines changed
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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\Argument;
13+
14+
/**
15+
* Represents a complex argument containing nested values.
16+
*
17+
* @author Titouan Galopin <galopintitouan@gmail.com>
18+
*/
19+
interface ArgumentInterface
20+
{
21+
/**
22+
* @return array
23+
*/
24+
public function getValues();
25+
26+
public function setValues(array $values);
27+
}
+43Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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\Argument;
13+
14+
/**
15+
* Represents a collection of values to lazily iterate over.
16+
*
17+
* @author Titouan Galopin <galopintitouan@gmail.com>
18+
*/
19+
class IteratorArgument implements ArgumentInterface
20+
{
21+
private $values;
22+
23+
public function __construct(array $values)
24+
{
25+
$this->values = $values;
26+
}
27+
28+
/**
29+
* @return array The values to lazily iterate over
30+
*/
31+
public function getValues()
32+
{
33+
return $this->values;
34+
}
35+
36+
/**
37+
* @param array $values The values to lazily iterate over
38+
*/
39+
public function setValues(array $values)
40+
{
41+
$this->values = $values;
42+
}
43+
}
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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\Argument;
13+
14+
/**
15+
* @internal
16+
*/
17+
class RewindableGenerator implements \IteratorAggregate
18+
{
19+
private $generator;
20+
21+
public function __construct(callable $generator)
22+
{
23+
$this->generator = $generator;
24+
}
25+
26+
public function getIterator()
27+
{
28+
$g = $this->generator;
29+
30+
return $g();
31+
}
32+
}

‎src/Symfony/Component/DependencyInjection/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ CHANGELOG
44
3.3.0
55
-----
66

7+
* Add "iterator" argument type for lazy iteration over a set of values and services
8+
79
* Using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and
810
will not be supported anymore in 4.0.
911

‎src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php
+11-4Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1415
use Symfony\Component\DependencyInjection\Definition;
1516
use Symfony\Component\DependencyInjection\Reference;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -91,19 +92,25 @@ public function process(ContainerBuilder $container)
9192
* Processes service definitions for arguments to find relationships for the service graph.
9293
*
9394
* @param array $arguments An array of Reference or Definition objects relating to service definitions
95+
* @param bool $lazy Whether the references nested in the arguments should be considered lazy or not
9496
*/
95-
private function processArguments(array $arguments)
97+
private function processArguments(array $arguments, $lazy = false)
9698
{
9799
foreach ($arguments as $argument) {
98100
if (is_array($argument)) {
99-
$this->processArguments($argument);
101+
$this->processArguments($argument, $lazy);
102+
} elseif ($argument instanceof ArgumentInterface) {
103+
$this->processArguments($argument->getValues(), true);
100104
} elseif ($argument instanceof Reference) {
105+
$targetDefinition = $this->getDefinition((string) $argument);
106+
101107
$this->graph->connect(
102108
$this->currentId,
103109
$this->currentDefinition,
104110
$this->getDefinitionId((string) $argument),
105-
$this->getDefinition((string) $argument),
106-
$argument
111+
$targetDefinition,
112+
$argument,
113+
$lazy || ($targetDefinition && $targetDefinition->isLazy())
107114
);
108115
} elseif ($argument instanceof Definition) {
109116
$this->processArguments($argument->getArguments());

‎src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php
+2-3Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,8 @@ private function checkOutEdges(array $edges)
6060
$id = $node->getId();
6161

6262
if (empty($this->checkedNodes[$id])) {
63-
64-
// don't check circular dependencies for lazy services
65-
if (!$node->getValue() || !$node->getValue()->isLazy()) {
63+
// Don't check circular references for lazy edges
64+
if (!$node->getValue() || !$edge->isLazy()) {
6665
$searchKey = array_search($id, $this->currentPath);
6766
$this->currentPath[] = $id;
6867

‎src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1415
use Symfony\Component\DependencyInjection\Definition;
1516
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
1617
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -49,6 +50,8 @@ private function processReferences(array $arguments)
4950
foreach ($arguments as $argument) {
5051
if (is_array($argument)) {
5152
$this->processReferences($argument);
53+
} elseif ($argument instanceof ArgumentInterface) {
54+
$this->processReferences($argument->getValues());
5255
} elseif ($argument instanceof Definition) {
5356
$this->processDefinition($argument);
5457
} elseif ($argument instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $argument->getInvalidBehavior()) {

‎src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1415
use Symfony\Component\DependencyInjection\Definition;
1516
use Symfony\Component\DependencyInjection\Reference;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -63,6 +64,8 @@ private function validateReferences(array $arguments)
6364
foreach ($arguments as $argument) {
6465
if (is_array($argument)) {
6566
$this->validateReferences($argument);
67+
} elseif ($argument instanceof ArgumentInterface) {
68+
$this->validateReferences($argument->getValues());
6669
} elseif ($argument instanceof Reference) {
6770
$targetDefinition = $this->getDefinition((string) $argument);
6871

‎src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1415
use Symfony\Component\DependencyInjection\Definition;
1516
use Symfony\Component\DependencyInjection\Reference;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -67,6 +68,8 @@ private function inlineArguments(ContainerBuilder $container, array $arguments,
6768
}
6869
if (is_array($argument)) {
6970
$arguments[$k] = $this->inlineArguments($container, $argument);
71+
} elseif ($argument instanceof ArgumentInterface) {
72+
$argument->setValues($this->inlineArguments($container, $argument->getValues()));
7073
} elseif ($argument instanceof Reference) {
7174
if (!$container->hasDefinition($id = (string) $argument)) {
7275
continue;

‎src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1415
use Symfony\Component\DependencyInjection\ContainerBuilder;
1516
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1617
use Symfony\Component\DependencyInjection\Reference;
@@ -98,6 +99,10 @@ private function updateArgumentReferences(array $replacements, $definitionId, ar
9899
$arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument);
99100
continue;
100101
}
102+
if ($argument instanceof ArgumentInterface) {
103+
$argument->setValues($this->updateArgumentReferences($replacements, $definitionId, $argument->getValues()));
104+
continue;
105+
}
101106
// Skip arguments that don't need replacement
102107
if (!$argument instanceof Reference) {
103108
continue;

‎src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1415
use Symfony\Component\DependencyInjection\ChildDefinition;
1516
use Symfony\Component\DependencyInjection\Definition;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -63,6 +64,8 @@ private function resolveArguments(ContainerBuilder $container, array $arguments,
6364
}
6465
if (is_array($argument)) {
6566
$arguments[$k] = $this->resolveArguments($container, $argument);
67+
} elseif ($argument instanceof ArgumentInterface) {
68+
$argument->setValues($this->resolveArguments($container, $argument->getValues()));
6669
} elseif ($argument instanceof Definition) {
6770
if ($argument instanceof ChildDefinition) {
6871
$arguments[$k] = $argument = $this->resolveDefinition($container, $argument);

‎src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1415
use Symfony\Component\DependencyInjection\ContainerInterface;
1516
use Symfony\Component\DependencyInjection\Reference;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -84,6 +85,8 @@ private function processArguments(array $arguments, $inMethodCall = false, $inCo
8485
foreach ($arguments as $k => $argument) {
8586
if (is_array($argument)) {
8687
$arguments[$k] = $this->processArguments($argument, $inMethodCall, true);
88+
} elseif ($argument instanceof ArgumentInterface) {
89+
$argument->setValues($this->processArguments($argument->getValues(), $inMethodCall, true));
8790
} elseif ($argument instanceof Reference) {
8891
$id = (string) $argument;
8992

‎src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1415
use Symfony\Component\DependencyInjection\Alias;
1516
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
1617
use Symfony\Component\DependencyInjection\Reference;
@@ -65,6 +66,8 @@ private function processArguments(array $arguments)
6566
foreach ($arguments as $k => $argument) {
6667
if (is_array($argument)) {
6768
$arguments[$k] = $this->processArguments($argument);
69+
} elseif ($argument instanceof ArgumentInterface) {
70+
$argument->setValues($this->processArguments($argument->getValues()));
6871
} elseif ($argument instanceof Reference) {
6972
$defId = $this->getDefinitionId($id = (string) $argument);
7073

‎src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php
+14-2Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,24 @@ public function clear()
8484
* @param string $destId
8585
* @param string $destValue
8686
* @param string $reference
87+
* @param bool $lazy
8788
*/
88-
public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null)
89+
public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null/*, $lazy = false*/)
8990
{
91+
if (func_num_args() >= 6) {
92+
$lazy = func_get_arg(5);
93+
} else {
94+
if (__CLASS__ !== get_class($this)) {
95+
$r = new \ReflectionMethod($this, __FUNCTION__);
96+
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
97+
@trigger_error(sprintf('Method %s() will have a 6th `$lazy = false` argument in version 4.0. Not defining it is deprecated since 3.3.', get_class($this), __FUNCTION__), E_USER_DEPRECATED);
98+
}
99+
}
100+
$lazy = false;
101+
}
90102
$sourceNode = $this->createNode($sourceId, $sourceValue);
91103
$destNode = $this->createNode($destId, $destValue);
92-
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference);
104+
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy);
93105

94106
$sourceNode->addOutEdge($edge);
95107
$destNode->addInEdge($edge);

‎src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php
+14-1Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,20 @@ class ServiceReferenceGraphEdge
2323
private $sourceNode;
2424
private $destNode;
2525
private $value;
26+
private $lazy;
2627

2728
/**
2829
* @param ServiceReferenceGraphNode $sourceNode
2930
* @param ServiceReferenceGraphNode $destNode
3031
* @param string $value
32+
* @param bool $lazy
3133
*/
32-
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null)
34+
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, $lazy = false)
3335
{
3436
$this->sourceNode = $sourceNode;
3537
$this->destNode = $destNode;
3638
$this->value = $value;
39+
$this->lazy = $lazy;
3740
}
3841

3942
/**
@@ -65,4 +68,14 @@ public function getDestNode()
6568
{
6669
return $this->destNode;
6770
}
71+
72+
/**
73+
* Returns true if the edge is lazy, meaning it's a dependency not requiring direct instantiation.
74+
*
75+
* @return bool
76+
*/
77+
public function isLazy()
78+
{
79+
return $this->lazy;
80+
}
6881
}

‎src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\DependencyInjection;
1313

14+
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
15+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
1416
use Symfony\Component\DependencyInjection\Compiler\Compiler;
1517
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1618
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
@@ -961,6 +963,19 @@ public function resolveServices($value)
961963
foreach ($value as $k => $v) {
962964
$value[$k] = $this->resolveServices($v);
963965
}
966+
} elseif ($value instanceof IteratorArgument) {
967+
$parameterBag = $this->getParameterBag();
968+
$value = new RewindableGenerator(function () use ($value, $parameterBag) {
969+
foreach ($value->getValues() as $k => $v) {
970+
foreach (self::getServiceConditionals($v) as $s) {
971+
if (!$this->has($s)) {
972+
continue 2;
973+
}
974+
}
975+
976+
yield $k => $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($v)));
977+
}
978+
});
964979
} elseif ($value instanceof Reference) {
965980
$value = $this->get((string) $value, $value->getInvalidBehavior());
966981
} elseif ($value instanceof Definition) {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.