On May 29, 2023, at 17:48, Andreas Hennings <andr...@dqxtech.net> wrote: > Quite often I found myself writing attribute classes that need to fill > some default values or do some validation based on the symbol the > attribute is attached to. > E.g. a parameter attribute might require a specific type on that > parameter, or it might fill a default value based on the parameter > name.
+1. This is a substantial limitation in the attribute system. > Currently I see two ways to do this: > 1. Do the logic in the code that reads the attribute, instead of the > attribute class. This works ok for one-off attribute classes, but it > becomes quite unflexible with attribute interfaces, where 3rd parties > can provide their own attribute class implementations. > 2. Add additional methods to the attribute class that take the symbol > reflector as a parameter, like "setReflectionMethod()", or > "setReflectionClass()". Or the method in the attribute class that > returns the values can have a reflector as a parameter. I see a third way which introduces less "magic": 3.a. Add a method to ReflectionAttribute which retrieves the target of the attribute as an appropriate reflection object. (Sadly, the obvious name "getTarget" is already taken; I'll call it "getReflectionTarget" for now.) 3.b. Add a static method to ReflectionAttribute which, when called within an Attribute constructor which is being called by ReflectionAttribute::newInstance(), returns the ReflectionAttribute object which is being instantiated. These features could be used together to set a default property on an attribute based on its target, e.g. #[Attribute(Attribute::TARGET_PROPERTY)] class PropertyAnnotation { public string $name; public function __construct(?string $name = null) { $this->name = $name ?? ReflectionAttribute::underConstruction()->getReflectionTarget()->getName(); } } Another variant that comes to mind is: 3.b. Add a new flag to attributes which causes the ReflectionAttribute object to be passed to the constructor as the first argument, e.g. #[Attribute(Attribute::TARGET_PROPERTY | Attribute::WHO_MADE_ME)] class PropertyAnnotation { public string $name; public function __construct(ReflectionAttribute $attr, ?string $name = null) { $this->name = $name ?? $attr->getReflectionTarget()->getName(); } } This is a little messier because it can't be used "under the covers" by an attribute base class, but it accomplishes the same goals. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php