Am 07.06.2026, 06:29:17 schrieb Benjamin Außenhofer <[email protected]>:
> > > Am 05.06.2026, 23:52:54 schrieb Daniel Scherzer < > [email protected]>: > >> On Mon, May 4, 2026 at 1:24 PM Daniel Scherzer < >> [email protected]> wrote: >> >>> Hi internals, >>> >>> I'd like to start the discussion for a new RFC about adding a new >>> method, ReflectionAttribute::getCurrent(), to access the current reflection >>> target of an attribute. >>> >>> * RFC: https://wiki.php.net/rfc/reflectionattribute-getcurrent >>> * Implementation: https://github.com/php/php-src/pull/21440 >>> >>> Thanks, >>> -Daniel >>> >> >> Barring any additional feedback, I plan to open the vote on this in the >> next few days. >> >> -Daniel >> > > Hi Daniel, > > sorry i could not find the time to reply earlier. the idea in general is > good, but i still very much disagree about the static method to get the > reflector context. > > An attribute is often passed around as a configuration object or hierachy > down to other services, for example in Symfony validator. > > #[NotNull] > public $foo; > > $validator = new NotNullValidator(); > $validator->validate($instance->foo, $notNullAttribute); > > For testing purposes it makes sense in these examples to construct the > attributes directly using new SomeAttribute(). Now with this proposal, you > need to be careful and know from the outside, when that is possible, or > when you need to call it through ReflectionAttribute::newInstance(). The > coupling becomes very strong suddenly. > > When you create the attributes yourself and without calling newInstance(), > then the method does not work at all: > > $class = $reflectionAttribute->getName(); > new $class(…$this->getArguments())); // ReflectionAttribute::getCurrent() > returns null in this ctor. > > This is something the RFC is not clear about fully. There is the > definition of an „attribute constructor“, but a constructor of an Attribute > can be called many ways. This proposal is about the ctor only when called > through newInstance(). > > I propose instead that we extend the newInstance() factory method, to be > more versatile, and use the benefit here that we already have a factory > method where we can place this logic: > > <?php > > interface ReflectionAwareAttribute > { > public function targetsReflector(Reflector $reflector); > } > > class ReflectionAttribute > { > public function newInstance() > { > $class = $this->getName(); > $instance = new $class(...$this->getArguments()); > > if ($instance instanceof ReflectionAwareAttribute) { > $instance->targetsReflector($this); > } > > return $instance; > } > } > > This is much cleaner OOP wise. > > > 1. Increases testability of attribute classes that are reflection > aware, only need a Reflector instance, no need to have actual attribute > example set up. > 2. Uses the existing factory method to concentrate the construction > logic into one place. > 3. avoids static method call with unclear semantics on when > „getCurrent()“ returns something. > 4. if a user of attributes has to avoid newInstance() (for whatever > reason), they can call targetsReflector() themselves. There is no way to > „setCurrent()“ reflector however, hiding part of this new behavior in the > engine, where it could be 100% user controlled. > > Just came up with another benefit of my alternative proposal, it can be polyfilled much easier, which Symfony as a big attribute user is certainly interested in. A polyfill can ship the interface, and then at every callsite of newInstance() add another call: if (PHP_VERSION_ID <= 80600 && method_exists($attribute, 'targetsReflector‘ )) { $attribute->targetsReflector($reflector); } > The only „downside" is that you cant make properties set in > targetsReflector() readonly. > > But from a semantical point of view, the attribute constructor should not > have „magic" access to the reflector just to satisfy some own desire to > achieve more readonly/value objects. And a class with a constructor calling > a static method to get state cannot be called „simple“ or „value object“ in > my opinion anyways. > > Take: > > #[Entity(repositoryClass: UserRepository::class, table: ‚users’)] > #[Column(type: Types::STRING, name: ‚foo‘, nullable: false)] > > This is just a different way of writing: > new Entity(repositoryClass: UserRepository::class, table: ‚users’) > new Column(type: Types::STRING, name: ‚foo‘, nullable: false) > > Basic understanding of this for any PHP developer makes population of the > global ReflectionAttribute::getCurrent() seeem „magical“, something we > should avoid. > > greetings > Benjamin > > >
