Skip to content

[DependencyInjection] fix dumping service-closure-arguments #41176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,6 @@ private function convertParameters(array $parameters, string $type, \DOMElement
$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');
Expand All @@ -290,8 +287,12 @@ private function convertParameters(array $parameters, string $type, \DOMElement
} 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ private function dumpValue($value)
{
if ($value instanceof ServiceClosureArgument) {
$value = $value->getValues()[0];

return new TaggedValue('service_closure', $this->getServiceCall((string) $value, $value));
}
if ($value instanceof ArgumentInterface) {
$tag = $value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\DependencyInjection\Alias;
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;
Expand Down Expand Up @@ -507,6 +508,13 @@ private function getArgumentsAsPhp(\DOMElement $node, string $name, string $file
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
Expand Down Expand Up @@ -745,6 +746,15 @@ private function resolveServices($value, string $file, bool $isParameter = false
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@
<xsd:enumeration value="constant" />
<xsd:enumeration value="binary" />
<xsd:enumeration value="iterator" />
<xsd:enumeration value="service_closure" />
<xsd:enumeration value="service_locator" />
<!-- "tagged" is an alias of "tagged_iterator", using "tagged_iterator" is preferred. -->
<xsd:enumeration value="tagged" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand Down Expand Up @@ -230,6 +231,17 @@ public function testTaggedArguments()
$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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand Down Expand Up @@ -117,6 +118,17 @@ public function testTaggedArguments()
$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());
}

private function assertEqualYamlStructure(string $expected, string $yaml, string $message = '')
{
$parser = new Parser();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="service_container" class="Symfony\Component\DependencyInjection\ContainerInterface" public="true" synthetic="true"/>
<service id="foo" class="Foo">
<argument type="service_closure" id="bar" on-invalid="ignore"/>
</service>
<service id="Psr\Container\ContainerInterface" alias="service_container" public="false"/>
<service id="Symfony\Component\DependencyInjection\ContainerInterface" alias="service_container" public="false"/>
</services>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

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
public: false
Symfony\Component\DependencyInjection\ContainerInterface:
alias: service_container
public: false
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Symfony\Component\Config\Util\Exception\XmlParsingException;
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;
Expand Down Expand Up @@ -376,6 +377,15 @@ public function testParseTaggedArgumentsWithIndexBy()
$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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand Down Expand Up @@ -364,6 +365,15 @@ public function testTaggedArgumentsWithIndex()
}
}

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();
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/DependencyInjection/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"symfony/service-contracts": "^1.1.6|^2"
},
"require-dev": {
"symfony/yaml": "^3.4|^4.0|^5.0",
"symfony/yaml": "^4.4|^5.0",
"symfony/config": "^4.3",
"symfony/expression-language": "^3.4|^4.0|^5.0"
},
Expand Down