Skip to content

Commit 5ad4e57

Browse files
[DI] Add ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE
1 parent b9fc357 commit 5ad4e57

25 files changed

+380
-30
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

1414
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
15+
use Symfony\Component\DependencyInjection\ContainerInterface;
1516
use Symfony\Component\DependencyInjection\Definition;
1617
use Symfony\Component\DependencyInjection\ExpressionLanguage;
1718
use Symfony\Component\DependencyInjection\Reference;
@@ -96,7 +97,8 @@ protected function processValue($value, $isRoot = false)
9697
$this->getDefinitionId((string) $value),
9798
$targetDefinition,
9899
$value,
99-
$this->lazy || ($targetDefinition && $targetDefinition->isLazy())
100+
$this->lazy || ($targetDefinition && $targetDefinition->isLazy()),
101+
ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()
100102
);
101103

102104
return $value;

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

Lines changed: 10 additions & 7 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\Exception\InvalidArgumentException;
1415
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
1516
use Symfony\Component\DependencyInjection\ContainerInterface;
1617
use Symfony\Component\DependencyInjection\Reference;
@@ -24,14 +25,16 @@ class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass
2425
{
2526
protected function processValue($value, $isRoot = false)
2627
{
27-
if ($value instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
28-
$destId = (string) $value;
29-
30-
if (!$this->container->has($destId)) {
31-
throw new ServiceNotFoundException($destId, $this->currentId);
32-
}
28+
if (!$value instanceof Reference) {
29+
return parent::processValue($value, $isRoot);
30+
}
31+
if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior() && !$this->container->has($id = (string) $value)) {
32+
throw new ServiceNotFoundException($id, $this->currentId);
33+
}
34+
if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior() && $this->container->has($id = (string) $value) && !$this->container->getDefinition($id)->isShared()) {
35+
throw new InvalidArgumentException(sprintf('Invalid ignore-on-uninitialized reference found in service "%s": target service "%s" is not shared.', $this->currentId, $id));
3336
}
3437

35-
return parent::processValue($value, $isRoot);
38+
return $value;
3639
}
3740
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

1414
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
15+
use Symfony\Component\DependencyInjection\ContainerInterface;
1516
use Symfony\Component\DependencyInjection\Definition;
1617
use Symfony\Component\DependencyInjection\Reference;
1718

src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ protected function processValue($value, $isRoot = false)
7474
if ($optionalBehavior = '?' === $type[0]) {
7575
$type = substr($type, 1);
7676
$optionalBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
77+
} elseif ($optionalBehavior = '!' === $type[0]) {
78+
$type = substr($type, 1);
79+
$optionalBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
7780
}
7881
if (is_int($key)) {
7982
$key = $type;

src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public function process(ContainerBuilder $container)
5050
$referencingAliases = array();
5151
$sourceIds = array();
5252
foreach ($edges as $edge) {
53+
if ($edge->isWeak()) {
54+
continue;
55+
}
5356
$node = $edge->getSourceNode();
5457
$sourceIds[] = $node->getId();
5558

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,23 +85,26 @@ public function clear()
8585
* @param string $destValue
8686
* @param string $reference
8787
* @param bool $lazy
88+
* @param bool $weak
8889
*/
89-
public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null/*, bool $lazy = false*/)
90+
public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null/*, bool $lazy = false, bool $weak = false*/)
9091
{
91-
if (func_num_args() >= 6) {
92+
if (func_num_args() >= 7) {
9293
$lazy = func_get_arg(5);
94+
$weak = func_get_arg(6);
9395
} else {
9496
if (__CLASS__ !== get_class($this)) {
9597
$r = new \ReflectionMethod($this, __FUNCTION__);
9698
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
97-
@trigger_error(sprintf('Method %s() will have a 6th `bool $lazy = false` argument in version 4.0. Not defining it is deprecated since 3.3.', __METHOD__), E_USER_DEPRECATED);
99+
@trigger_error(sprintf('Method %s() will have a two additional `bool $lazy = false, bool $weak = false` arguments in version 4.0. Not defining them is deprecated since version 3.4.', __METHOD__), E_USER_DEPRECATED);
98100
}
99101
}
100102
$lazy = false;
103+
$weak = false;
101104
}
102105
$sourceNode = $this->createNode($sourceId, $sourceValue);
103106
$destNode = $this->createNode($destId, $destValue);
104-
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy);
107+
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak);
105108

106109
$sourceNode->addOutEdge($edge);
107110
$destNode->addInEdge($edge);

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,22 @@ class ServiceReferenceGraphEdge
2424
private $destNode;
2525
private $value;
2626
private $lazy;
27+
private $weak;
2728

2829
/**
2930
* @param ServiceReferenceGraphNode $sourceNode
3031
* @param ServiceReferenceGraphNode $destNode
3132
* @param string $value
3233
* @param bool $lazy
34+
* @param bool $weak
3335
*/
34-
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, $lazy = false)
36+
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, $lazy = false, $weak = false)
3537
{
3638
$this->sourceNode = $sourceNode;
3739
$this->destNode = $destNode;
3840
$this->value = $value;
3941
$this->lazy = $lazy;
42+
$this->weak = $weak;
4043
}
4144

4245
/**
@@ -78,4 +81,15 @@ public function isLazy()
7881
{
7982
return $this->lazy;
8083
}
84+
85+
86+
/**
87+
* Returns true if the edge is weak, meaning it should prevent removing the target service.
88+
*
89+
* @return bool
90+
*/
91+
public function isWeak()
92+
{
93+
return $this->weak;
94+
}
8195
}

src/Symfony/Component/DependencyInjection/Container.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
* * NULL_ON_INVALID_REFERENCE: Returns null
4444
* * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference
4545
* (for instance, ignore a setter if the service does not exist)
46+
* * IGNORE_ON_UNINITIALIZED_REFERENCE: Ignores/returns null for uninitialized services
4647
*
4748
* @author Fabien Potencier <fabien@symfony.com>
4849
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
@@ -304,9 +305,9 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE
304305

305306
try {
306307
if (isset($this->fileMap[$id])) {
307-
return $this->load($this->fileMap[$id]);
308+
return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->load($this->fileMap[$id]);
308309
} elseif (isset($this->methodMap[$id])) {
309-
return $this->{$this->methodMap[$id]}();
310+
return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->{$this->methodMap[$id]}();
310311
} elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
311312
$id = $normalizedId;
312313
continue;
@@ -315,7 +316,7 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE
315316
// and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
316317
@trigger_error('Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED);
317318

318-
return $this->{$method}();
319+
return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->{$method}();
319320
}
320321

321322
break;

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,9 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV
565565
{
566566
$id = $this->normalizeId($id);
567567

568+
if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) {
569+
return parent::get($id, $invalidBehavior);
570+
}
568571
if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
569572
return $service;
570573
}
@@ -1160,6 +1163,11 @@ public function resolveServices($value)
11601163
continue 2;
11611164
}
11621165
}
1166+
foreach (self::getInitializedConditionals($v) as $s) {
1167+
if (!$this->get($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
1168+
continue 2;
1169+
}
1170+
}
11631171

11641172
yield $k => $this->resolveServices($v);
11651173
}
@@ -1171,6 +1179,11 @@ public function resolveServices($value)
11711179
continue 2;
11721180
}
11731181
}
1182+
foreach (self::getInitializedConditionals($v) as $s) {
1183+
if (!$this->get($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
1184+
continue 2;
1185+
}
1186+
}
11741187

11751188
++$count;
11761189
}
@@ -1397,6 +1410,8 @@ public function log(CompilerPassInterface $pass, $message)
13971410
* @param mixed $value An array of conditionals to return
13981411
*
13991412
* @return array An array of Service conditionals
1413+
*
1414+
* @internal since version 3.4
14001415
*/
14011416
public static function getServiceConditionals($value)
14021417
{
@@ -1413,6 +1428,30 @@ public static function getServiceConditionals($value)
14131428
return $services;
14141429
}
14151430

1431+
/**
1432+
* Returns the initialized conditionals.
1433+
*
1434+
* @param mixed $value An array of conditionals to return
1435+
*
1436+
* @return array An array of uninitialized conditionals
1437+
*
1438+
* @internal
1439+
*/
1440+
public static function getInitializedConditionals($value)
1441+
{
1442+
$services = array();
1443+
1444+
if (is_array($value)) {
1445+
foreach ($value as $v) {
1446+
$services = array_unique(array_merge($services, self::getInitializedConditionals($v)));
1447+
}
1448+
} elseif ($value instanceof Reference && $value->getInvalidBehavior() === ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE) {
1449+
$services[] = (string) $value;
1450+
}
1451+
1452+
return $services;
1453+
}
1454+
14161455
/**
14171456
* Computes a reasonably unique hash of a value.
14181457
*
@@ -1465,13 +1504,16 @@ private function getProxyInstantiator()
14651504

14661505
private function callMethod($service, $call)
14671506
{
1468-
$services = self::getServiceConditionals($call[1]);
1469-
1470-
foreach ($services as $s) {
1507+
foreach (self::getServiceConditionals($call[1]) as $s) {
14711508
if (!$this->has($s)) {
14721509
return;
14731510
}
14741511
}
1512+
foreach (self::getInitializedConditionals($call[1]) as $s) {
1513+
if (!$this->get($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
1514+
return;
1515+
}
1516+
}
14751517

14761518
call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1]))));
14771519
}

src/Symfony/Component/DependencyInjection/ContainerInterface.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface ContainerInterface extends PsrContainerInterface
2727
const EXCEPTION_ON_INVALID_REFERENCE = 1;
2828
const NULL_ON_INVALID_REFERENCE = 2;
2929
const IGNORE_ON_INVALID_REFERENCE = 3;
30+
const IGNORE_ON_UNINITIALIZED_REFERENCE = 4;
3031

3132
/**
3233
* Sets a service.

0 commit comments

Comments
 (0)