[PHP-DEV] [RFC] [Discussion] Add ReflectionAttribute::getCurrent()

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.

Thanks,
-Daniel

On 4 May 2026 21:24:39 BST, Daniel Scherzer <daniel.e.scherzer@gmail.com> 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: PHP: rfc:reflectionattribute-getcurrent

"a new static method, ReflectionAttribute::getCurrent(), that, when called from an attribute constructor, returns a reflection object corresponding to what the attribute was applied to."

This sounds like an arbitrary new rule for just this functionality. I don't think we should have special rules for a single static method call.

I believe it's useful to have something like this, but I'm not in favour of this approach.

Would it not be possible for this to be a normal (dynamic) method on the ReflectionAttrbute object?

cheers
Derick

On Mon, May 4, 2026 at 2:13 PM Derick Rethans <derick@php.net> wrote:

On 4 May 2026 21:24:39 BST, Daniel Scherzer <daniel.e.scherzer@gmail.com> 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.

“a new static method, ReflectionAttribute::getCurrent(), that, when called from an attribute constructor, returns a reflection object corresponding to what the attribute was applied to.”

This sounds like an arbitrary new rule for just this functionality. I don’t think we should have special rules for a single static method call.

I believe it’s useful to have something like this, but I’m not in favour of this approach.

Would it not be possible for this to be a normal (dynamic) method on the ReflectionAttrbute object?

cheers
Derick

I agree that this is a bit odd, but the problem with just adding a dynamic method on the ReflectionAttribute object is that, in the attribute constructor, there is no access to the ReflectionAttribute instance (unless you want to extract it from the backtrace, which we shouldn’t suggest). Internally, the implementation is doing that backtrace processing in a more stable way than userland probably would.

We could adjust the signature, so that there is still a static method to get the ReflectionAttribute, but then a normal dynamic method call on that object to get the target reflection object:

class ReflectionAttribute {

// Call from the constructor of an attribute to get the original ReflectionAttribute instance
public static function getCurrent(): ReflectionAttribute {}

// After using getCurrent(), use this to get the reflection target
public function getReflectionTarget(): ReflectionAttributeTarget {}
}

but either way I think we need some kind of (C-implemented) backtrace processing, and because of that it makes sense to limit this to just attribute constructors so that we don’t need to process backtraces to an arbitrary depth.

An alternative would be to provide the reflection target (or the ReflectionAttribute instance) to the attribute constructor as a parameter, but then we need some kind of way for attribute classes to signal that they want to be given that parameter, and then you get into the weeds on how to opt-in (mark the parameter with a different attribute? add an interface, even though constructors are exempt from signature checks?) that would probably make that harder to reason with for end users.

-Daniel

On Mon, May 4, 2026, at 23:22, Daniel Scherzer wrote:

On Mon, May 4, 2026 at 2:13 PM Derick Rethans <derick@php.net> wrote:

On 4 May 2026 21:24:39 BST, Daniel Scherzer <daniel.e.scherzer@gmail.com> 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.

“a new static method, ReflectionAttribute::getCurrent(), that, when called from an attribute constructor, returns a reflection object corresponding to what the attribute was applied to.”

This sounds like an arbitrary new rule for just this functionality. I don’t think we should have special rules for a single static method call.

I believe it’s useful to have something like this, but I’m not in favour of this approach.

Would it not be possible for this to be a normal (dynamic) method on the ReflectionAttrbute object?

cheers
Derick

I agree that this is a bit odd, but the problem with just adding a dynamic method on the ReflectionAttribute object is that, in the attribute constructor, there is no access to the ReflectionAttribute instance (unless you want to extract it from the backtrace, which we shouldn’t suggest). Internally, the implementation is doing that backtrace processing in a more stable way than userland probably would.

We could adjust the signature, so that there is still a static method to get the ReflectionAttribute, but then a normal dynamic method call on that object to get the target reflection object:

class ReflectionAttribute {

// Call from the constructor of an attribute to get the original ReflectionAttribute instance
public static function getCurrent(): ReflectionAttribute {}

// After using getCurrent(), use this to get the reflection target
public function getReflectionTarget(): ReflectionAttributeTarget {}
}

but either way I think we need some kind of (C-implemented) backtrace processing, and because of that it makes sense to limit this to just attribute constructors so that we don’t need to process backtraces to an arbitrary depth.

An alternative would be to provide the reflection target (or the ReflectionAttribute instance) to the attribute constructor as a parameter, but then we need some kind of way for attribute classes to signal that they want to be given that parameter, and then you get into the weeds on how to opt-in (mark the parameter with a different attribute? add an interface, even though constructors are exempt from signature checks?) that would probably make that harder to reason with for end users.

-Daniel

What about simply allowing an acceptance of the ReflectionAttribute in the constructor? Engine-level injection, basically. You could have it transparent from the attribute (kinda like self from python):

#[Attribute]
class Att {
  public function __construct(RelectionAttributeTarget $self, string $name) {}
}
function thing(#[Att("name")] string $b) {}

It’d still be something “special” but attributes are already kinda special and magical.

— Rob

On Mon, May 4, 2026 at 2:47 PM Rob Landers rob@bottled.codes wrote:

On Mon, May 4, 2026, at 23:22, Daniel Scherzer wrote:

On Mon, May 4, 2026 at 2:13 PM Derick Rethans <derick@php.net> wrote:

On 4 May 2026 21:24:39 BST, Daniel Scherzer <daniel.e.scherzer@gmail.com> 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.

“a new static method, ReflectionAttribute::getCurrent(), that, when called from an attribute constructor, returns a reflection object corresponding to what the attribute was applied to.”

This sounds like an arbitrary new rule for just this functionality. I don’t think we should have special rules for a single static method call.

I believe it’s useful to have something like this, but I’m not in favour of this approach.

Would it not be possible for this to be a normal (dynamic) method on the ReflectionAttrbute object?

cheers
Derick

I agree that this is a bit odd, but the problem with just adding a dynamic method on the ReflectionAttribute object is that, in the attribute constructor, there is no access to the ReflectionAttribute instance (unless you want to extract it from the backtrace, which we shouldn’t suggest). Internally, the implementation is doing that backtrace processing in a more stable way than userland probably would.

We could adjust the signature, so that there is still a static method to get the ReflectionAttribute, but then a normal dynamic method call on that object to get the target reflection object:

class ReflectionAttribute {

// Call from the constructor of an attribute to get the original ReflectionAttribute instance
public static function getCurrent(): ReflectionAttribute {}

// After using getCurrent(), use this to get the reflection target
public function getReflectionTarget(): ReflectionAttributeTarget {}
}

but either way I think we need some kind of (C-implemented) backtrace processing, and because of that it makes sense to limit this to just attribute constructors so that we don’t need to process backtraces to an arbitrary depth.

An alternative would be to provide the reflection target (or the ReflectionAttribute instance) to the attribute constructor as a parameter, but then we need some kind of way for attribute classes to signal that they want to be given that parameter, and then you get into the weeds on how to opt-in (mark the parameter with a different attribute? add an interface, even though constructors are exempt from signature checks?) that would probably make that harder to reason with for end users.

-Daniel

What about simply allowing an acceptance of the ReflectionAttribute in the constructor? Engine-level injection, basically. You could have it transparent from the attribute (kinda like self from python):

#[Attribute]
class Att {
  public function __construct(RelectionAttributeTarget $self, string $name) {}
}
function thing(#[Att("name")] string $b) {}

It’d still be something “special” but attributes are already kinda special and magical.

— Rob

The problem with the lack of an explicit opt-in is that it would break existing attributes E.g. on 8.5, ReflectionAttribute::newInstance() for that example Att would be called with a single argument (the string), and on 8.6 it would be called with 2 arguments (the ReflectionAttributeTarget and the string) without the attribute author having, or attribute user, having changed anything.

And you might think we could check based on parameter types, but then the attribute would be dropping support for older versions of PHP, whereas with a “magic” method the call could be conditional.

Plus, just because a class can be used as an attribute, does not mean it must always be used as an attribute - and I found at least one case in symfony tests where they actually manually instantiate an attribute, https://github.com/symfony/symfony/blob/8e8f87c6fa5f47a431fc2f49bdbe601f6769e19d/src/Symfony/Component/JsonPath/Tests/Attribute/AsJsonPathFunctionTest.php.

Basically, if we provided the reflection information (whether it is a ReflectionAttribute or ReflectionAttributeTarget) via a parameter, we would run into one or more of the following

  • without any opt-in, breaking existing attributes by adding a new unexpected parameter
  • with an implicit opt-in of a typed parameter, breaking backwards compatibility for attributes supporting multiple versions of PHP
  • with an explicit opt-in of this parameter should have the reflection information, similar backwards compatibility breaks
  • confusion for library authors about how to use the new feature

I agree that this is a bit weird as a calling pattern, but I’ve been trying to think of an alternative for months (I first started working on this at Longhorn PHP in October) and haven’t been able to come up with anything cleaner.

The only slight improvement I had thought of a few months ago was the addition of the instance method on ReflectionAttribute to get the reflection target, and then separately ::getCurrent() would return the ReflectionAttribute instance, but I think some “magic” method like this is still needed.

-Daniel

On Mon, May 4, 2026, at 4:58 PM, Daniel Scherzer wrote:

On Mon, May 4, 2026 at 2:47 PM Rob Landers <rob@bottled.codes> wrote:

__

On Mon, May 4, 2026, at 23:22, Daniel Scherzer wrote:

On Mon, May 4, 2026 at 2:13 PM Derick Rethans <derick@php.net> wrote:

On 4 May 2026 21:24:39 BST, Daniel Scherzer <daniel.e.scherzer@gmail.com> 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: PHP: rfc:reflectionattribute-getcurrent

"a new static method, ReflectionAttribute::getCurrent(), that, when called from an attribute constructor, returns a reflection object corresponding to what the attribute was applied to."

This sounds like an arbitrary new rule for just this functionality. I don't think we should have special rules for a single static method call.

I believe it's useful to have something like this, but I'm not in favour of this approach.

Would it not be possible for this to be a normal (dynamic) method on the ReflectionAttrbute object?

cheers
Derick

I agree that this is a bit odd, but the problem with just adding a dynamic method on the ReflectionAttribute object is that, in the attribute constructor, there is no access to the ReflectionAttribute instance (unless you want to extract it from the backtrace, which we shouldn't suggest). Internally, the implementation is doing that backtrace processing in a more stable way than userland probably would.

We could adjust the signature, so that there is still a static method to *get* the ReflectionAttribute, but then a normal dynamic method call on that object to get the target reflection object:

class ReflectionAttribute {

    // Call from the constructor of an attribute to get the original ReflectionAttribute instance
    public static function getCurrent(): ReflectionAttribute {}

    // After using getCurrent(), use this to get the reflection target
    public function getReflectionTarget(): ReflectionAttributeTarget {}
}

but either way I think we need some kind of (C-implemented) backtrace processing, and because of that it makes sense to limit this to *just* attribute constructors so that we don't need to process backtraces to an arbitrary depth.

An alternative would be to provide the reflection target (or the ReflectionAttribute instance) to the attribute constructor as a parameter, but then we need some kind of way for attribute classes to signal that they want to be given that parameter, and then you get into the weeds on how to opt-in (mark the parameter with a different attribute? add an interface, even though constructors are exempt from signature checks?) that would probably make that harder to reason with for end users.

-Daniel

What about simply allowing an acceptance of the ReflectionAttribute in the constructor? Engine-level injection, basically. You could have it transparent from the attribute (kinda like self from python):

#[Attribute]
class Att {
  public function __construct(RelectionAttributeTarget $self, string $name) {}
}

function thing(#[Att("name")] string $b) {}

It'd still be something "special" but attributes are already kinda special and magical.

— Rob

The problem with the lack of an explicit opt-in is that it would break
existing attributes E.g. on 8.5, ReflectionAttribute::newInstance() for
that example `Att` would be called with a single argument (the string),
and on 8.6 it would be called with 2 arguments (the
ReflectionAttributeTarget and the string) without the attribute author
having, *or attribute user*, having changed anything.

And you might think we could check based on parameter types, but then
the attribute would be dropping support for older versions of PHP,
whereas with a "magic" method the call could be conditional.

Plus, just because a class *can* be used as an attribute, does not mean
it *must always* be used as an attribute - and I found at least one
case in symfony tests where they actually manually instantiate an
attribute,
symfony/src/Symfony/Component/JsonPath/Tests/Attribute/AsJsonPathFunctionTest.php at 8e8f87c6fa5f47a431fc2f49bdbe601f6769e19d · symfony/symfony · GitHub.

Basically, if we provided the reflection information (whether it is a
ReflectionAttribute or ReflectionAttributeTarget) via a parameter, we
would run into one or more of the following

* without any opt-in, breaking existing attributes by adding a new
unexpected parameter
* with an implicit opt-in of a typed parameter, breaking backwards
compatibility for attributes supporting multiple versions of PHP
* with an explicit opt-in of *this parameter should have the reflection
information*, similar backwards compatibility breaks
* confusion for library authors about how to use the new feature

I agree that this is a bit weird as a calling pattern, but I've been
trying to think of an alternative for months (I first started working
on this at Longhorn PHP in October) and haven't been able to come up
with anything cleaner.

The only slight improvement I had thought of a few months ago was the
addition of the instance method on ReflectionAttribute to get the
reflection target, and then separately ::getCurrent() would return the
ReflectionAttribute instance, but I think some "magic" method like this
is still needed.

-Daniel

For context, there was a discussion last year about this problem space:

The issue is that there's really no good, clean solution to giving an attribute access to the reflection target it is on. All options have some significant drawback. (See that thread for details.)

I don't like it either, but this approach is the least-bad I can think of. If someone has a better alternative with fewer trade-offs, do share.

I believe this would let me greatly simplify AttributeUtils, in practice.

(Disclosure: Daniel and I discussed this issue at length back at Longhorn PHP, along with a few others.)

--Larry Garfield

On Mon, May 4, 2026 at 2:13 PM Derick Rethans <derick@php.net> wrote:

On 4 May 2026 21:24:39 BST, Daniel Scherzer <daniel.e.scherzer@gmail.com> 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.

“a new static method, ReflectionAttribute::getCurrent(), that, when called from an attribute constructor, returns a reflection object corresponding to what the attribute was applied to.”

This sounds like an arbitrary new rule for just this functionality. I don’t think we should have special rules for a single static method call.

I believe it’s useful to have something like this, but I’m not in favour of this approach.

Would it not be possible for this to be a normal (dynamic) method on the ReflectionAttrbute object?

cheers
Derick

In order to reduce the scope of the weird new method, I have updated the RFC to split it up:

  • ReflectionAttribute::getCurrent(), when called from an attribute constructor, returns a ReflectionAttribute matching the usage (replacing the currently-possible hacking with backtraces)
  • ReflectionAttribute::getReflectionTarget() is a normal (dynamic) method returning the ReflectionAttributeTarget

These are expected to be used together in the constructors of attributes, e.g. ReflectionAttribute::getCurrent()->getReflectionTarget(), but the normal getReflectionTarget() method is also useful and usable elsewhere.

-Daniel