diff --git a/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php b/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php new file mode 100644 index 0000000000..19e0d2b971 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php @@ -0,0 +1,37 @@ + + * + * 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 + */ +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; + } +} diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index cea65b20b7..013da9ad4e 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -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 (``) support 3.3.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 2cb261dfbc..6c0448ecb4 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -61,6 +61,7 @@ class PassConfig new AutowireRequiredMethodsPass(), new ResolveBindingsPass(), new AutowirePass(false), + new ResolveTaggedIteratorArgumentPass(), new ResolveServiceSubscribersPass(), new ResolveReferencesToAliasesPass(), new ResolveInvalidReferencesPass(), diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveTaggedIteratorArgumentPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveTaggedIteratorArgumentPass.php new file mode 100644 index 0000000000..009cee9bf5 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveTaggedIteratorArgumentPass.php @@ -0,0 +1,38 @@ + + * + * 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 + */ +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; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index fb307a76e8..cfd7c38413 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Dumper; 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; @@ -298,6 +299,9 @@ class XmlDumper extends Dumper 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'); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 916a2317b3..dc44cba17e 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -19,6 +19,7 @@ use Symfony\Component\DependencyInjection\Alias; 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; @@ -263,6 +264,9 @@ class YamlDumper extends Dumper $value = $value->getValues()[0]; } if ($value instanceof ArgumentInterface) { + if ($value instanceof TaggedIteratorArgument) { + return new TaggedValue('tagged', $value->getTag()); + } if ($value instanceof IteratorArgument) { $tag = 'iterator'; } else { diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php index 132529087a..b44f1e4310 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php @@ -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; @@ -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. * diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index f152d46267..e5a43e391d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -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; @@ -518,6 +519,12 @@ 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 '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; diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index d9fd24c3d7..4088a31529 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Alias; 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; @@ -726,6 +727,13 @@ class YamlFileLoader extends FileLoader 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)); 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 0be8658e3d..3a55a7df67 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 @@ -208,6 +208,7 @@ + @@ -233,6 +234,7 @@ + @@ -258,6 +260,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveTaggedIteratorArgumentPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveTaggedIteratorArgumentPassTest.php new file mode 100644 index 0000000000..a219f86007 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveTaggedIteratorArgumentPassTest.php @@ -0,0 +1,40 @@ + + * + * 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 + */ +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']); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php index bab4e1ec83..b98e894c37 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php @@ -5,9 +5,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\BarService; return function (ContainerConfigurator $c) { - $s = $c->services(); $s->set(BarService::class) ->args(array(inline('FooClass'))); - }; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php index 30178217b6..6fd84485e7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php @@ -5,7 +5,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\BarService; return function (ContainerConfigurator $c) { - $c->services() ->set('bar', 'Class1') ->set(BarService::class) @@ -20,5 +19,4 @@ return function (ContainerConfigurator $c) { ->parent('bar') ->parent(BarService::class) ; - }; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php index 7fa93d2a91..de3b99d745 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php @@ -5,7 +5,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo; return function (ContainerConfigurator $c) { - $c->import('basic.php'); $s = $c->services()->defaults() @@ -19,5 +18,4 @@ return function (ContainerConfigurator $c) { $s->set(Foo::class)->args(array(ref('bar')))->public(); $s->set('bar', Foo::class)->call('setFoo')->autoconfigure(false); - }; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php index 13d0213bf1..062e8c00ab 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php @@ -6,7 +6,6 @@ use App\FooService; use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype; return function (ContainerConfigurator $c) { - $s = $c->services(); $s->instanceof(Prototype\Foo::class) ->property('p', 0) @@ -20,5 +19,4 @@ return function (ContainerConfigurator $c) { $s->load(Prototype::class.'\\', '../Prototype')->exclude('../Prototype/*/*'); $s->set('foo', FooService::class); - }; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php index 22a98c2c57..7711624e6f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php @@ -5,7 +5,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo; return function (ContainerConfigurator $c) { - $c->parameters() ('foo', 'Foo') ('bar', 'Bar') @@ -17,5 +16,4 @@ return function (ContainerConfigurator $c) { ('bar', Foo::class) ->call('setFoo') ; - }; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php index 77bc132dcb..21cab6c6af 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php @@ -5,7 +5,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype; return function (ContainerConfigurator $c) { - $di = $c->services()->defaults() ->tag('baz'); $di->load(Prototype::class.'\\', '../Prototype') @@ -20,5 +19,4 @@ return function (ContainerConfigurator $c) { ->parent('foo'); $di->set('foo')->lazy()->abstract(); $di->get(Prototype\Foo::class)->lazy(false); - }; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php index 85543b6ebb..3f4d5f15ea 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php @@ -9,7 +9,6 @@ require_once __DIR__.'/../includes/classes.php'; require_once __DIR__.'/../includes/foo.php'; return function (ContainerConfigurator $c) { - $p = $c->parameters(); $p->set('baz_class', 'BazClass'); $p->set('foo_class', FooClass::class) @@ -119,4 +118,11 @@ return function (ContainerConfigurator $c) { $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'))); }; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php index 91a9af43d9..452dab6d80 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -4,6 +4,7 @@ require_once __DIR__.'/../includes/classes.php'; 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; @@ -161,5 +162,15 @@ $container ->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; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot index df9a1cae7c..6a34c5735c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot @@ -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"]; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index ec7c57a8df..3075285dcf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -56,6 +56,8 @@ class ProjectServiceContainer extends Container 'new_factory' => 'getNewFactoryService', 'new_factory_service' => 'getNewFactoryServiceService', 'service_from_static_method' => 'getServiceFromStaticMethodService', + 'tagged_iterator' => 'getTaggedIteratorService', + 'tagged_iterator_foo' => 'getTaggedIteratorFooService', ); $this->privates = array( 'configurator_service' => true, @@ -63,6 +65,7 @@ class ProjectServiceContainer extends Container 'factory_simple' => true, 'inlined' => true, 'new_factory' => true, + 'tagged_iterator_foo' => true, ); $this->aliases = array( 'Psr\\Container\\ContainerInterface' => 'service_container', @@ -337,6 +340,18 @@ class ProjectServiceContainer extends Container return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); } + /** + * Gets the public 'tagged_iterator' shared service. + * + * @return \Bar + */ + protected function getTaggedIteratorService() + { + return $this->services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { + return new \EmptyIterator(); + }, 0)); + } + /** * Gets the private 'configurator_service' shared service. * @@ -404,6 +419,16 @@ class ProjectServiceContainer extends Container return $instance; } + /** + * Gets the private 'tagged_iterator_foo' shared service. + * + * @return \Bar + */ + protected function getTaggedIteratorFooService() + { + return $this->services['tagged_iterator_foo'] = new \Bar(); + } + /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index 059f091281..d6c0fc11ed 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -235,6 +235,27 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); + [Container%s/getTaggedIteratorService.php] => services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { + yield 0 => ${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->load(__DIR__.'/getFooService.php')) && false ?: '_'}; + yield 1 => ${($_ = isset($this->services['tagged_iterator_foo']) ? $this->services['tagged_iterator_foo'] : $this->services['tagged_iterator_foo'] = new \Bar()) && false ?: '_'}; +}, 2)); + + [Container%s/getTaggedIteratorFooService.php] => services['tagged_iterator_foo'] = new \Bar(); + [Container%s/Container.php] => __DIR__.'/getMethodCall1Service.php', 'new_factory_service' => __DIR__.'/getNewFactoryServiceService.php', 'service_from_static_method' => __DIR__.'/getServiceFromStaticMethodService.php', + 'tagged_iterator' => __DIR__.'/getTaggedIteratorService.php', + 'tagged_iterator_foo' => __DIR__.'/getTaggedIteratorFooService.php', ); $this->privates = array( 'factory_simple' => true, + 'tagged_iterator_foo' => true, ); $this->aliases = array( 'alias_for_alias' => 'foo', diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 103bb0de34..46e76a4d7f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -49,9 +49,12 @@ class ProjectServiceContainer extends Container 'method_call1' => 'getMethodCall1Service', 'new_factory_service' => 'getNewFactoryServiceService', 'service_from_static_method' => 'getServiceFromStaticMethodService', + 'tagged_iterator' => 'getTaggedIteratorService', + 'tagged_iterator_foo' => 'getTaggedIteratorFooService', ); $this->privates = array( 'factory_simple' => true, + 'tagged_iterator_foo' => true, ); $this->aliases = array( 'alias_for_alias' => 'foo', @@ -339,6 +342,19 @@ class ProjectServiceContainer extends Container return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); } + /** + * Gets the public 'tagged_iterator' shared service. + * + * @return \Bar + */ + protected function getTaggedIteratorService() + { + return $this->services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { + yield 0 => ${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'}; + yield 1 => ${($_ = isset($this->services['tagged_iterator_foo']) ? $this->services['tagged_iterator_foo'] : $this->services['tagged_iterator_foo'] = new \Bar()) && false ?: '_'}; + }, 2)); + } + /** * Gets the private 'factory_simple' shared service. * @@ -353,6 +369,16 @@ class ProjectServiceContainer extends Container return $this->services['factory_simple'] = new \SimpleFactoryClass('foo'); } + /** + * Gets the private 'tagged_iterator_foo' shared service. + * + * @return \Bar + */ + protected function getTaggedIteratorFooService() + { + return $this->services['tagged_iterator_foo'] = new \Bar(); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index 06f314f5ef..d12c00319a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -133,6 +133,12 @@ + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index b6b76da513..cf713d189e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -145,6 +145,16 @@ services: alias_for_alias: alias: 'foo' public: true + tagged_iterator_foo: + class: Bar + tags: + - { name: foo } + public: false + tagged_iterator: + class: Bar + arguments: + - !tagged foo + public: true Psr\Container\ContainerInterface: alias: service_container public: false diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 9f868f2744..088c6c5e4f 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -774,8 +774,8 @@ class Inline $nextOffset += strspn($value, ' ', $nextOffset); // Is followed by a scalar - if (!isset($value[$nextOffset]) || !in_array($value[$nextOffset], array('[', '{'), true)) { - // Manage scalars in {@link self::evaluateScalar()} + if ((!isset($value[$nextOffset]) || !in_array($value[$nextOffset], array('[', '{'), true)) && 'tagged' !== $tag) { + // Manage non-whitelisted scalars in {@link self::evaluateScalar()} return; } diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index af4cb3fbe4..92a99c8521 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -698,6 +698,8 @@ class Parser if ('' !== $matches['tag']) { if ('!!binary' === $matches['tag']) { return Inline::evaluateBinaryScalar($data); + } elseif ('tagged' === $matches['tag']) { + return new TaggedValue(substr($matches['tag'], 1), $data); } elseif ('!' !== $matches['tag']) { @trigger_error($this->getDeprecationMessage(sprintf('Using the custom tag "%s" for the value "%s" is deprecated since version 3.3. It will be replaced by an instance of %s in 4.0.', $matches['tag'], $data, TaggedValue::class)), E_USER_DEPRECATED); }