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.


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

Reply via email to