diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
index 408ed3c40d..9c950c75ed 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
@@ -275,9 +275,6 @@ class XmlDumper extends Dumper
$element->setAttribute($keyAttribute, $key);
}
- if ($value instanceof ServiceClosureArgument) {
- $value = $value->getValues()[0];
- }
if (\is_array($tag = $value)) {
$element->setAttribute('type', 'collection');
$this->convertParameters($value, $type, $element, 'key');
@@ -301,8 +298,12 @@ class XmlDumper extends Dumper
} elseif ($value instanceof ServiceLocatorArgument) {
$element->setAttribute('type', 'service_locator');
$this->convertParameters($value->getValues(), $type, $element, 'key');
- } elseif ($value instanceof Reference) {
+ } elseif ($value instanceof Reference || $value instanceof ServiceClosureArgument) {
$element->setAttribute('type', 'service');
+ if ($value instanceof ServiceClosureArgument) {
+ $element->setAttribute('type', 'service_closure');
+ $value = $value->getValues()[0];
+ }
$element->setAttribute('id', (string) $value);
$behavior = $value->getInvalidBehavior();
if (ContainerInterface::NULL_ON_INVALID_REFERENCE == $behavior) {
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
index c590526b6d..cf40ec0bf4 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
@@ -254,6 +254,8 @@ class YamlDumper extends Dumper
{
if ($value instanceof ServiceClosureArgument) {
$value = $value->getValues()[0];
+
+ return new TaggedValue('service_closure', $this->getServiceCall((string) $value, $value));
}
if ($value instanceof ArgumentInterface) {
$tag = $value;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
index a5b42f8835..5dfe97355f 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
@@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
@@ -494,6 +495,13 @@ class XmlFileLoader extends FileLoader
throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".', $name, $file));
}
break;
+ case 'service_closure':
+ if ('' === $arg->getAttribute('id')) {
+ throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service_closure" has no or empty "id" attribute in "%s".', $name, $file));
+ }
+
+ $arguments[$key] = new ServiceClosureArgument(new Reference($arg->getAttribute('id'), $invalidBehavior));
+ break;
case 'service_locator':
$arg = $this->getArgumentsAsPhp($arg, $name, $file);
try {
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index 4fe4839179..bf7ad8b721 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
@@ -797,6 +798,15 @@ class YamlFileLoader extends FileLoader
throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file));
}
}
+ if ('service_closure' === $value->getTag()) {
+ $argument = $this->resolveServices($argument, $file, $isParameter);
+
+ if (!$argument instanceof Reference) {
+ throw new InvalidArgumentException(sprintf('"!service_closure" tag only accepts service references in "%s".', $file));
+ }
+
+ return new ServiceClosureArgument($argument);
+ }
if ('service_locator' === $value->getTag()) {
if (!\is_array($argument)) {
throw new InvalidArgumentException(sprintf('"!service_locator" tag only accepts maps in "%s".', $file));
diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
index 03988686ba..3d98eafc37 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
+++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
@@ -291,6 +291,7 @@
+
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php
index f05a1c5ee3..83b4cf63d2 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php
@@ -14,6 +14,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -252,6 +253,17 @@ class XmlDumperTest extends TestCase
$this->assertStringEqualsFile(self::$fixturesPath.'/xml/services_with_tagged_arguments.xml', $dumper->dump());
}
+ public function testServiceClosure()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'Foo')
+ ->addArgument(new ServiceClosureArgument(new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))
+ ;
+
+ $dumper = new XmlDumper($container);
+ $this->assertStringEqualsFile(self::$fixturesPath.'/xml/services_with_service_closure.xml', $dumper->dump());
+ }
+
public function testDumpAbstractServices()
{
$container = include self::$fixturesPath.'/containers/container_abstract.php';
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php
index 3f4d5bc1fb..793002eebb 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php
@@ -14,6 +14,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -119,6 +120,17 @@ class YamlDumperTest extends TestCase
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_with_tagged_argument.yml', $dumper->dump());
}
+ public function testServiceClosure()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'Foo')
+ ->addArgument(new ServiceClosureArgument(new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))
+ ;
+
+ $dumper = new YamlDumper($container);
+ $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_with_service_closure.yml', $dumper->dump());
+ }
+
public function testDumpServiceWithAbstractArgument()
{
$container = new ContainerBuilder();
@@ -130,6 +142,7 @@ class YamlDumperTest extends TestCase
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_with_abstract_argument.yml', $dumper->dump());
}
+
private function assertEqualYamlStructure(string $expected, string $yaml, string $message = '')
{
$parser = new Parser();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_service_closure.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_service_closure.xml
new file mode 100644
index 0000000000..a202b33249
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_service_closure.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+ The "%alias_id%" autowiring alias is deprecated. Define it explicitly in your app if you want to keep using it.
+
+
+ The "%alias_id%" autowiring alias is deprecated. Define it explicitly in your app if you want to keep using it.
+
+
+
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_service_closure.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_service_closure.yml
new file mode 100644
index 0000000000..98e1996d72
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_service_closure.yml
@@ -0,0 +1,21 @@
+
+services:
+ service_container:
+ class: Symfony\Component\DependencyInjection\ContainerInterface
+ public: true
+ synthetic: true
+ foo:
+ class: Foo
+ arguments: [!service_closure '@?bar']
+ Psr\Container\ContainerInterface:
+ alias: service_container
+ deprecated:
+ package: symfony/dependency-injection
+ version: 5.1
+ message: The "%alias_id%" autowiring alias is deprecated. Define it explicitly in your app if you want to keep using it.
+ Symfony\Component\DependencyInjection\ContainerInterface:
+ alias: service_container
+ deprecated:
+ package: symfony/dependency-injection
+ version: 5.1
+ message: The "%alias_id%" autowiring alias is deprecated. Define it explicitly in your app if you want to keep using it.
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
index 8714905f71..8a5be20d18 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
@@ -23,6 +23,7 @@ use Symfony\Component\Config\Util\Exception\XmlParsingException;
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
@@ -381,6 +382,15 @@ class XmlFileLoaderTest extends TestCase
$this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('foo_tagged_locator')->getArgument(0));
}
+ public function testParseServiceClosure()
+ {
+ $container = new ContainerBuilder();
+ $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
+ $loader->load('services_with_service_closure.xml');
+
+ $this->assertEquals(new ServiceClosureArgument(new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), $container->getDefinition('foo')->getArgument(0));
+ }
+
public function testParseTagsWithoutNameThrowsException()
{
$this->expectException(InvalidArgumentException::class);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
index 8591b9f128..bbb77e9923 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
@@ -21,6 +21,7 @@ use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Resource\GlobResource;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
@@ -375,6 +376,15 @@ class YamlFileLoaderTest extends TestCase
$this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('bar_service_tagged_locator')->getArgument(0));
}
+ public function testParseServiceClosure()
+ {
+ $container = new ContainerBuilder();
+ $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
+ $loader->load('services_with_service_closure.yml');
+
+ $this->assertEquals(new ServiceClosureArgument(new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), $container->getDefinition('foo')->getArgument(0));
+ }
+
public function testNameOnlyTagsAreAllowedAsString()
{
$container = new ContainerBuilder();