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 da4e441

Browse filesBrowse files
[DI] Allow creating ServiceLocator-based services in extensions
1 parent 3729a15 commit da4e441
Copy full SHA for da4e441

File tree

11 files changed

+176
-1
lines changed
Filter options

11 files changed

+176
-1
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
+5Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
+3Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
+4Lines 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

+46Lines 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+
}
+65Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/CHANGELOG.md
+1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+14Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+6Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+25-1Lines changed: 25 additions & 1 deletion
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,10 +1541,12 @@ private function dumpValue($value, $interpolate = true)
15401541
}
15411542

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

@@ -1681,6 +1684,27 @@ private function dumpValue($value, $interpolate = true)
16811684
return $this->export($value);
16821685
}
16831686

1687+
private function dumpServiceClosure(Reference $reference, $interpolate, $oneLine)
1688+
{
1689+
$type = '';
1690+
if (PHP_VERSION_ID >= 70000 && $reference instanceof AutowirableReference) {
1691+
$type = $reference->getType();
1692+
if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $reference->getInvalidBehavior()) {
1693+
$type = ': \\'.$type;
1694+
} elseif (PHP_VERSION_ID >= 70100) {
1695+
$type = ': ?\\'.$type;
1696+
} else {
1697+
$type = '';
1698+
}
1699+
}
1700+
1701+
if ($oneLine) {
1702+
return sprintf('function ()%s { return %s; }', $type, $this->dumpValue($reference, $interpolate));
1703+
}
1704+
1705+
return sprintf("function ()%s {\n return %s;\n }", $type, $this->dumpValue($reference, $interpolate));
1706+
}
1707+
16841708
/**
16851709
* Dumps a string to a literal (aka PHP Code) class value.
16861710
*

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
+4Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
+3Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.