Skip to content

Commit 794b5e1

Browse files
committed
feature #42039 [DependencyInjection] Autoconfigurable attributes on methods, properties and parameters (ruudk)
This PR was merged into the 5.4 branch. Discussion ---------- [DependencyInjection] Autoconfigurable attributes on methods, properties and parameters | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? |no | New feature? | yes | Deprecations? |no | Tickets | | License | MIT | Doc PR | ## Introduction symfony/symfony#39897 introduced the possibility auto configure classes that were annotated with attributes: ```php $container->registerAttributeForAutoconfiguration( MyAttribute::class, static function (ChildDefinition $definition, MyAttribute $attribute, \ReflectionClass $reflector): void { $definition->addTag('my_tag', ['some_property' => $attribute->someProperty]); } ); ``` This works great. But it only works when the attribute is added on the class. With this PR, it's now possible to also auto configure methods, properties and parameters. ## How does it work? Let's say you have an attribute that targets classes and methods like this: ```php #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY)] final class MyAttribute { } ``` You have two services that use them: ```php #[MyAttribute] class MyService { } class MyOtherService { #[MyAttribute] public function myMethod() {} } ``` You can now use `registerAttributeForAutoconfiguration` in your extension, together with a union of the types that you want to seach for. In this example, the extension only cares for classes and methods, so it uses `\ReflectionClass|\ReflectionMethod $reflector`: ```php final class MyBundleExtension extends Extension { public function load(array $configs, ContainerBuilder $container) : void { $container->registerAttributeForAutoconfiguration( MyAttribute::class, static function (ChildDefinition $definition, MyAttribute $attribute, \ReflectionClass|\ReflectionMethod $reflector) : void { $args = []; if ($reflector instanceof \ReflectionMethod) { $args['method'] = $reflector->getName(); } $definition->addTag('my.tag', $args); } ); } } ``` This will tag `MyService` with `my.tag` and it will tag `MyOtherService` with `my.tag, method: myMethod` If the extension also wants to target the properties that are annotated with attributes, it can either change the union to `\ReflectionClass|\ReflectionMethod|\ReflectionProperty $reflector` or it can just use `\Reflector $reflector` and do the switching in the callable. ## Another example Let's say you have an attribute like this: ```php #[Attribute(Attribute::TARGET_CLASS)] final class MyAttribute { } ``` and you use it like this: ```php $container->registerAttributeForAutoconfiguration( MyAttribute::class, static function (ChildDefinition $definition, MyAttribute $attribute, \ReflectionClass|\ReflectionMethod $reflector) : void { $definition->addTag('my.tag'); } ); ``` you'll get an error saying that `ReflectionMethod` is not possible as the attribute only targets classes. Commits ------- 917fcc09f7 [DependencyInjection] Autoconfigurable attributes on methods, properties and parameters
2 parents aa2bbd4 + 33748af commit 794b5e1

File tree

1 file changed

+9
-2
lines changed

1 file changed

+9
-2
lines changed

DependencyInjection/FrameworkExtension.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,8 +557,15 @@ public function load(array $configs, ContainerBuilder $container)
557557
$container->registerForAutoconfiguration(LoggerAwareInterface::class)
558558
->addMethodCall('setLogger', [new Reference('logger')]);
559559

560-
$container->registerAttributeForAutoconfiguration(AsEventListener::class, static function (ChildDefinition $definition, AsEventListener $attribute): void {
561-
$definition->addTag('kernel.event_listener', get_object_vars($attribute));
560+
$container->registerAttributeForAutoconfiguration(AsEventListener::class, static function (ChildDefinition $definition, AsEventListener $attribute, \Reflector $reflector) {
561+
$tagAttributes = get_object_vars($attribute);
562+
if ($reflector instanceof \ReflectionMethod) {
563+
if (isset($tagAttributes['method'])) {
564+
throw new LogicException(sprintf('AsEventListener attribute cannot declare a method on "%s::%s()".', $reflector->class, $reflector->name));
565+
}
566+
$tagAttributes['method'] = $reflector->getName();
567+
}
568+
$definition->addTag('kernel.event_listener', $tagAttributes);
562569
});
563570
$container->registerAttributeForAutoconfiguration(AsController::class, static function (ChildDefinition $definition, AsController $attribute): void {
564571
$definition->addTag('controller.service_arguments');

0 commit comments

Comments
 (0)