[DI] Fix support for multiple tags for locators and iterators
This commit is contained in:
parent
84d32aca73
commit
6fc91eb192
@ -54,78 +54,81 @@ trait PriorityTaggedServiceTrait
|
||||
|
||||
foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) {
|
||||
$class = $r = null;
|
||||
$priority = 0;
|
||||
if (isset($attributes[0]['priority'])) {
|
||||
$priority = $attributes[0]['priority'];
|
||||
} elseif ($defaultPriorityMethod) {
|
||||
$class = $container->getDefinition($serviceId)->getClass();
|
||||
$class = $container->getParameterBag()->resolveValue($class) ?: null;
|
||||
|
||||
if (($r = $container->getReflectionClass($class)) && $r->hasMethod($defaultPriorityMethod)) {
|
||||
if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
|
||||
}
|
||||
$defaultPriority = null;
|
||||
$defaultIndex = null;
|
||||
|
||||
if (!$rm->isPublic()) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
|
||||
}
|
||||
foreach ($attributes as $attribute) {
|
||||
$index = $priority = null;
|
||||
|
||||
$priority = $rm->invoke(null);
|
||||
if (isset($attribute['priority'])) {
|
||||
$priority = $attribute['priority'];
|
||||
} elseif (null === $defaultPriority && $defaultPriorityMethod) {
|
||||
$class = $container->getDefinition($serviceId)->getClass();
|
||||
$class = $container->getParameterBag()->resolveValue($class) ?: null;
|
||||
|
||||
if (!\is_int($priority)) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer, got %s: tag "%s" on service "%s".', $class, $defaultPriorityMethod, \gettype($priority), $tagName, $serviceId));
|
||||
if (($r = ($r ?? $container->getReflectionClass($class))) && $r->hasMethod($defaultPriorityMethod)) {
|
||||
if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
|
||||
}
|
||||
|
||||
if (!$rm->isPublic()) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
|
||||
}
|
||||
|
||||
$defaultPriority = $rm->invoke(null);
|
||||
|
||||
if (!\is_int($defaultPriority)) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer, got %s: tag "%s" on service "%s".', $class, $defaultPriorityMethod, \gettype($priority), $tagName, $serviceId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $indexAttribute && !$needsIndexes) {
|
||||
$services[$priority][] = new Reference($serviceId);
|
||||
$priority = $priority ?? $defaultPriority ?? 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
if (null !== $indexAttribute && isset($attribute[$indexAttribute])) {
|
||||
$index = $attribute[$indexAttribute];
|
||||
} elseif (null === $defaultIndex && null === $indexAttribute && !$needsIndexes) {
|
||||
// With partially associative array, insertion to get next key is simpler.
|
||||
$services[$priority][] = null;
|
||||
end($services[$priority]);
|
||||
$defaultIndex = key($services[$priority]);
|
||||
} elseif (null === $defaultIndex && $defaultIndexMethod) {
|
||||
$class = $container->getDefinition($serviceId)->getClass();
|
||||
$class = $container->getParameterBag()->resolveValue($class) ?: null;
|
||||
|
||||
if (!$class) {
|
||||
$class = $container->getDefinition($serviceId)->getClass();
|
||||
$class = $container->getParameterBag()->resolveValue($class) ?: null;
|
||||
}
|
||||
if (($r = ($r ?? $container->getReflectionClass($class))) && $r->hasMethod($defaultIndexMethod)) {
|
||||
if (!($rm = $r->getMethod($defaultIndexMethod))->isStatic()) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
|
||||
}
|
||||
|
||||
if (null !== $indexAttribute && isset($attributes[0][$indexAttribute])) {
|
||||
$services[$priority][$attributes[0][$indexAttribute]] = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $attributes[0][$indexAttribute]);
|
||||
if (!$rm->isPublic()) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
$defaultIndex = $rm->invoke(null);
|
||||
|
||||
if (!$r && !$r = $container->getReflectionClass($class)) {
|
||||
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $serviceId));
|
||||
}
|
||||
if (!\is_string($defaultIndex)) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return a string, got %s: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, \gettype($defaultIndex), $tagName, $serviceId, $indexAttribute));
|
||||
}
|
||||
}
|
||||
|
||||
$class = $r->name;
|
||||
|
||||
if (!$r->hasMethod($defaultIndexMethod)) {
|
||||
if ($needsIndexes) {
|
||||
$services[$priority][$serviceId] = new TypedReference($serviceId, $class);
|
||||
|
||||
continue;
|
||||
$defaultIndex = $defaultIndex ?? $serviceId;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" not found: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
|
||||
$index = $index ?? $defaultIndex;
|
||||
|
||||
$reference = null;
|
||||
if (!$class || 'stdClass' === $class) {
|
||||
$reference = new Reference($serviceId);
|
||||
} elseif ($index === $serviceId) {
|
||||
$reference = new TypedReference($serviceId, $class);
|
||||
} else {
|
||||
$reference = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, \is_string($index) ? $index : null);
|
||||
}
|
||||
|
||||
$services[$priority][$index] = $reference;
|
||||
}
|
||||
|
||||
if (!($rm = $r->getMethod($defaultIndexMethod))->isStatic()) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
|
||||
}
|
||||
|
||||
if (!$rm->isPublic()) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
|
||||
}
|
||||
|
||||
$key = $rm->invoke(null);
|
||||
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return a string, got %s: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, \gettype($key), $tagName, $serviceId, $indexAttribute));
|
||||
}
|
||||
|
||||
$services[$priority][$key] = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $key);
|
||||
}
|
||||
|
||||
if ($services) {
|
||||
|
@ -314,6 +314,32 @@ class IntegrationTest extends TestCase
|
||||
$this->assertSame(['bar_tab_class_with_defaultmethod' => $container->get(BarTagClass::class), 'foo' => $container->get(FooTagClass::class)], $param);
|
||||
}
|
||||
|
||||
public function testTaggedIteratorWithMultipleIndexAttribute()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register(BarTagClass::class)
|
||||
->setPublic(true)
|
||||
->addTag('foo_bar', ['foo' => 'bar'])
|
||||
->addTag('foo_bar', ['foo' => 'bar_duplicate'])
|
||||
;
|
||||
$container->register(FooTagClass::class)
|
||||
->setPublic(true)
|
||||
->addTag('foo_bar')
|
||||
->addTag('foo_bar')
|
||||
;
|
||||
$container->register(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), 'bar_duplicate' => $container->get(BarTagClass::class), 'foo_tag_class' => $container->get(FooTagClass::class)], $param);
|
||||
}
|
||||
|
||||
public function testTaggedServiceWithDefaultPriorityMethod()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
@ -350,7 +376,7 @@ class IntegrationTest extends TestCase
|
||||
->addTag('foo_bar')
|
||||
;
|
||||
$container->register('foo_bar_tagged', FooBarTaggedClass::class)
|
||||
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo')))
|
||||
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo', null, true)))
|
||||
->setPublic(true)
|
||||
;
|
||||
|
||||
@ -369,6 +395,40 @@ class IntegrationTest extends TestCase
|
||||
$this->assertSame(['bar' => $container->get('bar_tag'), 'foo_tag_class' => $container->get('foo_tag')], $same);
|
||||
}
|
||||
|
||||
public function testTaggedServiceLocatorWithMultipleIndexAttribute()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('bar_tag', BarTagClass::class)
|
||||
->setPublic(true)
|
||||
->addTag('foo_bar', ['foo' => 'bar'])
|
||||
->addTag('foo_bar', ['foo' => 'bar_duplicate'])
|
||||
;
|
||||
$container->register('foo_tag', FooTagClass::class)
|
||||
->setPublic(true)
|
||||
->addTag('foo_bar')
|
||||
->addTag('foo_bar')
|
||||
;
|
||||
$container->register('foo_bar_tagged', FooBarTaggedClass::class)
|
||||
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo', null, true)))
|
||||
->setPublic(true)
|
||||
;
|
||||
|
||||
$container->compile();
|
||||
|
||||
$s = $container->get('foo_bar_tagged');
|
||||
|
||||
/** @var ServiceLocator $serviceLocator */
|
||||
$serviceLocator = $s->getParam();
|
||||
$this->assertTrue($s->getParam() instanceof ServiceLocator, sprintf('Wrong instance, should be an instance of ServiceLocator, %s given', \is_object($serviceLocator) ? \get_class($serviceLocator) : \gettype($serviceLocator)));
|
||||
|
||||
$same = [
|
||||
'bar' => $serviceLocator->get('bar'),
|
||||
'bar_duplicate' => $serviceLocator->get('bar_duplicate'),
|
||||
'foo_tag_class' => $serviceLocator->get('foo_tag_class'),
|
||||
];
|
||||
$this->assertSame(['bar' => $container->get('bar_tag'), 'bar_duplicate' => $container->get('bar_tag'), 'foo_tag_class' => $container->get('foo_tag')], $same);
|
||||
}
|
||||
|
||||
public function testTaggedServiceLocatorWithIndexAttributeAndDefaultMethod()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
@ -381,7 +441,7 @@ class IntegrationTest extends TestCase
|
||||
->addTag('foo_bar', ['foo' => 'foo'])
|
||||
;
|
||||
$container->register('foo_bar_tagged', FooBarTaggedClass::class)
|
||||
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo', 'getFooBar')))
|
||||
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo', 'getFooBar', true)))
|
||||
->setPublic(true)
|
||||
;
|
||||
|
||||
|
Reference in New Issue
Block a user