On Thu, 3 Jul 2025 at 19:17, Stephen Reay <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.

>
> 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

Reply via email to