# PHP RFC: Add ReflectionAttribute->getTargetReflector( * Version: 0.9 * Date: 2025-07-07 (use today's date here) * Author: Andreas Hennings, andreas.hennings@dqxtech.net * Status: Draft * Implementation: https://github.com/php/php-src/pull/19066 ## Introduction This RFC introduces a new method ReflectionAttribute->getTargetReflector(), which provides the reflector object from which the ReflectionAttribute object originates. This implies that, internally, the ReflectionAttribute object maintains a reference to that original reflector object. The RFC is meant as an incremental step towards "target-aware attributes". It does not yet provide a (clean) way to inject the target reflector into the attribute instance, but it does move us one step closer. ## Proposal A new public method ::getTargetReflector() will be available on ReflectionAttribute objects. The method has no parameters. The return value is the same reflector object from which `->getAttributes()` was called to obtain the ReflectionAttribute object. The return type is hinted as `\ReflectionClass|\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty|\ReflectionClassConstant`. The following assertion will pass for all inputs (given that `$reflector` is an instance of a built-in class): ```php function test(Reflector $reflector, int $i): void { // Userland reflector classes if (!match (get_class($reflector)) { ReflectionClass::class, ReflectionFunctionAbstract::class, ReflectionParameter::class, ReflectionProperty::class, ReflectionClassConstant::class => true, default => false, }) { assert(true); return; } $reflection_attribute = $reflector->getAttributes()[$i] ?? null; if ($reflection_attribute === null) { assert(true); return; } assert($reflection_attribute->getTargetReflector() === $reflector); } ``` ### Examples ```php #[Attribute] class A {} #[A] function foo() {} $reflection_function = new ReflectionFunction('foo'); $reflection_attribute = $reflection_function->getAttributes()[0]; assert($reflection_attribute->getTargetReflector() === $reflection_function); ``` ### Motivation Different participants in the php mailing list have observed and expressed the need for an attribute class to know which symbol (class, function, parameter etc) the attribute is attached to. E.g. an attribute on a constructor parameter might specify which service should be injected, and it might do so based on the parameter type. Often it is desirable to do this logic in the attribute itself, rather than in the code that reads and processes the attributes. Currently, php itself does not provide the target reflector into an attribute class.\ Instead, this needs to happen in userland. Any attribute that relies on such a mechanism will only work if instantiated with that specific userland library, otherwise the attribute instance would remain incomplete. #### Relation to existing proposals Different proposals were made how to provide the target reflector to the attribute instance: - The target reflector could be provided through "method injection", by calling a method on the attribute instance after it is constructed. This works, but it means the reflector is not available during the constructor. - The target reflector could be provided as an additional constructor parameter - this would cause confusion if a value for that parameter is provided in the attribute statement. - The target reflector could be provided into a property, which could even happen before the constructor is called. - The target reflector could be exposed through a static method `ReflectionAttribute::getCurrentTargetReflector()`, which would only return that value during the invocation of `ReflectionAttribute->newInstance()`. This would allow to have all the logic in the constructor. But it was argued that this is undesirable due to its reliance on global state. - With all the proposals above, one debatable question is whether one should provide/inject the target reflector, or the ReflectionAttribute object itself, from which one could then obtain the target reflector. What all of these proposals have in common is that the ReflectionAttribute will need a reference to the original (target) reflector. And for the sake of completeness, one should expect to be able to obtain this target reflector through a public method on ReflectionAttribute. This RFC introduces this common part of the different proposals, while leaving the more difficult choices to a follow-up RFC. #### Userland benefits The proposed new method will slightly simplify some userland implementations: Currently, a userland implementation that wants to provide the target reflector to an attribute instance will need a reference to that target reflector. Now, such a userland function would be possible that only expects a ReflectionAttribute parameter, and that does not need the target reflector as a second parameter. ```php interface InjectAttributeTargetReflector { public function injectTargetReflector(\Reflector $reflector): void; } function get_attribute_instance(\ReflectionAttribute $reflection_attribute): object { $attribute_instance = $reflection_attribute->newInstance(); if ($attribute_instance instanceof InjectAttributeTargetReflector) { $attribute_instance->injectTargetReflector($reflection_attribute->getTargetReflector()); } return $attribute_instance; } ``` Aside of this, it is now possible to obtain the target reflector in the attribute constructor with the help of `debug_backtrace()`. However, any public library should probably avoid this, as it is mostly meant as a debug tool, and some environments might even have it in their disabled functions list. ## Backward Incompatible Changes Userland or extension code that provides a subclass of ReflectionAttribute will need to implement the `getTargetReflector()` method in a way that matches the contract, or accept that an exception will be thrown. ## Proposed PHP Version(s) Next PHP 8.x. ## RFC Impact ### Future Scope The next step will be to find ways to provide the target reflector object to the actual attribute instance. This shall happen in follow-up RFCs. ===== Voting Choices ===== Pick a title that reflects the concrete choice people will vote on. Please consult [[https://github.com/php/policies/blob/main/feature-proposals.rst#voting|the php/policies repository]] for the current voting guidelines. * Yes * No ===== Patches and Tests ===== Links to proof of concept PR. If there is no patch, make it clear who will create a patch, or whether a volunteer to help with implementation is needed. ===== Implementation ===== After the RFC is implemented, this section should contain: - the version(s) it was merged into - a link to the git commit(s) - a link to the PHP manual entry for the feature ===== References ===== Links to external references, discussions, or RFCs. ===== 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.