Re: [RFC] [Discussion] Add ReflectionAttribute::getCurrent()

From: Date: Sun, 17 May 2026 03:35:37 +0000
Subject: Re: [RFC] [Discussion] Add ReflectionAttribute::getCurrent()
References: 1 2 3 4 5  Groups: php.internals 
Request: Send a blank email to internals+get-130919@lists.php.net to get a copy of this message
On Sat, May 16, 2026, at 8:30 PM, Benjamin Außenhofer wrote: >> In this case, the attribute needs, by definition, to know the name and type of the property >> it's on, but that can be overridden. Any serializer or ORM is going to need to address this >> use case in some form or another; I don't know off hand how Symfony Serializer or Doctrine >> handle it, but in Serde I took the "setter injection" approach, triggered by the presence >> of an interface. > > But you have Serde or AttributeUtils making the instances of Field or > not? I mean you already have the code in userland already that makes > this working, why does it need to be in core with this specific API > that is not obvious to readers and not idiomatic PHP. >> >> This does work, and is in production now. But as Daniel and others have noted, it means >> there's a gap period where the object could be in an invalid state, because construction is >> split across multiple startup methods. (The real code has a whole lot more than just one additional >> setter callbacks.) It also means that trying to construct the attribute object with reflection >> yourself, rather than going through AttributeUtils' API, would lead to a broken object since >> the secondary pseudo-constructors don't get called. > > I don’t think this is a powerful enough argument, there are many cases > where there are gaps where objects are not in a valid state yet. I have made that same point myself in the past many times; yet all the SA tools on the market right now still yell at me for writing to a readonly property from anywhere other than the constructor. >> What this RFC would allow is rewriting the above as: >> >> #[Attribute(Attribute::TARGET_PROPERTY)] >> class Field { >> >> public function __construct( >> public private(set) ?string $name = null, >> public private(set) ?string $type = null, >> ) { >> if ($rProp = ReflectionAttribute::getCurrent()->getReflectionTarget()) { >> $this->name ??= $rProp->getName(); >> $this->type ??= $rProp->getType()->getName(); >> } >> } >> } > > The problem with this code is that it needs explicit if and support to > make it testable at all by still allowing to pass name and type in the > constructor. Which proves Tim’s argument that this is too tightly > coupled. > > With the way ReflectionAttribute defers the constuction of the > attribute, you can close the gap yourself. > > if (is_a($reflectionAttribute->getName(), FromReflectionParameter::class) { > $className = $reflectionAttribute->getName(); > $attribute = $className::fromReflectionParameter($reflector); > } else { > $attribute = $reflectionAttribute->newInstance(); > } I don't see what purpose that serves... What AttributeUtils does now is approximately this (again, simplified for clarity): public function analyze(string $class, string $attribute) { $rClass = new ReflectionClass($class); $classDef = $rClass->getAttributes($attribute, ReflectionAttribute::IS_INSTANCEOF)[0] ?? new $attribute(); if ($classDef instanceof FromReflectionClass) { $classDef->fromReflection($rClass); } if ($classDef instanceof ParseProperties) { // Use reflection to get attributes off the properties of the class. $classDef->setProperties($properties); } // And similar code for other components, hard coded. } Again, this does work, but it means you *must* go through AU's analyze() method or nothing works. I fully agree with the concerns that this is too modal/global. But so far, this seems the least bad way to address the issue of attributes being fundamentally dumb about their context. If someone has a better approach for handling that, please please do share it. I will also note the bonus feature in this RFC, the ReflectionAttributeTarget interface. I frankly want that even more than the reflection target. :-) Even if this RFC fails, that should be brought back in its own little RFC and needs to pass. --Larry Garfield

« previous php.internals (#130919) next »