diff --git a/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php b/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php index 06513fd903e01..e8423c8d74eb5 100644 --- a/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php +++ b/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php @@ -20,16 +20,17 @@ class Autoconfigure { /** - * @param array>|string[]|null $tags The tags to add to the service - * @param array>|null $calls The calls to be made when instantiating the service - * @param array|null $bind The bindings to declare for the service - * @param bool|string|null $lazy Whether the service is lazy-loaded - * @param bool|null $public Whether to declare the service as public - * @param bool|null $shared Whether to declare the service as shared - * @param bool|null $autowire Whether to declare the service as autowired - * @param array|null $properties The properties to define when creating the service - * @param array{string, string}|string|null $configurator A PHP function, reference or an array containing a class/reference and a method to call after the service is fully initialized - * @param string|null $constructor The public static method to use to instantiate the service + * @param array>|string[]|null $tags The tags to add to the service + * @param array>|null $calls The calls to be made when instantiating the service + * @param array|null $bind The bindings to declare for the service + * @param bool|string|null $lazy Whether the service is lazy-loaded + * @param bool|null $public Whether to declare the service as public + * @param bool|null $shared Whether to declare the service as shared + * @param bool|null $autowire Whether to declare the service as autowired + * @param array|null $properties The properties to define when creating the service + * @param array{string, string}|string|null $configurator A PHP function, reference or an array containing a class/reference and a method to call after the service is fully initialized + * @param string|null $constructor The public static method to use to instantiate the service + * @param array>|string[]|null $resourceTags The resource tags to add to the service */ public function __construct( public ?array $tags = null, @@ -42,6 +43,7 @@ public function __construct( public ?array $properties = null, public array|string|null $configurator = null, public ?string $constructor = null, + public ?array $resourceTags = null, ) { } } diff --git a/src/Symfony/Component/DependencyInjection/Attribute/AutoconfigureResourceTag.php b/src/Symfony/Component/DependencyInjection/Attribute/AutoconfigureResourceTag.php new file mode 100644 index 0000000000000..87c2eea12d5ce --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Attribute/AutoconfigureResourceTag.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Attribute; + +/** + * @author Nicolas Grekas + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] +class AutoconfigureResourceTag extends Autoconfigure +{ + /** + * @param string|null $name The resource tag name to add + * @param array $attributes The attributes to attach to the resource tag + */ + public function __construct(?string $name = null, array $attributes = []) + { + parent::__construct( + resourceTags: [ + [$name ?? 0 => $attributes], + ] + ); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Attribute/AutoconfigureTag.php b/src/Symfony/Component/DependencyInjection/Attribute/AutoconfigureTag.php index dab5595618e28..29dbc5a8efb64 100644 --- a/src/Symfony/Component/DependencyInjection/Attribute/AutoconfigureTag.php +++ b/src/Symfony/Component/DependencyInjection/Attribute/AutoconfigureTag.php @@ -20,8 +20,8 @@ class AutoconfigureTag extends Autoconfigure { /** - * @param string|null $name The tag name to add - * @param array $attributes The tag attributes to attach to the tag + * @param string|null $name The tag name to add + * @param array $attributes The attributes to attach to the tag */ public function __construct(?string $name = null, array $attributes = []) { diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 6fa6bf4f77760..7212720f826af 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.4 --- + * Allow adding resource tags using any config format * Allow `#[AsAlias]` to be extended * Add argument `$target` to `ContainerBuilder::registerAliasForArgument()` * Deprecate registering a service without a class when its id is a non-existing FQCN diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RegisterAutoconfigureAttributesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RegisterAutoconfigureAttributesPass.php index ec40eee2d3872..e8a2c077cd23f 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RegisterAutoconfigureAttributesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RegisterAutoconfigureAttributesPass.php @@ -72,11 +72,17 @@ private static function registerForAutoconfiguration(ContainerBuilder $container self::$registerForAutoconfiguration = static function (ContainerBuilder $container, \ReflectionClass $class, \ReflectionAttribute $attribute) use ($parseDefinitions, $yamlLoader) { $attribute = (array) $attribute->newInstance(); - foreach ($attribute['tags'] ?? [] as $i => $tag) { - if (\is_array($tag) && [0] === array_keys($tag)) { - $attribute['tags'][$i] = [$class->name => $tag[0]]; + foreach (['tags', 'resourceTags'] as $type) { + foreach ($attribute[$type] ?? [] as $i => $tag) { + if (\is_array($tag) && [0] === array_keys($tag)) { + $attribute[$type][$i] = [$class->name => $tag[0]]; + } } } + if (isset($attribute['resourceTags'])) { + $attribute['resource_tags'] = $attribute['resourceTags']; + } + unset($attribute['resourceTags']); $parseDefinitions->invoke( $yamlLoader, diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php index 29372b0fc8c72..7582ab5daa926 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php @@ -54,6 +54,26 @@ final public function tag(string $name, array $attributes = []): static return $this; } + /** + * Adds a resource tag for this definition. + * + * @return $this + * + * @throws InvalidArgumentException when an invalid tag name or attribute is provided + */ + final public function resourceTag(string $name, array $attributes = []): static + { + if ('' === $name) { + throw new InvalidArgumentException('The resource tag name in "_defaults" must be a non-empty string.'); + } + + $this->validateAttributes($name, $attributes); + + $this->definition->addResourceTag($name, $attributes); + + return $this; + } + /** * Defines an instanceof-conditional to be applied to following service definitions. */ diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php index 4ee120adcbeae..20ebda660cb65 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php @@ -33,6 +33,24 @@ final public function tag(string $name, array $attributes = []): static return $this; } + /** + * Adds a resource tag for this definition. + * + * @return $this + */ + final public function resourceTag(string $name, array $attributes = []): static + { + if ('' === $name) { + throw new InvalidArgumentException(\sprintf('The resource tag name for service "%s" must be a non-empty string.', $this->id)); + } + + $this->validateAttributes($name, $attributes); + + $this->definition->addResourceTag($name, $attributes); + + return $this; + } + private function validateAttributes(string $tag, array $attributes, array $path = []): void { foreach ($attributes as $name => $value) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 1b3f73b849982..83ec4e53747bd 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -346,28 +346,31 @@ private function parseDefinition(\DOMElement $service, string $file, Definition ); } - $tags = $this->getChildren($service, 'tag'); + foreach (['tag', 'resource-tag'] as $type) { + foreach ($this->getChildren($service, $type) as $tag) { + $tagNameComesFromAttribute = $tag->childElementCount || '' === $tag->nodeValue; + if ('' === $tagName = $tagNameComesFromAttribute ? $tag->getAttribute('name') : $tag->nodeValue) { + throw new InvalidArgumentException(\sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $service->getAttribute('id'), $file)); + } - foreach ($tags as $tag) { - $tagNameComesFromAttribute = $tag->childElementCount || '' === $tag->nodeValue; - if ('' === $tagName = $tagNameComesFromAttribute ? $tag->getAttribute('name') : $tag->nodeValue) { - throw new InvalidArgumentException(\sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $service->getAttribute('id'), $file)); - } + $parameters = $this->getTagAttributes($tag, \sprintf('The attribute name of tag "%s" for service "%s" in %s must be a non-empty string.', $tagName, $service->getAttribute('id'), $file)); + foreach ($tag->attributes as $name => $node) { + if ($tagNameComesFromAttribute && 'name' === $name) { + continue; + } - $parameters = $this->getTagAttributes($tag, \sprintf('The attribute name of tag "%s" for service "%s" in %s must be a non-empty string.', $tagName, $service->getAttribute('id'), $file)); - foreach ($tag->attributes as $name => $node) { - if ($tagNameComesFromAttribute && 'name' === $name) { - continue; + if (str_contains($name, '-') && !str_contains($name, '_') && !\array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { + $parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue); + } + // keep not normalized key + $parameters[$name] = XmlUtils::phpize($node->nodeValue); } - if (str_contains($name, '-') && !str_contains($name, '_') && !\array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { - $parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue); - } - // keep not normalized key - $parameters[$name] = XmlUtils::phpize($node->nodeValue); + match ($type) { + 'tag' => $definition->addTag($tagName, $parameters), + 'resource-tag' => $definition->addResourceTag($tagName, $parameters), + }; } - - $definition->addTag($tagName, $parameters); } $definition->setTags(array_merge_recursive($definition->getTags(), $defaults->getTags())); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 8d2b677fae8e9..51f449a23b7dc 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -57,6 +57,7 @@ class YamlFileLoader extends FileLoader 'configurator' => 'configurator', 'calls' => 'calls', 'tags' => 'tags', + 'resource_tags' => 'resource_tags', 'decorates' => 'decorates', 'decoration_inner_name' => 'decoration_inner_name', 'decoration_priority' => 'decoration_priority', @@ -83,6 +84,7 @@ class YamlFileLoader extends FileLoader 'configurator' => 'configurator', 'calls' => 'calls', 'tags' => 'tags', + 'resource_tags' => 'resource_tags', 'autowire' => 'autowire', 'autoconfigure' => 'autoconfigure', 'bind' => 'bind', @@ -97,6 +99,7 @@ class YamlFileLoader extends FileLoader 'configurator' => 'configurator', 'calls' => 'calls', 'tags' => 'tags', + 'resource_tags' => 'resource_tags', 'autowire' => 'autowire', 'bind' => 'bind', 'constructor' => 'constructor', @@ -105,6 +108,7 @@ class YamlFileLoader extends FileLoader private const DEFAULTS_KEYWORDS = [ 'public' => 'public', 'tags' => 'tags', + 'resource_tags' => 'resource_tags', 'autowire' => 'autowire', 'autoconfigure' => 'autoconfigure', 'bind' => 'bind', @@ -281,9 +285,13 @@ private function parseDefaults(array &$content, string $file): array } } - if (isset($defaults['tags'])) { - if (!\is_array($tags = $defaults['tags'])) { - throw new InvalidArgumentException(\sprintf('Parameter "tags" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file)); + foreach (['tags', 'resource_tags'] as $type) { + if (!isset($defaults[$type])) { + continue; + } + + if (!\is_array($tags = $defaults[$type])) { + throw new InvalidArgumentException(\sprintf('Parameter "%s" in "_defaults" must be an array in "%s". Check your YAML syntax.', $type, $file)); } foreach ($tags as $tag) { @@ -296,7 +304,7 @@ private function parseDefaults(array &$content, string $file): array $tag = current($tag); } else { if (!isset($tag['name'])) { - throw new InvalidArgumentException(\sprintf('A "tags" entry in "_defaults" is missing a "name" key in "%s".', $file)); + throw new InvalidArgumentException(\sprintf('A "%s" entry in "_defaults" is missing a "name" key in "%s".', $type, $file)); } $name = $tag['name']; unset($tag['name']); @@ -602,38 +610,43 @@ private function parseDefinition(string $id, array|string|null $service, string } } - $tags = $service['tags'] ?? []; - if (!\is_array($tags)) { - throw new InvalidArgumentException(\sprintf('Parameter "tags" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file)); - } - - if (isset($defaults['tags'])) { - $tags = array_merge($tags, $defaults['tags']); - } + foreach (['tags', 'resource_tags'] as $type) { + $tags = $service[$type] ?? []; + if (!\is_array($tags)) { + throw new InvalidArgumentException(\sprintf('Parameter "%s" must be an array for service "%s" in "%s". Check your YAML syntax.', $type, $id, $file)); + } - foreach ($tags as $tag) { - if (!\is_array($tag)) { - $tag = ['name' => $tag]; + if (isset($defaults[$type])) { + $tags = array_merge($tags, $defaults[$type]); } - if (1 === \count($tag) && \is_array(current($tag))) { - $name = key($tag); - $tag = current($tag); - } else { - if (!isset($tag['name'])) { - throw new InvalidArgumentException(\sprintf('A "tags" entry is missing a "name" key for service "%s" in "%s".', $id, $file)); + foreach ($tags as $tag) { + if (!\is_array($tag)) { + $tag = ['name' => $tag]; } - $name = $tag['name']; - unset($tag['name']); - } - if (!\is_string($name) || '' === $name) { - throw new InvalidArgumentException(\sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file)); - } + if (1 === \count($tag) && \is_array(current($tag))) { + $name = key($tag); + $tag = current($tag); + } else { + if (!isset($tag['name'])) { + throw new InvalidArgumentException(\sprintf('A "%s" entry is missing a "name" key for service "%s" in "%s".', $type, $id, $file)); + } + $name = $tag['name']; + unset($tag['name']); + } + + if (!\is_string($name) || '' === $name) { + throw new InvalidArgumentException(\sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file)); + } - $this->validateAttributes(\sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, '%s', $file), $tag); + $this->validateAttributes(\sprintf('A "%s" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, '%s', $type, $file), $tag); - $definition->addTag($name, $tag); + match ($type) { + 'tags' => $definition->addTag($name, $tag), + 'resource_tags' => $definition->addResourceTag($name, $tag), + }; + } } if (null !== $decorates = $service['decorates'] ?? null) { 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 befdb658f38ef..3d88b5802188b 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 @@ -151,6 +151,7 @@ + @@ -197,6 +198,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/services.schema.json b/src/Symfony/Component/DependencyInjection/Loader/schema/services.schema.json index b58d8c37d349f..07952956e83e6 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/services.schema.json +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/services.schema.json @@ -249,6 +249,7 @@ "properties": { "type": "object", "additionalProperties": { "$ref": "#/$defs/parameterValue" } }, "calls": { "$ref": "#/$defs/calls" }, "tags": { "$ref": "#/$defs/tags" }, + "resource_tags": { "$ref": "#/$defs/tags" }, "decorates": { "type": "string" }, "decoration_inner_name": { "type": "string" }, "decoration_priority": { "type": "integer" }, @@ -285,6 +286,7 @@ "configurator": { "$ref": "#/$defs/callable" }, "calls": { "$ref": "#/$defs/calls" }, "tags": { "$ref": "#/$defs/tags" }, + "resource_tags": { "$ref": "#/$defs/tags" }, "autowire": { "type": "boolean" }, "autoconfigure": { "type": "boolean" }, "bind": { "type": "object", "additionalProperties": { "$ref": "#/$defs/parameterValue" } }, @@ -322,6 +324,7 @@ "properties": { "public": { "type": "boolean" }, "tags": { "$ref": "#/$defs/tags" }, + "resource_tags": { "$ref": "#/$defs/tags" }, "autowire": { "type": "boolean" }, "autoconfigure": { "type": "boolean" }, "bind": { @@ -342,6 +345,7 @@ "configurator": { "$ref": "#/$defs/callable" }, "calls": { "$ref": "#/$defs/calls" }, "tags": { "$ref": "#/$defs/tags" }, + "resource_tags": { "$ref": "#/$defs/tags" }, "autowire": { "type": "boolean" }, "bind": { "type": "object", "additionalProperties": { "$ref": "#/$defs/parameterValue" } }, "constructor": { "type": "string" } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterAutoconfigureAttributesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterAutoconfigureAttributesPassTest.php index 931a4f5c4405b..609edd89f9abe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterAutoconfigureAttributesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterAutoconfigureAttributesPassTest.php @@ -26,6 +26,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedOverwrite; use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedProperties; use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedTag; +use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureResourceTagsAttributed; use Symfony\Component\DependencyInjection\Tests\Fixtures\LazyAutoconfigured; use Symfony\Component\DependencyInjection\Tests\Fixtures\LazyLoaded; use Symfony\Component\DependencyInjection\Tests\Fixtures\MultipleAutoconfigureAttributed; @@ -249,4 +250,20 @@ public function testMultipleAutoconfigureAllowed() ; $this->assertEquals([MultipleAutoconfigureAttributed::class => $expected], $container->getAutoconfiguredInstanceof()); } + + public function testAutoconfiguredResourceTags() + { + $container = new ContainerBuilder(); + $container->register('foo', AutoconfigureResourceTagsAttributed::class) + ->setAutoconfigured(true); + + (new RegisterAutoconfigureAttributesPass())->process($container); + + $expected = (new ChildDefinition('')) + ->addResourceTag('my.tag', ['foo' => 'bar']) + ->addResourceTag('another.tag') + ; + + $this->assertEquals([AutoconfigureResourceTagsAttributed::class => $expected], $container->getAutoconfiguredInstanceof()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureResourceTagsAttributed.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureResourceTagsAttributed.php new file mode 100644 index 0000000000000..6aba60dea7d46 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureResourceTagsAttributed.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Fixtures; + +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureResourceTag; + +#[AutoconfigureResourceTag('my.tag', ['foo' => 'bar'])] +#[AutoconfigureResourceTag('another.tag')] +class AutoconfigureResourceTagsAttributed +{ +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/resource_tags.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/resource_tags.php new file mode 100644 index 0000000000000..8de6b1c9bfabd --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/resource_tags.php @@ -0,0 +1,12 @@ +services(); + + $services->set('foo', stdClass::class) + ->resourceTag('my.tag', ['foo' => 'bar']) + ->resourceTag('another.tag') + ; +}; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_resource_tags.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_resource_tags.xml new file mode 100644 index 0000000000000..49b977277f30f --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_resource_tags.xml @@ -0,0 +1,11 @@ + + + + + + another.tag + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_resource_tags.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_resource_tags.yml new file mode 100644 index 0000000000000..adb5425fa58bc --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_resource_tags.yml @@ -0,0 +1,6 @@ +services: + foo: + class: stdClass + resource_tags: + - { name: 'my.tag', foo: 'bar' } + - 'another.tag' diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php index c54f31f779b47..d62d54be97d07 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php @@ -144,6 +144,21 @@ public static function provideConfig() yield ['env_param']; } + public function testResourceTags() + { + $fixtures = realpath(__DIR__.'/../Fixtures'); + $loader = new PhpFileLoader($container = new ContainerBuilder(), new FileLocator()); + $loader->load($fixtures.'/config/resource_tags.php'); + + $def = $container->getDefinition('foo'); + $this->assertTrue($def->hasTag('container.excluded')); + $this->assertTrue($def->hasTag('my.tag')); + $this->assertTrue($def->hasTag('another.tag')); + $this->assertSame([['foo' => 'bar']], $def->getTag('my.tag')); + $this->assertSame([[]], $def->getTag('another.tag')); + $this->assertTrue($def->isAbstract()); + } + public function testAutoConfigureAndChildDefinition() { $fixtures = realpath(__DIR__.'/../Fixtures'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 0079ccfcfc7cd..feb200292a757 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -369,6 +369,21 @@ public function testLoadServices() $this->assertEquals(['decorated', 'decorated.pif-pouf', 5, ContainerInterface::IGNORE_ON_INVALID_REFERENCE], $services['decorator_service_with_name_and_priority_and_on_invalid']->getDecoratedService()); } + public function testResourceTags() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_resource_tags.xml'); + + $def = $container->getDefinition('foo'); + $this->assertTrue($def->hasTag('container.excluded')); + $this->assertTrue($def->hasTag('my.tag')); + $this->assertTrue($def->hasTag('another.tag')); + $this->assertSame([['foo' => 'bar']], $def->getTag('my.tag')); + $this->assertSame([[]], $def->getTag('another.tag')); + $this->assertTrue($def->isAbstract()); + } + public function testParsesIteratorArgument() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index bf142e59fa234..f36b8645df8fe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -247,6 +247,21 @@ public function testLoadServices() $this->assertEquals(['decorated', 'decorated.pif-pouf', 5, ContainerInterface::IGNORE_ON_INVALID_REFERENCE], $services['decorator_service_with_name_and_priority_and_on_invalid']->getDecoratedService()); } + public function testResourceTags() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_resource_tags.yml'); + + $def = $container->getDefinition('foo'); + $this->assertTrue($def->hasTag('container.excluded')); + $this->assertTrue($def->hasTag('my.tag')); + $this->assertTrue($def->hasTag('another.tag')); + $this->assertSame([['foo' => 'bar']], $def->getTag('my.tag')); + $this->assertSame([[]], $def->getTag('another.tag')); + $this->assertTrue($def->isAbstract()); + } + public function testLoadShortSyntax() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/Scheduler/Attribute/AsCronTask.php b/src/Symfony/Component/Scheduler/Attribute/AsCronTask.php index 9b77c70cda8a8..9c0798d2a62b1 100644 --- a/src/Symfony/Component/Scheduler/Attribute/AsCronTask.php +++ b/src/Symfony/Component/Scheduler/Attribute/AsCronTask.php @@ -20,16 +20,16 @@ class AsCronTask { /** - * @param string $expression The cron expression to define the task schedule (i.e. "5 * * * *") - * @param string|null $timezone The timezone used with the cron expression - * @param int|null $jitter The cron jitter, in seconds; for example, if set to 60, the cron - * will randomly wait for a number of seconds between 0 and 60 before - * executing which allows to avoid load spikes that can happen when many tasks - * run at the same time - * @param array|string|null $arguments The arguments to pass to the cron task - * @param string $schedule The name of the schedule responsible for triggering the task - * @param string|null $method The method to run as the task when the attribute target is a class - * @param string[]|string|null $transports One or many transports through which the message scheduling the task will go + * @param string $expression The cron expression to define the task schedule (i.e. "5 * * * *") + * @param string|null $timezone The timezone used with the cron expression + * @param int|null $jitter The cron jitter, in seconds; for example, if set to 60, the cron + * will randomly wait for a number of seconds between 0 and 60 before + * executing which allows to avoid load spikes that can happen when many tasks + * run at the same time + * @param array|string|null $arguments The arguments to pass to the cron task + * @param string $schedule The name of the schedule responsible for triggering the task + * @param string|null $method The method to run as the task when the attribute target is a class + * @param string[]|string|null $transports One or many transports through which the message scheduling the task will go */ public function __construct( public readonly string $expression, diff --git a/src/Symfony/Component/Scheduler/Attribute/AsPeriodicTask.php b/src/Symfony/Component/Scheduler/Attribute/AsPeriodicTask.php index e962ecaaf1b08..1c3851ed6971e 100644 --- a/src/Symfony/Component/Scheduler/Attribute/AsPeriodicTask.php +++ b/src/Symfony/Component/Scheduler/Attribute/AsPeriodicTask.php @@ -20,17 +20,17 @@ class AsPeriodicTask { /** - * @param string|int $frequency A string (i.e. "every hour") or an integer (the number of seconds) representing the frequency of the task - * @param string|null $from A string representing the start time of the periodic task (i.e. "08:00:00") - * @param string|null $until A string representing the end time of the periodic task (i.e. "20:00:00") - * @param int|null $jitter The cron jitter, in seconds; for example, if set to 60, the cron - * will randomly wait for a number of seconds between 0 and 60 before - * executing which allows to avoid load spikes that can happen when many tasks - * run at the same time - * @param array|string|null $arguments The arguments to pass to the cron task - * @param string $schedule The name of the schedule responsible for triggering the task - * @param string|null $method The method to run as the task when the attribute target is a class - * @param string[]|string|null $transports One or many transports through which the message scheduling the task will go + * @param string|int $frequency A string (i.e. "every hour") or an integer (the number of seconds) representing the frequency of the task + * @param string|null $from A string representing the start time of the periodic task (i.e. "08:00:00") + * @param string|null $until A string representing the end time of the periodic task (i.e. "20:00:00") + * @param int|null $jitter The cron jitter, in seconds; for example, if set to 60, the cron + * will randomly wait for a number of seconds between 0 and 60 before + * executing which allows to avoid load spikes that can happen when many tasks + * run at the same time + * @param array|string|null $arguments The arguments to pass to the cron task + * @param string $schedule The name of the schedule responsible for triggering the task + * @param string|null $method The method to run as the task when the attribute target is a class + * @param string[]|string|null $transports One or many transports through which the message scheduling the task will go */ public function __construct( public readonly string|int $frequency,