> On 4 Jul 2025, at 00:54, Andreas Hennings <andr...@dqxtech.net> wrote: > > On Thu, 3 Jul 2025 at 19:17, Stephen Reay <php-li...@koalephant.com > <mailto:php-li...@koalephant.com>> wrote: >> >> >> >> >> Sent from my iPhone >>> On 3 Jul 2025, at 23:40, Larry Garfield <la...@garfieldtech.com> wrote: >>> >>> On Wed, Jul 2, 2025, at 5:26 PM, Andreas Hennings wrote: >>>> This topic was discussed in the past as "Declaration-aware >>>> attributes", and mentioned in the discussion to "Amendments to >>>> Attributes". >>>> I now want to propose a close-to-RFC iteration of this. >>>> (I don't have RFC Karma, my wiki account is "Andreas Hennings >>>> (donquixote)") >>>> >>>> ----- >>>> >>>> Primary proposal >>>> ============= >>>> >>>> I propose to introduce 3 new methods on ReflectionAttribute. >>>> >>>> static ReflectionAttribute::getCurrentTargetReflector(): ?Reflector >>>> Most of the time, this will return NULL. >>>> During the execution of ReflectionAttribute->newInstance(), it will >>>> return the reflector of the symbol on which the attribute is found. >>>> (in other words, during >>>> $reflector->getAttributes()[$i]->newInstance(), it will return >>>> $reflector.) >>>> During the execution of >>>> ReflectionAttribute::invokeWithTargetAttribute($target, $callback), it >>>> will return $target. >>>> If the call stack contains multiple calls to the above mentioned >>>> methods, only the closest/deepest one counts. >>>> (This means that php needs to maintain a stack of reflectors.) >>> >>> *snip* >>> >>>> Other alternatives >>>> ====================== >>>> >>>> In older discussions, it was suggested to provide the target reflector >>>> as a special constructor parameter. >>>> This is problematic because an attribute expression #[MyAttribute('a', >>>> 'b', 'c')] expects to pass values to all the parameters. >>>> >>>> Another idea was to provide the target reflector through a kind of >>>> setter method on the attribute class. >>>> This can work, but it makes attribute classes harder to write, because >>>> the constructor does not have all the information. >>>> It may also prevent attribute classes from being stateless (depending >>>> how we define stateless). >>>> >>>> >>>> Userland implementations >>>> ========================= >>>> >>>> One userland implementation that was mentioned in this list in the >>>> past is in the 'crell/attributeutils' package. >>>> This one uses a kind of setter injection for the target reflector. >>>> See >>>> https://github.com/Crell/AttributeUtils/blob/master/src/FromReflectionClass.php >>> >>> Hey, I know that guy! :-) >>> >>>> Another userland implementation is in the >>>> 'ock/reflector-aware-attributes' package. >>>> https://github.com/ock-php/reflector-aware-attributes (I created that one) >>>> This supports both a setter method and getting the target reflector >>>> from the attribute constructor. >>>> >>>> The problem with any userland implementation is that it only works if >>>> the attribute is instantiated (or processed) using that userland >>>> library. >>>> Simply calling $reflector->getAttributes()[0]->newInstance() would >>>> either return an instance that is incomplete, or it would break, if >>>> the attribute class expects access to its target. >>> >>> I am unsurprisingly in favor of finding a solution here, as there are >>> innumerable cases where you need the reflectable that the attribute is on; >>> the most common for me is using the name/type of a property as defaults for >>> the attribute. >>> >>> However, I am very skeptical about a stateful global value as the solution. >>> We've tried very hard to remove those from PHP, mostly successfully. >>> Adding another one back in feels like a major step backwards, and a great >>> place for weird bugs to hide. >>> >>> A setter method injection is what I did in AttributeUtils, because it was >>> the only real option. Alternatively, I suppose core could use property >>> setter injection (either a magically named property like $__reflector, or a >>> property that itself has an attribute on it, etc.). That would allow it to >>> be set before the constructor is called, and with property hooks would >>> allow processing either immediately or later in the constructor. The >>> downside here is that Attribute are, generally, serializable, but a >>> Reflection object is not. So if someone wanted a serializable attribute >>> they would have to accept the property, use it, and then remember to unset >>> it at some point. That's clumsy. >>> >>> --Larry Garfield >>> >> >> As someone that's written yet another userland "solution" for this problem, >> I have an alternative solution, based somewhat on an internalised concept >> of "never store Reflectors". >> >> Introduce an interface "ReflectorAttribute" (bike shedding to come); which >> accepts a single Reflector argument. >> >> If the attribute implements the interface, the method is called immediately >> following instantiation. > > Yep, this is the "method injection" mentioned by Larry, or what I > referred to as "setter injection". > I have not seen your library, but I assume that's where it is going. >
Hi Andreas, I guess the key difference I wanted to highlight is that the existing discussion keeps referencing it as a "setter" - which I think from user land at least will generally be understood to mean setting a property on an object - which then adapted into Larry's mention of specifically setting a property before the constructor is run. I think it's a bad idea to reference this concept as "setting a property" - my understanding is that it's never a good idea to hang onto Reflector objects longer than absolutely necessary, so I don't think this feature should then result in people doing that due to the impression it gives (i.e. if it was referred to as "setReflection<subtype>()" or if the description for it is "allows an Attribute to store the Reflector target it was declared on" etc) Cheers Stephen >> >> Yes this means logic dependant on the reflector has to be delayed until the >> method is called. I think this is an acceptable payoff for the solution: it >> only applies to attributes that explicitly opt in to receive the Reflector, >> and it helps to not encourage storing the reflector in a property. > > yep, same tradeoff I mentioned in the other mail. > >> >> In theory I guess it could call a static named constructor, but the other >> arguments would have to be an array, which would end up being quite clunky >> if the goal is to derive default argument values from the reflector. >> >> I'm really looking forward to this feature, thanks for introducing this >> discussion/RFC! >> >> >> >> Cheers >> >> Stephen