# RFC: Add ReflectionAttribute::getCurrent()

- Status: Draft
- Authors: Daniel Scherzer, daniel.e.scherzer@gmail.com
- Date: 2026-04-19
- Version: 0.4
- Implementation: https://github.com/php/php-src/pull/21440
- Original: https://wiki.php.net/rfc/reflectionattribute-getcurrent
- Page: https://externals.io/rfc/reflectionattribute-getcurrent
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Votes

### TODO (closed)

Yes: 0 · No: 0 · Abstain: 0

## Discussions on the mailing list

- [Declaration-aware attributes](https://externals.io/message/120443.md) — started on 2023-05-30, 15 messages
- [\[RFC\] \[Discussion\] Add ReflectionAttribute::getCurrent()](https://externals.io/message/130766.md) — started on 2026-05-04, 24 messages
- [\[VOTE\] RFC: Add ReflectionAttribute::getCurrent()](https://externals.io/message/131212.md) — started on 2026-06-12, 5 messages

---

Userland attributes (i.e. attributes not provided by PHP itself or an extension) are created with `ReflectionAttribute::newInstance()` and have their constructors called with the arguments provided to the attribute. However, they lack addition context as to what they are being applied to. While it is possible to hack around with `debug_backtrace` to identify the original `ReflectionAttribute` instance, and thus the type of construct the attribute is applied to (class, class constant, etc.) there is no way to identify *which* class (or class constant, etc.) the attribute is being applied to.

This RFC proposes the addition of two new methods:

- `ReflectionAttribute::getReflectionTarget()` allows identifying the target of an attribute represented by a `ReflectionAttribute` instance
- the static method `ReflectionAttribute::getCurrent()`, *when called from an attribute constructor*, returns the `ReflectionAttribute` corresponding to the attribute usage

Together, these methods allow an attribute constructor to identify the target it is applied to.

For the purposes of this RFC, an “item” is something that can have attributes applied to it, including classes, functions, parameters, constants, etc.

## Introduction

Attributes were first introduced in [attributes\_v2](https://externals.io/rfc/attributes_v2) in PHP 8.0, with the current syntax adopted [shorter\_attribute\_syntax\_change](https://externals.io/rfc/shorter_attribute_syntax_change). Userland attributes provide a way to add metadata to items in a structured way that can be read by external tools using reflection.

However, userland attributes, when created via `ReflectionAttribute::newInstance()`, only know about the arguments passed to the attribute when it was applied. Backtracing hacking can be used to identify the `ReflectionAttribute` object that was used, and from there `ReflectionAttribute::getTarget()` could be used to identify what type of item the attribute was applied to, but not which specific item.

Various workarounds exist in userland, such as:

- the [Crell/AttributeUtils](https://github.com/Crell/AttributeUtils) composer library, which provides interfaces (e.g. `FromReflectionClass`) that indicate an attribute wants to be told what item (e.g. for `FromReflectionClass`, what class) the attribute was applied to. This information is provided after the attribute is constructed, and only when the attribute is loaded through the library.
- adding manual `::tryFrom` static constructors to use (e.g. Symfony/console's attributes like [Option](https://github.com/symfony/console/blob/ad665e88b844414e81707b0a33dfe757c65393f7/Attribute/Option.php#L60))

However, these rely on using an external library or creating attributes separately from `ReflectionAttribute::newInstance()`. This RFC proposes two new methods; the first, `ReflectionAttribute::getReflectionTarget()`, allows associating a `ReflectionAttribute` with the specific target it was applied to, rather than just the type of target. The second, `ReflectionAttribute::getCurrent()`, abstracts away the backtrace hacking. These would allow attributes to add custom validation or otherwise make use of the information about what item they were applied to:

```php
<?php
#[Attribute]
class Demo {
    public function __construct($args) {
        // will echo the same ReflectionClass that `echo $case;` does below
        // this is a contrived example, real world code would do something useful
        echo ReflectionAttribute::getCurrent()->getReflectionTarget();
    }
}
 
#[Demo("class")]
class WithDemo {
 
}
 
$case = new ReflectionClass(WithDemo::class);
echo $case;
echo "\n";
$case->getAttributes()[0]->newInstance();
 
?>
```

## Proposal

When called within the constructor of an attribute object, `ReflectionAttribute::getCurrent()` will return a `ReflectionAttribute` that corresponds to the attribute application.

Anywhere that a `ReflectionAttribute` object is available (now including attribute constructors), `ReflectionAttribute::getReflectionTarget()` returns the reflection object of the item to which the attribute was attached. The type of that reflection object is highly variable, as there are many types of item to which an attribute may be attached, and there is not a 1:1 correspondence between the `Attribute::TARGET_*` constant used and the resulting reflection object type.

For example, a `Attribute::TARGET_CLASS_CONSTANT` attribute could be applied to a class constant, unit enum case, or backed enum case, which would result in `ReflectionClassConstant`, `ReflectionEnumUnitCase`, or `ReflectionEnumBackedCase`, respectively.

To avoid a huge union return type, and to account for potential future expansions of attribute targets (e.g. [attributes-on-constants](https://externals.io/rfc/attributes-on-constants)) or new reflection subclasses, this RFC introduces a new interface, `ReflectionAttributeTarget`, which represents “things that can have attributes” or “things that an attribute can be applied to”.

Thus, the additions in this RFC look like the following (classes that implement `Reflector` did so previously, and are just updated to also implement `ReflectionAttributeTarget`)

```php
<?php
 
interface ReflectionAttributeTarget extends Reflector
{
    public function getAttributes(?string $name = null, int $flags = 0): array {}
}
 
// ...
 
abstract class ReflectionFunctionAbstract implements Reflector, ReflectionAttributeTarget { /* ... */ }
class ReflectionClass implements Reflector, ReflectionAttributeTarget { /* ... */ }
class ReflectionProperty implements Reflector, ReflectionAttributeTarget { /* ... */ }
class ReflectionClassConstant implements Reflector, ReflectionAttributeTarget { /* ... */ }
class ReflectionParameter implements Reflector, ReflectionAttributeTarget { /* ... */ }
class ReflectionConstant implements Reflector, ReflectionAttributeTarget { /* ... */ }
 
class ReflectionAttribute implements Reflector
{
    // ...
    public function getReflectionTarget(): ReflectionAttributeTarget {}
 
    public static function getCurrent(): ReflectionAttribute {}
}
 
?>
```

When using the result of `ReflectionAttribute::getReflectionTarget()`, the developer may either `assert()` the type of the reflection object and/or use an `instanceof` check, as appropriate, if the logic depends on the type, or to provide information to static analysis tools and IDEs. For example, if an attribute may be placed on a class or method, it could check that like so:

```php
#[Attribute(Attribute::TARGET_CLASS)]
class SingleTargetAttribute {
    public function __construct() {
        // SA tools won't know what type $target is,
        // beyond ReflectionAttributeTarget
        $target = ReflectionAttribute::getCurrent()->getReflectionTarget();
 
        assert($target instanceof ReflectionClass);
        // Now SA tools know that $target is ReflectionClass, and can
        // offer appropriate code completion and validation.
 
        // More stuff here.
    }
}
 
 
#[Attribute(Attribute::TARGET_CLASS|Attribute::TARGET_METHOD)]
class MultiUseAttribute {
    public function __construct() {
        $target = ReflectionAttribute::getCurrent()->getReflectionTarget();
        if ($target instanceof ReflectionClass) {
            // Do class-specific logic here.
        } else if ($target instanceof ReflectionMethod) {
            // Do method-specific logic here.
        }
        // Do logic common to all attributes here.
    }
}
```

### Examples

Simple example:

```php
<?php
 
#[Attribute(Attribute::TARGET_CLASS)]
class StableInterface {
    public function __construct() {
        $target = ReflectionAttribute::getCurrent()->getReflectionTarget();
        // For SA tools:
        assert($target instanceof ReflectionClass);
 
        // Should only be applied to interfaces
        if (!$target->isInterface()) {
            throw new Exception("#[StableInterface] is only for interfaces!");
        }
 
        // Some global function that exists elsewhere
        record_stable_interface($target->getName());
    }
}
 
?>
```

The above attribute, when instantiated, will confirm that it is being applied to an interface (rather than a class, trait, or enum) and then record the name of the targeted interface. This could then be used by tools to automatically generate a list of interfaces in a library that are considered stable, perhaps with the help of the [olvlvl/composer-attribute-collector library](https://github.com/olvlvl/composer-attribute-collector).

`ReflectionAttribute::getCurrent()` must be called directly from the constructor of an attribute that is being instantiated with `ReflectionAttribute::newInstance()`, otherwise it fails.

### Returned classes

`ReflectionAttribute::getReflectionTarget()` is documented on a type level to return `ReflectionAttributeTarget`, but the actual underlying class will be:

| Flag | Applied to | Return type |
|---|---|---|
| `Attribute::TARGET_CLASS` | Class | `ReflectionClass` |
| `Attribute::TARGET_CLASS` | Trait | `ReflectionClass` |
| `Attribute::TARGET_CLASS` | Interface | `ReflectionClass` |
| `Attribute::TARGET_CLASS` | Enum | `ReflectionEnum` |
| `Attribute::TARGET_METHOD` | Class method | `ReflectionMethod` |
| `Attribute::TARGET_CLASS_CONSTANT` | Class constant | `ReflectionClassConstant` |
| `Attribute::TARGET_CLASS_CONSTANT` | Enum case (unit) | `ReflectionEnumUnitCase` |
| `Attribute::TARGET_CLASS_CONSTANT` | Enum case (backed) | `ReflectionEnumBackedCase` |
| `Attribute::TARGET_PROPERTY` | Class property | `ReflectionProperty` |
| `Attribute::TARGET_FUNCTION` | Global function | `ReflectionFunction` |
| `Attribute::TARGET_PARAMETER` | Function parameter | `ReflectionParameter` |
| `Attribute::TARGET_PARAMETER` | Method parameter | `ReflectionParameter` |
| `Attribute::TARGET_CONSTANT` | Global constant | `ReflectionConstant` |

Note that

- `ReflectionEnum` is a subclass of `ReflectionClass`
- `ReflectionEnumBackedCase` is a subclass of `ReflectionEnumUnitCase` which is a subclass of `ReflectionClassConstant`

To be clear, even when the `ReflectionAttribute` was retrieved from a different type, the return type will be the type listed in the table above. For example, using `ReflectionClassConstant` with an enum case works fine, but `ReflectionAttribute::getReflectionTarget()` will return the enum-specific subclass. Similarly, `ReflectionObject` can be used for classes and enums, but `ReflectionClass` or `ReflectionEnum` will be returned from `ReflectionAttribute::getReflectionTarget()`

## Backward Incompatible Changes

None

## Proposed PHP Version(s)

Next minor version (PHP 8.6).

## RFC Impact

### To the Ecosystem

Attribute-related libraries may want to make use of the new feature.

### To Existing Extensions

Only ext/reflection is affected - all of this is implemented in reflection.

### To SAPIs

None

## Open Issues

None

## Voting Choices

Add `ReflectionAttribute::getCurrent()`, `ReflectionAttribute::getReflectionTarget()` and the associated `ReflectionAttributeTarget` interface?

---

Primary Vote requiring a 2/3 majority to accept the RFC:

**Vote: TODO** — Yes: 0 · No: 0 · Abstain: 0 (closed)

## Patches and Tests

<https://github.com/php/php-src/pull/21440>

## Implementation

After the RFC is implemented, this section should contain:

1. the version(s) it was merged into
2. a link to the git commit(s)
3. a link to the PHP manual entry for the feature

## References

- [attributes\_v2](https://externals.io/rfc/attributes_v2)
- [shorter\_attribute\_syntax\_change](https://externals.io/rfc/shorter_attribute_syntax_change)
- [attributes-on-constants](https://externals.io/rfc/attributes-on-constants)
- <https://github.com/Crell/AttributeUtils>
- <https://github.com/olvlvl/composer-attribute-collector>
- Symfony/console (<https://github.com/symfony/console/tree/ad665e88b844414e81707b0a33dfe757c65393f7>)
- Previous mailing list discussion: <https://externals.io/message/120443>

## Rejected Features

Keep this updated with features that were discussed on the mail lists.

## Changelog

If there are major changes to the initial proposal, please include a short summary with a date or a link to the mailing list announcement here, as not everyone has access to the wikis' version history.

- v0.1: created
- v0.2: updates with improvements from Crell
- v0.3: switch from having just a static method, `ReflectionAttribute::getCurrent(): ReflectionAttributeTarget`, to a static method, `ReflectionAttribute::getCurrent(): ReflectionAttribute` and an instance method `ReflectionAttribute::getReflectionTarget(): ReflectionAttributeTarget`
- v0.4: in light of initial votes and feedback, both on the mailing list and elsewhere, RFC vote cancelled pending overhaul
