8000 [DI] Reference tagged services in config by ro0NL · Pull Request #22200 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[DI] Reference tagged services in config #22200

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
Sep 28, 2017
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
@@ -0,0 +1,37 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Argument;

/**
* Represents a collection of services found by tag name to lazily iterate over.
*
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class TaggedIteratorArgument extends IteratorArgument
{
private $tag;

/**
* @param string $tag
*/
public function __construct($tag)
{
parent::__construct(array());

$this->tag = (string) $tag;
}

public function getTag()
{
return $this->tag;
}
}
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ CHANGELOG
* deprecated support for top-level anonymous services in XML
* deprecated case insensitivity of parameter names
* deprecated the `ResolveDefinitionTemplatesPass` class in favor of `ResolveChildDefinitionsPass`
* added `TaggedIteratorArgument` with YAML (`!tagged foo`) and XML (`<service type="tagged"/>`) support

3.3.0
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public function __construct()
new AutowireRequiredMethodsPass(),
new ResolveBindingsPass(),
new AutowirePass(false),
new ResolveTaggedIteratorArgumentPass(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be before the DecoratorServicePass, so that tagging a service and then decorating it creates the reference with the original id (and so gets the decorated service rather than the inner one). It should be at the same place than the ServiceLocatorTagPass (which is here for the same reason)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stof debugged this real quick, and AFAIK for a decorated service we already inherit tags via ResolveChildDefinitionsPass, which runs sooner.

Thus ContainerBuilder::findTaggedServiceIds() returns expected service id's already. Am i missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

child definitions are not about decorated services. Child definitions are about service definition inheritance (which is precisely why we renamed DecoratorDefinition to ChildDefinition as the previous name was totally misleading, especially after we implemented service decoration)

new ResolveServiceSubscribersPass(),
new ResolveReferencesToAliasesPass(),
new ResolveInvalidReferencesPass(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;

/**
* Resolves all TaggedIteratorArgument arguments.
*
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class ResolveTaggedIteratorArgumentPass extends AbstractRecursivePass
{
use PriorityTaggedServiceTrait;

/**
* {@inheritdoc}
*/
protected function processValue($value, $isRoot = false)
{
if (!$value instanceof TaggedIteratorArgument) {
return parent::processValue($value, $isRoot);
}

$value->setValues($this->findAndSortTaggedServices($value->getTag(), $this->container));

return $value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
Expand Down Expand Up @@ -298,6 +299,9 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent
if (is_array($value)) {
$element->setAttribute('type', 'collection');
$this->convertParameters($value, $type, $element, 'key');
} elseif ($value instanceof TaggedIteratorArgument) {
$element->setAttribute('type', 'tagged');
$element->setAttribute('tag', $value->getTag());
} elseif ($value instanceof IteratorArgument) {
$element->setAttribute('type', 'iterator');
$this->convertParameters($value->getValues(), $type, $element, 'key');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Parameter;
Expand Down Expand Up @@ -263,6 +264,9 @@ private function dumpValue($value)
$value = $value->getValues()[0];
}
if ($val 8000 ue instanceof ArgumentInterface) {
if ($value instanceof TaggedIteratorArgument) {
return new TaggedValue('tagged', $value->getTag());
}
if ($value instanceof IteratorArgument) {
$tag = 'iterator';
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
Expand Down Expand Up @@ -115,6 +116,18 @@ function iterator(array $values)
return new IteratorArgument(AbstractConfigurator::processValue($values, true));
}

/**
* Creates a lazy iterator by tag name.
*
* @param string $tag
*
* @return TaggedIteratorArgument
*/
function tagged($tag)
{
return new TaggedIteratorArgument($tag);
}

/**
* Creates an expression.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Loader;

use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
Expand Down Expand Up @@ -518,6 +519,12 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase =
throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".', $name, $file));
}
break;
case 'tagged':
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'));
break;
case 'string':
$arguments[$key] = $arg->nodeValue;
break;
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\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
Expand Down Expand Up @@ -726,6 +727,13 @@ private function resolveServices($value, $file, $isParameter = false)
throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file));
}
}
if ('tagged' === $value->getTag()) {
if (!is_string($argument) || !$argument) {
throw new InvalidArgumentException(sprintf('"!tagged" tag only accepts non empty string in "%s".', $file));
}

return new TaggedIteratorArgument($argument);
}
if ('service' === $value->getTag()) {
if ($isParameter) {
throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="on-invalid" type="invalid_sequence" />
<xsd:attribute name="strict" type="boolean" />
<xsd:attribute name="tag" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="bind" mixed="true">
Expand All @@ -233,6 +234,7 @@
<xsd:attribute name="index" type="xsd:integer" />
<xsd:attribute name="on-invalid" type="invalid_sequence" />
<xsd:attribute name="strict" type="boolean" />
<xsd:attribute name="tag" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="call">
Expand All @@ -258,6 +260,7 @@
<xsd:enumeration value="string" />
<xsd:enumeration value="constant" />
<xsd:enumeration value="iterator" />
<xsd:enumeration value="tagged" />
</xsd:restriction>
</xsd:simpleType>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Compiler;

use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class ResolveTaggedIteratorArgumentPassTest extends TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container->register('a', 'stdClass')->addTag('foo');
$container->register('b', 'stdClass')->addTag('foo', array('priority' => 20));
$container->register('c', 'stdClass')->addTag('foo', array('priority' => 10));
$container->register('d', 'stdClass')->setProperty('foos', new TaggedIteratorArgument('foo'));

(new ResolveTaggedIteratorArgumentPass())->process($container);

$properties = $container->getDefinition('d')->getProperties();
$expected = new TaggedIteratorArgument('foo');
$expected->setValues(array(new Reference('b'), new Reference('c'), new Reference('a')));
$this->assertEquals($expected, $properties['foos']);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
use App\BarService;

return function (ContainerConfigurator $c) {

$s = $c->services();
$s->set(BarService::class)
->args(array(inline('FooClass')));

};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use App\BarService;

return function (ContainerConfigurator $c) {

$c->services()
->set('bar', 'Class1')
->set(BarService::class)
Expand All @@ -20,5 +19,4 @@
->parent('bar')
->parent(BarService::class)
;

};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo;

return function (ContainerConfigurator $c) {

$c->import('basic.php');

$s = $c->services()->defaults()
Expand All @@ -19,5 +18,4 @@

$s->set(Foo::class)->args(array(ref('bar')))->public();
$s->set('bar', Foo::class)->call('setFoo')->autoconfigure(false);

};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;

return function (ContainerConfigurator $c) {

$s = $c->services();
$s->instanceof(Prototype\Foo::class)
->property('p', 0)
Expand All @@ -20,5 +19,4 @@
$s->load(Prototype::class.'\\', '../Prototype')->exclude('../Prototype/*/*');

$s->set('foo', FooService::class);

};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo;

return function (ContainerConfigurator $c) {

$c->parameters()
('foo', 'Foo')
('bar', 'Bar')
Expand All @@ -17,5 +16,4 @@
('bar', Foo::class)
->call('setFoo')
;

};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;

return function (ContainerConfigurator $c) {

$di = $c->services()->defaults()
->tag('baz');
$di->load(Prototype::class.'\\', '../Prototype')
Expand All @@ -20,5 +19,4 @@
->parent('foo');
$di->set('foo')->lazy()->abstract();
$di->get(Prototype\Foo::class)->lazy(false);

};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
require_once __DIR__.'/../includes/foo.php';

return function (ContainerConfigurator $c) {

$p = $c->parameters();
$p->set('baz_class', 'BazClass');
$p->set('foo_class', FooClass::class)
Expand Down Expand Up @@ -119,4 +118,11 @@
$s->set('lazy_context_ignore_invalid_ref', 'LazyContext')
->args(array(iterator(array(ref('foo.baz'), ref('invalid')->ignoreOnInvalid())), iterator(array())));

$s->set('tagged_iterator_foo', 'Bar')
->private()
->tag('foo');

$s->set('tagged_iterator', 'Bar')
->public()
->args(array(tagged('foo')));
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require_once __DIR__.'/../includes/foo.php';

use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
Expand Down Expand Up @@ -161,5 +162,15 @@
->setArguments(array(new IteratorArgument(array(new Reference('foo.baz'), new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))), new IteratorArgument(array())))
->setPublic(true)
;
$container
->register('tagged_iterator_foo', 'Bar')
->addTag('foo')
->setPublic(false)
;
$container
->register('tagged_iterator', 'Bar')
->addArgument(new TaggedIteratorArgument('foo'))
->setPublic(true)
;

return $container;
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ digraph sc {
node_factory_service_simple [label="factory_service_simple\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_lazy_context [label="lazy_context\nLazyContext\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_lazy_context_ignore_invalid_ref [label="lazy_context_ignore_invalid_ref\nLazyContext\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_tagged_iterator_foo [label="tagged_iterator_foo\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_tagged_iterator [label="tagged_iterator\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foobaz [label="foobaz\n\n", shape=record, fillcolor="#ff9999", style="filled"];
Expand Down
Loading
0