Skip to content

Commit efa0fa9

Browse files
author
Anthony MARTIN
committed
[DI] Allow tagged_locator tag to be used as an argument
Signed-off-by: Anthony MARTIN <anthony.martin@sensiolabs.com>
1 parent dcd1b81 commit efa0fa9

18 files changed

+187
-19
lines changed

Argument/ServiceLocatorArgument.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Argument;
1313

14+
use Symfony\Component\DependencyInjection\Reference;
15+
1416
/**
1517
* Represents a closure acting as a service locator.
1618
*
@@ -19,4 +21,24 @@
1921
class ServiceLocatorArgument implements ArgumentInterface
2022
{
2123
use ReferenceSetArgumentTrait;
24+
25+
private $taggedIteratorArgument;
26+
27+
/**
28+
* @param Reference[]|TaggedIteratorArgument $values
29+
*/
30+
public function __construct($values = [])
31+
{
32+
if ($values instanceof TaggedIteratorArgument) {
33+
$this->taggedIteratorArgument = $values;
34+
$this->values = [];
35+
} else {
36+
$this->setValues($values);
37+
}
38+
}
39+
40+
public function getTaggedIteratorArgument(): ?TaggedIteratorArgument
41+
{
42+
return $this->taggedIteratorArgument;
43+
}
2244
}

Argument/TaggedIteratorArgument.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ class TaggedIteratorArgument extends IteratorArgument
2323
private $defaultIndexMethod;
2424

2525
/**
26-
* TaggedIteratorArgument constructor.
27-
*
2826
* @param string $tag The name of the tag identifying the target services
2927
* @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection
3028
* @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

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* added support for deprecating aliases
1010
* made `ContainerParametersResource` final and not implement `Serializable` anymore
1111
* added ability to define an index for a tagged collection
12+
* added ability to define an index for services in an injected service locator argument
1213

1314
4.2.0
1415
-----

Compiler/ServiceLocatorTagPass.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,18 @@
2727
*/
2828
final class ServiceLocatorTagPass extends AbstractRecursivePass
2929
{
30+
use PriorityTaggedServiceTrait;
31+
3032
protected function processValue($value, $isRoot = false)
3133
{
3234
if ($value instanceof ServiceLocatorArgument) {
35+
if ($value->getTaggedIteratorArgument()) {
36+
$value->setValues($this->findAndSortTaggedServices($value->getTaggedIteratorArgument(), $this->container));
37+
}
38+
3339
return self::register($this->container, $value->getValues());
3440
}
41+
3542
if (!$value instanceof Definition || !$value->hasTag('container.service_locator')) {
3643
return parent::processValue($value, $isRoot);
3744
}

Dumper/XmlDumper.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,18 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent
298298
$element->setAttribute('type', 'iterator');
299299
$this->convertParameters($value->getValues(), $type, $element, 'key');
300300
} elseif ($value instanceof ServiceLocatorArgument) {
301-
$element->setAttribute('type', 'service_locator');
302-
$this->convertParameters($value->getValues(), $type, $element, 'key');
301+
if ($value->getTaggedIteratorArgument()) {
302+
$element->setAttribute('type', 'tagged_locator');
303+
$element->setAttribute('tag', $value->getTaggedIteratorArgument()->getTag());
304+
$element->setAttribute('index-by', $value->getTaggedIteratorArgument()->getIndexAttribute());
305+
306+
if (null !== $value->getTaggedIteratorArgument()->getDefaultIndexMethod()) {
307+
$element->setAttribute('default-index-method', $value->getTaggedIteratorArgument()->getDefaultIndexMethod());
308+
}
309+
} else {
310+
$element->setAttribute('type', 'service_locator');
311+
$this->convertParameters($value->getValues(), $type, $element, 'key');
312+
}
303313
} elseif ($value instanceof Reference) {
304314
$element->setAttribute('type', 'service');
305315
$element->setAttribute('id', (string) $value);

Dumper/YamlDumper.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,18 @@ private function dumpValue($value)
252252
$tag = 'iterator';
253253
} elseif ($value instanceof ServiceLocatorArgument) {
254254
$tag = 'service_locator';
255+
if ($value->getTaggedIteratorArgument()) {
256+
$taggedValueContent = [
257+
'tag' => $value->getTaggedIteratorArgument()->getTag(),
258+
'index_by' => $value->getTaggedIteratorArgument()->getIndexAttribute(),
259+
];
260+
261+
if (null !== $value->getTaggedIteratorArgument()->getDefaultIndexMethod()) {
262+
$taggedValueContent['default_index_method'] = $value->getTaggedIteratorArgument()->getDefaultIndexMethod();
263+
}
264+
265+
return new TaggedValue('tagged_locator', $taggedValueContent);
266+
}
255267
} else {
256268
throw new RuntimeException(sprintf('Unspecified Yaml tag for type "%s".', \get_class($value)));
257269
}

Loader/Configurator/ContainerConfigurator.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ function tagged(string $tag, string $indexAttribute = null, string $defaultIndex
121121
return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod);
122122
}
123123

124+
/**
125+
* Creates a service locator by tag name.
126+
*/
127+
function tagged_locator(string $tag, string $indexAttribute, string $defaultIndexMethod = null): ServiceLocatorArgument
128+
{
129+
return new ServiceLocatorArgument(new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod));
130+
}
131+
124132
/**
125133
* Creates an expression.
126134
*/

Loader/XmlFileLoader.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,13 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase =
533533
throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service_locator" only accepts maps of type="service" references in "%s".', $name, $file));
534534
}
535535
break;
536+
case 'tagged_locator':
537+
if (!$arg->getAttribute('tag') || !$arg->getAttribute('index-by')) {
538+
throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="tagged_locator" has no or empty "tag" or "index-by" attributes in "%s".', $name, $file));
539+
}
540+
541+
$arguments[$key] = new ServiceLocatorArgument(new TaggedIteratorArgument($arg->getAttribute('tag'), $arg->getAttribute('index-by'), $arg->getAttribute('default-index-method') ?: null));
542+
break;
536543
case 'tagged':
537544
if (!$arg->getAttribute('tag')) {
538545
throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="tagged" has no or empty "tag" attribute in "%s".', $name, $file));

Loader/YamlFileLoader.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,9 @@ private function resolveServices($value, $file, $isParameter = false)
702702
if (!\is_array($argument)) {
703703
throw new InvalidArgumentException(sprintf('"!service_locator" tag only accepts maps in "%s".', $file));
704704
}
705+
705706
$argument = $this->resolveServices($argument, $file, $isParameter);
707+
706708
try {
707709
return new ServiceLocatorArgument($argument);
708710
} catch (InvalidArgumentException $e) {
@@ -724,6 +726,17 @@ private function resolveServices($value, $file, $isParameter = false)
724726

725727
throw new InvalidArgumentException(sprintf('"!tagged" tags only accept a non empty string or an array with a key "tag" in "%s".', $file));
726728
}
729+
if ('tagged_locator' === $value->getTag()) {
730+
if (\is_array($argument) && isset($argument['tag'], $argument['index_by']) && $argument['tag'] && $argument['index_by']) {
731+
if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method'])) {
732+
throw new InvalidArgumentException(sprintf('"!tagged_locator" tag contains unsupported key "%s"; supported ones are "tag", "index_by" and "default_index_method".', implode('"", "', $diff)));
733+
}
734+
735+
return new ServiceLocatorArgument(new TaggedIteratorArgument($argument['tag'], $argument['index_by'], $argument['default_index_method'] ?? null));
736+
}
737+
738+
throw new InvalidArgumentException(sprintf('"!tagged_locator" tags only accept an array with at least keys "tag" and "index_by" in "%s".', $file));
739+
}
727740
if ('service' === $value->getTag()) {
728741
if ($isParameter) {
729742
throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file));

Loader/schema/dic/services/services-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@
264264
<xsd:enumeration value="iterator" />
265265
<xsd:enumeration value="service_locator" />
266266
<xsd:enumeration value="tagged" />
267+
<xsd:enumeration value="tagged_locator" />
267268
</xsd:restriction>
268269
</xsd:simpleType>
269270

0 commit comments

Comments
 (0)