Skip to content

Commit

Permalink
[DI] change name to tag + add XMl support + adding yaml/xml tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Anthony MARTIN committed Feb 21, 2019
1 parent 020afbc commit 57937e4
Show file tree
Hide file tree
Showing 17 changed files with 219 additions and 6 deletions.
Expand Up @@ -22,6 +22,13 @@ class TaggedIteratorArgument extends IteratorArgument
private $indexAttribute;
private $defaultIndexMethod;

/**
* TaggedIteratorArgument constructor.
*
* @param string $tag The name of the tag identifying the target services
* @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection
* @param string|null $defaultIndexMethod The static method that should be called to get each service's key when their tag doesn't define the previous attribute
*/
public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null)
{
parent::__construct([]);
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Expand Up @@ -9,6 +9,7 @@ CHANGELOG
* added `%env(nullable:...)%` processor to allow empty variables to be processed as null values
* added support for deprecating aliases
* made `ContainerParametersResource` final and not implement `Serializable` anymore
* added ability to define an index for tagged collection

4.2.0
-----
Expand Down
Expand Up @@ -286,6 +286,14 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent
} elseif ($value instanceof TaggedIteratorArgument) {
$element->setAttribute('type', 'tagged');
$element->setAttribute('tag', $value->getTag());

if (null !== $value->getIndexAttribute()) {
$element->setAttribute('index-by', $value->getIndexAttribute());
}

if (null !== $value->getDefaultIndexMethod()) {
$element->setAttribute('default-index-method', $value->getDefaultIndexMethod());
}
} elseif ($value instanceof IteratorArgument) {
$element->setAttribute('type', 'iterator');
$this->convertParameters($value->getValues(), $type, $element, 'key');
Expand Down
13 changes: 13 additions & 0 deletions src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
Expand Up @@ -233,6 +233,19 @@ private function dumpValue($value)
}
if ($value instanceof ArgumentInterface) {
if ($value instanceof TaggedIteratorArgument) {
if (null !== $value->getIndexAttribute()) {
$taggedValueContent = [
'tag' => $value->getTag(),
'index_by' => $value->getIndexAttribute(),
];

if (null !== $value->getDefaultIndexMethod()) {
$taggedValueContent['default_index_method'] = $value->getDefaultIndexMethod();
}

return new TaggedValue('tagged', $taggedValueContent);
}

return new TaggedValue('tagged', $value->getTag());
}
if ($value instanceof IteratorArgument) {
Expand Down
Expand Up @@ -537,7 +537,8 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase =
if (!$arg->getAttribute('tag')) {
throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="tagged" has no or empty "tag" attribute in "%s".', $name, $file));
}
$arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'));

$arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'), $arg->getAttribute('index-by') ?: null, $arg->getAttribute('default-index-method') ?: null);
break;
case 'binary':
if (false === $value = base64_decode($arg->nodeValue)) {
Expand Down
Expand Up @@ -713,14 +713,16 @@ private function resolveServices($value, $file, $isParameter = false)
if (\is_string($argument) && $argument) {
return new TaggedIteratorArgument($argument);
}
if (\is_array($argument) && isset($argument['name']) && $argument['name']) {
if (array_diff(array_keys($argument), ['name', 'index_by', 'default_index_method'])) {
throw new InvalidArgumentException('"!tagged" tag contains unsupported keys. Supported are: "name, index_by, default_index_method".');

if (\is_array($argument) && isset($argument['tag']) && $argument['tag']) {
if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method'])) {
throw new InvalidArgumentException(sprintf('"!tagged" tag contains unsupported key "%s"; supported ones are "tag", "index_by" and "default_index_method".', implode(', ', $diff)));
}

return new TaggedIteratorArgument($argument['name'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null);
return new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null);
}
throw new InvalidArgumentException(sprintf('"!tagged" tag only accepts a non empty string or an array with a key "name" in "%s".', $file));

throw new InvalidArgumentException(sprintf('"!tagged" tags only accept a non empty string or an array with a key "tag" in "%s".', $file));
}
if ('service' === $value->getTag()) {
if ($isParameter) {
Expand Down
Expand Up @@ -234,6 +234,8 @@
<xsd:attribute name="index" type="xsd:integer" />
<xsd:attribute name="on-invalid" type="invalid_sequence" />
<xsd:attribute name="tag" type="xsd:string" />
<xsd:attribute name="index-by" type="xsd:string" />
<xsd:attribute name="default-index-method" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="call">
Expand Down
Expand Up @@ -14,10 +14,14 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarTagClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooTagClass;

/**
* This class tests the integration of the different compiler passes.
Expand Down Expand Up @@ -234,6 +238,54 @@ public function getYamlCompileTests()
$container,
];
}

public function testTaggedServiceWithIndexAttribute()
{
$container = new ContainerBuilder();
$container->register(BarTagClass::class, BarTagClass::class)
->setPublic(true)
->addTag('foo_bar', ['foo' => 'bar'])
;
$container->register(FooTagClass::class, FooTagClass::class)
->setPublic(true)
->addTag('foo_bar')
;
$container->register(FooBarTaggedClass::class, FooBarTaggedClass::class)
->addArgument(new TaggedIteratorArgument('foo_bar', 'foo'))
->setPublic(true)
;

$container->compile();

$s = $container->get(FooBarTaggedClass::class);

$param = iterator_to_array($s->getParam()->getIterator());
$this->assertSame(['bar' => $container->get(BarTagClass::class), 'foo_tag_class' => $container->get(FooTagClass::class)], $param);
}

public function testTaggedServiceWithIndexAttributeAndDefaultMethod()
{
$container = new ContainerBuilder();
$container->register(BarTagClass::class, BarTagClass::class)
->setPublic(true)
->addTag('foo_bar')
;
$container->register(FooTagClass::class, FooTagClass::class)
->setPublic(true)
->addTag('foo_bar', ['foo' => 'foo'])
;
$container->register(FooBarTaggedClass::class, FooBarTaggedClass::class)
->addArgument(new TaggedIteratorArgument('foo_bar', 'foo', 'getFooBar'))
->setPublic(true)
;

$container->compile();

$s = $container->get(FooBarTaggedClass::class);

$param = iterator_to_array($s->getParam()->getIterator());
$this->assertSame(['bar_tab_class_with_defaultmethod' => $container->get(BarTagClass::class), 'foo' => $container->get(FooTagClass::class)], $param);
}
}

class ServiceSubscriberStub implements ServiceSubscriberInterface
Expand Down
Expand Up @@ -13,6 +13,7 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Dumper\XmlDumper;
Expand Down Expand Up @@ -200,6 +201,19 @@ public function testDumpLoad()
$this->assertStringEqualsFile(self::$fixturesPath.'/xml/services_dump_load.xml', $dumper->dump());
}

public function testTaggedArgument()
{
$container = new ContainerBuilder();
$container->register('foo', 'Foo')->addTag('foo_tag');
$container->register('foo_tagged_iterator', 'Bar')
->setPublic(true)
->addArgument(new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar'))
;

$dumper = new XmlDumper($container);
$this->assertStringEqualsFile(self::$fixturesPath.'/xml/services_with_tagged_arguments.xml', $dumper->dump());
}

public function testDumpAbstractServices()
{
$container = include self::$fixturesPath.'/containers/container_abstract.php';
Expand Down
Expand Up @@ -13,6 +13,7 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
Expand Down Expand Up @@ -95,6 +96,16 @@ public function testInlineServices()
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_inline.yml', $dumper->dump());
}

public function testTaggedArgument()
{
$container = new ContainerBuilder();
$container->register('foo_service', 'Foo')->addTag('foo');
$container->register('foo_service_tagged', 'Bar')->addArgument(new TaggedIteratorArgument('foo', 'barfoo', 'foobar'));

$dumper = new YamlDumper($container);
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_with_tagged_argument.yml', $dumper->dump());
}

private function assertEqualYamlStructure($expected, $yaml, $message = '')
{
$parser = new Parser();
Expand Down
@@ -0,0 +1,16 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

class BarTagClass
{
public static function getDefaultFooName()
{
return 'bar_tag_class';
}

public static function getFooBar()
{
return 'bar_tab_class_with_defaultmethod';
}
}
@@ -0,0 +1,18 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

class FooBarTaggedClass
{
private $param;

public function __construct($param = [])
{
$this->param = $param;
}

public function getParam()
{
return $this->param;
}
}
@@ -0,0 +1,11 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

class FooTagClass
{
public static function getDefaultFooName()
{
return 'foo_tag_class';
}
}
@@ -0,0 +1,14 @@
<?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 http://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">
<tag name="foo_tag"/>
</service>
<service id="foo_tagged_iterator" class="Bar" public="true">
<argument type="tagged" tag="foo_tag" index-by="barfoo" default-index-method="foobar"/>
</service>
<service id="Psr\Container\ContainerInterface" alias="service_container" public="false"/>
<service id="Symfony\Component\DependencyInjection\ContainerInterface" alias="service_container" public="false"/>
</services>
</container>
@@ -0,0 +1,19 @@

services:
service_container:
class: Symfony\Component\DependencyInjection\ContainerInterface
public: true
synthetic: true
foo_service:
class: Foo
tags:
- { name: foo }
foo_service_tagged:
class: Bar
arguments: [!tagged { tag: foo, index_by: barfoo, default_index_method: foobar }]
Psr\Container\ContainerInterface:
alias: service_container
public: false
Symfony\Component\DependencyInjection\ContainerInterface:
alias: service_container
public: false
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\Config\Resource\GlobResource;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
Expand Down Expand Up @@ -315,6 +316,17 @@ public function testParsesTags()
}
}

public function testParseTaggedArgumentsWithIndexBy()
{
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('services_with_tagged_arguments.xml');

$this->assertCount(1, $container->getDefinition('foo')->getTag('foo_tag'));
$this->assertCount(1, $container->getDefinition('foo_tagged_iterator')->getArguments());
$this->assertEquals(new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar'), $container->getDefinition('foo_tagged_iterator')->getArgument(0));
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
*/
Expand Down
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\Config\Resource\GlobResource;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
Expand Down Expand Up @@ -279,6 +280,17 @@ public function testTagWithoutNameThrowsException()
}
}

public function testTaggedArgumentsWithIndex()
{
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('services_with_tagged_argument.yml');

$this->assertCount(1, $container->getDefinition('foo_service')->getTag('foo'));
$this->assertCount(1, $container->getDefinition('foo_service_tagged')->getArguments());
$this->assertEquals(new TaggedIteratorArgument('foo', 'barfoo', 'foobar'), $container->getDefinition('foo_service_tagged')->getArgument(0));
}

public function testNameOnlyTagsAreAllowedAsString()
{
$container = new ContainerBuilder();
Expand Down

0 comments on commit 57937e4

Please sign in to comment.