8000 [DI] Enable ServiceLocator to become a service, with AutowirableRefer… · symfony/symfony@c831e2d · GitHub
[go: up one dir, main page]

Skip to content

Commit c831e2d

Browse files
[DI] Enable ServiceLocator to become a service, with AutowirableReference
1 parent 3729a15 commit c831e2d

File tree

11 files changed

+154
-0
lines changed

11 files changed

+154
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\DependencyInjection\Alias;
1515
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
16+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
1718
use Symfony\Component\DependencyInjection\Definition;
1819
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
@@ -399,6 +400,10 @@ private function describeValue($value, $omitTags, $showArguments)
399400
);
400401
}
401402

403+
if ($value instanceof ServiceClosureArgument) {
404+
return $this->describeValue($value->getValues()[0], $omitTags, $showArguments);
405+
}
406+
402407
if ($value instanceof ArgumentInterface) {
403408
return $this->describeValue($value->getValues(), $omitTags, $showArguments);
404409
}

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\DependencyInjection\Alias;
1717
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1818
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
19+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1920
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
2021
use Symfony\Component\DependencyInjection\ContainerBuilder;
2122
use Symfony\Component\DependencyInjection\Definition;
@@ -332,6 +333,8 @@ protected function describeContainerDefinition(Definition $definition, array $op
332333
$argumentsInformation[] = sprintf('Service(%s)', (string) $argument);
333334
} elseif ($argument instanceof IteratorArgument) {
334335
$argumentsInformation[] = sprintf('Iterator (%d element(s))', count($argument->getValues()));
336+
} elseif ($argument instanceof ServiceClosureArgument) {
337+
$argumentsInformation[] = sprintf('Service(%s)', (string) $argument->getValues()[0]);
335338
} elseif ($argument instanceof ServiceLocatorArgument) {
336339
$argumentsInformation[] = sprintf('ServiceLocator (%d service(s))', count($argument->getValues()));
337340
} elseif ($argument instanceof ClosureProxyArgument) {

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\DependencyInjection\Alias;
1515
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1616
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
17+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1718
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1819
use Symfony\Component\DependencyInjection\ContainerBuilder;
1920
use Symfony\Component\DependencyInjection\Definition;
@@ -446,6 +447,9 @@ private function getArgumentNodes(array $arguments, \DOMDocument $dom)
446447
foreach ($this->getArgumentNodes($argument->getValues(), $dom) as $childArgumentXML) {
447448
$argumentXML->appendChild($childArgumentXML);
448449
}
450+
} elseif ($argument instanceof ServiceClosureArgument) {
451+
$argumentXML->setAttribute('type', 'service');
452+
$argumentXML->setAttribute('id', (string) $argument->getValues()[0]);
449453
} elseif ($argument instanceof ServiceLocatorArgument) {
450454
$argumentXML->setAttribute('type', 'service-locator');
451455

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
use Symfony\Component\DependencyInjection\Reference;
15+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16+
17+
/**
18+
* Represents a service wrapped in a memoizing closure.
19+
*
20+
* @author Nicolas Grekas <p@tchwork.com>
21+
*
22+
* @experimental in version 3.3
23+
*/
24+
class ServiceClosureArgument implements ArgumentInterface
25+
{
26+
private $values;
27+
28+
public function __construct(Reference $reference)
29+
{
30+
$this->values = array($reference);
31+
}
32+
33+
public function getValues()
34+
{
35+
return $this->values;
36+
}
37+
38+
public function setValues(array $values)
39+
{
40+
if (array(0) !== array_keys($values) || !($values[0] instanceof Reference || null === $values[0])) {
41+
throw new InvalidArgumentException('A ServiceClosureArgument must hold one and only one Reference.');
42+
}
43+
44+
$this->values = $values;
45+
}
46+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 typed service reference that can be autowired if the target is invalid.
16+
*
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*
19+
* @experimental in version 3.3
20+
*/
21+
class AutowirableReference extends Reference
22+
{
23+
private $id;
24+
private $type;
25+
private $autoRegister;
26+
27+
/**
28+
* @param string $id The service identifier
29+
* @param string $type The type of the identified service
30+
* @param int $invalidBehavior The behavior when the service does not exist
31+
* @param bool $autoRegister Whether new services should be created for discovered classes or not
32+
*
33+
* @see Container
34+
*/
35+
public function __construct($id, $type, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $autoRegister = true)
36+
{
37+
parent::__construct($id, $invalidBehavior);
38+
$this->id = $id;
39+
$this->type = $type;
40+
$this->autoRegister = $autoRegister;
41+
}
42+
43+
public function getType()
44+
{
45+
return $this->type;
46+
}
47+
48+
public function autoRegister()
49+
{
50+
return $this->autoRegister;
51+
}
52+
53+
public function setId($id)
54+
{
55+
$this->id = (string) $id;
56+
}
57+
58+
/**
59+
* {@inheritdoc}
60+
*/
61+
public function __toString()
62+
{
63+
return $this->id;
64+
}
65+
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
3.3.0
55
-----
66

7+
* [EXPERIMENTAL] added "AutowirableReference" and "ServiceClosureArgument" for autowirable service-locator services
78
* [EXPERIMENTAL] added "instanceof" section for local interface-defined configs
89
* added "service-locator" argument for lazy loading a set of identified values and services
910
* [EXPERIMENTAL] added prototype services for PSR4-based discovery and registration

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\AutowirableReference;
1415
use Symfony\Component\DependencyInjection\Config\AutowireServiceResource;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\ContainerInterface;
1618
use Symfony\Component\DependencyInjection\Definition;
1719
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
1820
use Symfony\Component\DependencyInjection\LazyProxy\InheritanceProxyHelper;
@@ -100,6 +102,18 @@ public static function createResourceForClass(\ReflectionClass $reflectionClass)
100102
*/
101103
protected function processValue($value, $isRoot = false)
102104
{
105+
if ($value instanceof AutowirableReference) {
106+
if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior() || $this->container->has((string) $value)) {
107+
return $value;
108+
}
109+
110+
if ($ref = $this->getAutowiredReference($value->getType(), $value->autoRegister())) {
111+
$value->setId($ref);
112+
}
113+
114+
return $value;
115+
}
116+
103117
if (!$value instanceof Definition || !$value->getAutowiredCalls()) {
104118
return parent::processValue($value, $isRoot);
105119
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1515
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1616
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
17+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1718
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1819
use Symfony\Component\DependencyInjection\Compiler\Compiler;
1920
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
@@ -1128,6 +1129,11 @@ public function resolveServices($value)
11281129
foreach ($value as $k => $v) {
11291130
$value[$k] = $this->resolveServices($v);
11301131
}
1132+
} elseif ($value instanceof ServiceClosureArgument) {
1133+
$reference = $value->getValues()[0];
1134+
$value = function () use ($reference) {
1135+
return $this->get((string) $reference, $reference->getInvalidBehavior());
1136+
};
11311137
} elseif ($value instanceof ServiceLocatorArgument) {
11321138
$parameterBag = $this->getParameterBag();
11331139
$services = array();

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1515
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1616
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
17+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1718
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1819
use Symfony\Component\DependencyInjection\Variable;
1920
use Symfony\Component\DependencyInjection\Definition;
@@ -1540,6 +1541,8 @@ private function dumpValue($value, $interpolate = true)
15401541
}
15411542

15421543
return sprintf('array(%s)', implode(', ', $code));
1544+
} elseif ($value instanceof ServiceClosureArgument) {
1545+
return sprintf("function () {\n return %s;\n }", $this->dumpValue($value->getValues()[0], $interpolate));
15431546
} elseif ($value instanceof ServiceLocatorArgument) {
15441547
$code = "\n";
15451548
foreach ($value->getValues() as $k => $v) {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1515
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
16+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1617
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1718
use Symfony\Component\DependencyInjection\ContainerInterface;
1819
use Symfony\Component\DependencyInjection\Parameter;
@@ -289,6 +290,9 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent
289290
$element->setAttribute($keyAttribute, $key);
290291
}
291292

293+
if ($value instanceof ServiceClosureArgument) {
294+
$value = $value->getValues()[0];
295+
}
292296
if (is_array($value)) {
293297
$element->setAttribute('type', 'collection');
294298
$this->convertParameters($value, $type, $element, 'key');

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ private function dumpCallable($callable)
254254
*/
255255
private function dumpValue($value)
256256
{
257+
if ($value instanceof ServiceClosureArgument) {
258+
$value = $value->getValues()[0];
259+
}
257260
if ($value instanceof ArgumentInterface) {
258261
if ($value instanceof IteratorArgument) {
259262
$tag = 'iterator';

0 commit comments

Comments
 (0)
0