Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127887 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id C9B4F1A00BE for ; Fri, 4 Jul 2025 22:11:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1751666988; bh=k7r96tIIM2xWMr4HgzfKs/CDjdkLfkXme2pjxYs1p/c=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=Zk32mNREnceBy8zOPVCdvkJeehon+j2xLLM2TY8DnUFvjC/eZlqD5nYQu0wAvABk8 KJqJqRFCCaTTD/KTyy0HVu5LlcWgCcJezGWuXdl9GMK+tuWC9ILLsSDat5CnfpSO2Q hLKEfRdKj2wWdLmDcP1VR+IyUgMvZIv2FOP2AWOe/EaPTrZnl4caNJQM+8HGfYYJ7P 9UvoY2Q7YwduajSs/rklGJpKQyZhuBtLpJMj+TgolLvU5dB0Hsl+0IK4i/K1mSACNG pJqdXRRJzM/14tJpqy3nk2peF7130FRKodqwoxBEOw7jt4fPxAajHWTs19JtPZewg1 lgzFJ2CqR+qpg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id EB772180537 for ; Fri, 4 Jul 2025 22:09:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Fri, 4 Jul 2025 22:09:46 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.phl.internal [10.202.2.45]) by mailfhigh.stl.internal (Postfix) with ESMTP id 0857C7A0133; Fri, 4 Jul 2025 18:11:38 -0400 (EDT) Received: from phl-imap-06 ([10.202.2.83]) by phl-compute-05.internal (MEProxy); Fri, 04 Jul 2025 18:11:38 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:cc:content-type:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1751667097; x= 1751753497; bh=4XgrbBg25A3H9PZGHmd3JIHp6uUYrAXpJlTGKQyRVco=; b=B fabogTNkZ2eBBDl64iKR6CXUYQDX0sFoquiP+paC4rdX4tGlJmYbMCaT3jOSq13j gE58ebeJmJPdTl9N1bqgc4eRKSyu3qsaJ1j7LkhRYll2MJWw2Sl9PefEcel2Smaf hOWxhMPCkLHPV9IRRO/TsIEH1wX6HeTgBqAPgrTjou4axyWsOIyLkmzTAa2QTD+F hEILm4gyAyVTFKN9Jss18yKwTLhRN+RxDpMBL9suhCHyf2lB053rFs2gyhXqeBGt DpN95xP3THNx1OBuzzzHDl6b0am864zYWag4r6cvyvrphgWdZ/y93+oJkEmjaUUh ydsd50qF81yQ/B6FUXvcQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; t= 1751667097; x=1751753497; bh=4XgrbBg25A3H9PZGHmd3JIHp6uUYrAXpJlT GKQyRVco=; b=lWRqgwjfn0P5AWsB54/qBNGdcI0pB+8hMD5i2Fh/R7tPdH4usQU oBB/C2vPJgkgqN1shH9yaStDcvhrUhvxBAKL2i0znFMcG+dCdnyQOs76xzA93sBG N9OmtQtqMOGW2OWj2Up2ChoY4WSWUIx/FMi5oJYYBAEGP25/P39iQltDDc+ZzaJ8 0LmMqGaLPbmWLSdVLGrwrUll3ia9qM3DNmCvE+KOd9xhM1E3XFYfNo0TOdBXKiPW ZmhSgMSpnE9007g/9qV6ORiZegQuS1icH0seuxmixDx4HNg5BvZHc0UilyFy9eCw SDcYyqxUEXkk6Ig9CCTEH2bU7NbdknH5jIw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddvgeefvdcutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenogfuuh hsphgvtghtffhomhgrihhnucdlgeelmdenucfjughrpefoggffhffvvefkjghfufgtsegr tderreertdejnecuhfhrohhmpedftfhosgcunfgrnhguvghrshdfuceorhhosgessghoth htlhgvugdrtghouggvsheqnecuggftrfgrthhtvghrnhepleevteehheelhefgveeiuddu ieduffekvdfhudevkeekgeeiveekgfelvdeigfdtnecuffhomhgrihhnpehgihhthhhusg drtghomhdpfehvgehlrdhorhhgnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghm pehmrghilhhfrhhomheprhhosgessghothhtlhgvugdrtghouggvshdpnhgspghrtghpth htohepgedpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtoheprghnughrvggrshesughq gihtvggthhdrnhgvthdprhgtphhtthhopehlrghrrhihsehgrghrfhhivghlughtvggthh drtghomhdprhgtphhtthhopehphhhpqdhlihhsthhssehkohgrlhgvphhhrghnthdrtgho mhdprhgtphhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 3EC77240008E; Fri, 4 Jul 2025 18:11:37 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: T8ffec5e1f43e82e3 Date: Sat, 05 Jul 2025 00:11:17 +0200 To: "Andreas Hennings" , "Stephen Reay" Cc: "Larry Garfield" , "php internals" Message-ID: <4839a478-9b65-4ac5-8444-ca286ddc72fd@app.fastmail.com> In-Reply-To: References: <1cfc477a-781f-40e9-9e37-dd748ef261be@app.fastmail.com> <20DACB49-3C84-4B23-98E2-35050D9EDAC9@koalephant.com> <3C4ED1AE-3E25-461D-8C3C-AF954499F905@koalephant.com> Subject: Re: [PHP-DEV] [RFC idea] Target-aware attributes Content-Type: multipart/alternative; boundary=c7f721f602964766a31e095596de52f6 From: rob@bottled.codes ("Rob Landers") --c7f721f602964766a31e095596de52f6 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Fri, Jul 4, 2025, at 17:21, Andreas Hennings wrote: > On Fri, 4 Jul 2025 at 06:30, Stephen Reay w= rote: > > > > > > > > On 4 Jul 2025, at 00:54, Andreas Hennings wrot= e: > > > > On Thu, 3 Jul 2025 at 19:17, Stephen Reay = wrote: > > > > > > > > > > > > Sent from my iPhone > > > > On 3 Jul 2025, at 23:40, Larry Garfield wro= te: > > > > =EF=BB=BFOn 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 (donqu= ixote)") > > > > ----- > > > > Primary proposal > > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > > > > 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 > > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > > > > In older discussions, it was suggested to provide the target reflect= or > > 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, becau= se > > the constructor does not have all the information. > > It may also prevent attribute classes from being stateless (depending > > how we define stateless). > > > > > > Userland implementations > > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D > > > > 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/FromReflecti= onClass.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 tha= t 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 ar= e 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 defa= ults for the attribute. > > > > However, I am very skeptical about a stateful global value as the so= lution. We've tried very hard to remove those from PHP, mostly successf= ully. 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 i= t was the only real option. Alternatively, I suppose core could use pro= perty setter injection (either a magically named property like $__reflec= tor, or a property that itself has an attribute on it, etc.). That woul= d allow it to be set before the constructor is called, and with property= hooks would allow processing either immediately or later in the constru= ctor. The downside here is that Attribute are, generally, serializable,= but a Reflection object is not. So if someone wanted a serializable at= tribute they would have to accept the property, use it, and then remembe= r to unset it at some point. That's clumsy. > > > > --Larry Garfield > > > > > > As someone that's written yet another userland "solution" for this p= roblem, I have an alternative solution, based somewhat on an internalise= d 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 imme= diately 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 existin= g discussion keeps referencing it as a "setter" - which I think from use= r land at least will generally be understood to mean setting a property = on an object - which then adapted into Larry's mention of specifically s= etting a property before the constructor is run. >=20 > I guess the main reason it "keeps" doing that is that an email is not > a wiki page that could be updated :) > I am happy to use different terminology in an RFC. > (with the current plan, it would only be mentioned under "alternatives > that were considered") > "method injection" seems fine. >=20 > But then how would we name such a method? > In Larry's library it is ->fromReflection(..), but this is something I > would typically use for static factories. > To me, ->setReflector() is still ok even if internally it is not a > setter. The method describes the contract, not what happens inside. > Another idea would be ->tellAboutReflector() or maybe ->injectTargetRe= flector()? >=20 > > > > I think it's a bad idea to reference this concept as "setting a prop= erty" - my understanding is that it's never a good idea to hang onto Ref= lector objects longer than absolutely necessary, so I don't think this f= eature should then result in people doing that due to the impression it = gives (i.e. if it was referred to as "setReflection()" or if th= e description for it is "allows an Attribute to store the Reflector targ= et it was declared on" etc) > > >=20 >=20 > -------- >=20 > Now, to resolve the controversial part of this discussion. >=20 > We could reduce the RFC to the uncontroversial part: > Provide a ReflectionAttribute->getTargetReflector(). >=20 > With this, the rest of the proposal can be implemented in userland > using debug_backtrace(). > https://3v4l.org/Ilrqm#vnull >=20 > #[Attribute] > class MyAttribute { > public function __construct() { > $target_reflector =3D > debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | > DEBUG_BACKTRACE_PROVIDE_OBJECT, 3)[1]['object']; > } > } >=20 > Yes it feels dirty, but now it is now longer something we have to > argue about in this list :) >=20 Why do we have to call a constructor before we initialize a property? ht= tps://3v4l.org/srdM6 You can do this in regular php, but it is even simpler in the engine. =E2=80=94 Rob --c7f721f602964766a31e095596de52f6 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable


On Fri, Jul 4, 2025, at 17:21, Andreas Hennings wrote:=
On Fri, 4 Jul = 2025 at 06:30, Stephen Reay <php-lists@koalephant.com> wrote:
>
>= ;
>
> On 4 Jul 2025, at 00:54, Andreas Henning= s <andreas@dqxtech.net>= wrote:
>
> On Thu, 3 Jul 2025 at 19:17, Steph= en Reay <php-lists@koalep= hant.com> wrote:
>
>
>
>
>
> Sent from my iPhone
= >
> On 3 Jul 2025, at 23:40, Larry Garfield <larry@garfieldtech.com> wrote:
>
> =EF=BB=BFOn Wed, Jul 2, 2025, at 5:26 PM, An= dreas Hennings wrote:
>
> This topic was discu= ssed in the past as "Declaration-aware
> attributes", and m= entioned in the discussion to "Amendments to
> Attributes".=
> I now want to propose a close-to-RFC iteration of this.<= /div>
> (I don't have RFC Karma, my wiki account is "Andreas Henn= ings (donquixote)")
>
> -----
>
> Primary proposal
> =3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D
>
> I propose to introduce 3 new = methods on ReflectionAttribute.
>
> static Ref= lectionAttribute::getCurrentTargetReflector(): ?Reflector
>= Most of the time, this will return NULL.
> During the exec= ution of ReflectionAttribute->newInstance(), it will
> r= eturn the reflector of the symbol on which the attribute is found.
=
> (in other words, during
> $reflector->getAttri= butes()[$i]->newInstance(), it will return
> $reflector.= )
> During the execution of
> ReflectionAttrib= ute::invokeWithTargetAttribute($target, $callback), it
> wi= ll return $target.
> If the call stack contains multiple ca= lls to the above mentioned
> methods, only the closest/deep= est one counts.
> (This means that php needs to maintain a = stack of reflectors.)
>
>
> *snip= *
>
> Other alternatives
> =3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
&= gt;
> In older discussions, it was suggested to provide the= target reflector
> as a special constructor parameter.
> This is problematic because an attribute expression #[MyAttr= ibute('a',
> 'b', 'c')] expects to pass values to all the p= arameters.
>
> 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 cl= asses harder to write, because
> the constructor does not h= ave all the information.
> It may also prevent attribute cl= asses from being stateless (depending
> how we define state= less).
>
>
> Userland implementat= ions
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D
>
> One userland impleme= ntation 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
<= div>> https://github.com/Crell/AttributeUtil= s/blob/master/src/FromReflectionClass.php
>
&= gt;
> Hey, I know that guy! :-)
>
&g= t; Another userland implementation is in the
> 'ock/reflect= or-aware-attributes' package.
> This s= upports both a setter method and getting the target reflector
= > from the attribute constructor.
>
> The p= roblem with any userland implementation is that it only works if
> the attribute is instantiated (or processed) using that userland=
> library.
> Simply calling $reflector->ge= tAttributes()[0]->newInstance() would
> either return an= instance that is incomplete, or it would break, if
> the a= ttribute class expects access to its target.
>
&g= t;
> I am unsurprisingly in favor of finding a solution her= e, as there are innumerable cases where you need the reflectable that th= e attribute is on; the most common for me is using the name/type of a pr= operty as defaults for the attribute.
>
> Howe= ver, I am very skeptical about a stateful global value as the solution.&= nbsp; We've tried very hard to remove those from PHP, mostly successfull= y.  Adding another one back in feels like a major step backwards, a= nd 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 pro= perty setter injection (either a magically named property like $__reflec= tor, 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 pro= perty hooks would allow processing either immediately or later in the co= nstructor.  The downside here is that Attribute are, generally, ser= ializable, but a Reflection object is not.  So if someone wanted a = serializable attribute they would have to accept the property, use it, a= nd then remember to unset it at some point.  That's clumsy.
>
> --Larry Garfield
>
>
> As someone that's written yet another userland "solution" f= or this problem, I have an alternative solution, based somewhat on an in= ternalised  concept of "never store Reflectors".
>
> Introduce an interface "ReflectorAttribute" (bike shedding t= o come); which accepts a single Reflector argument.
>
=
> If the attribute implements the interface, the method is calle= d immediately following instantiation.
>
>
> Yep, this is the "method injection" mentioned by Larry, or w= hat 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 f= rom user land at least will generally be understood to mean setting a pr= operty on an object - which then adapted into Larry's mention of specifi= cally setting a property before the constructor is run.

I guess the main reason it "keeps" doing that is that an email = is not
a wiki page that could be updated :)
I am hap= py to use different terminology in an RFC.
(with the current p= lan, it would only be mentioned under "alternatives
that were = considered")
"method injection" seems fine.

But then how would we name such a method?
In Larry's li= brary it is ->fromReflection(..), but this is something I
w= ould typically use for static factories.
To me, ->setReflec= tor() is still ok even if internally it is not a
setter. The m= ethod describes the contract, not what happens inside.
Another= idea would be ->tellAboutReflector() or maybe ->injectTargetRefle= ctor()?

>
> I think it's a bad = idea to reference this concept as "setting a property" - my understandin= g is that it's never a good idea to hang onto Reflector objects longer t= han absolutely necessary, so I don't think this feature should then resu= lt in people doing that due to the impression it gives (i.e. if it was r= eferred to as "setReflection<subtype>()" or if the description for= it is "allows an Attribute to store the Reflector target it was declare= d on" etc)
>


------= --

Now, to resolve the controversial part of th= is discussion.

We could reduce the RFC to the u= ncontroversial part:
Provide a ReflectionAttribute->getTarg= etReflector().

With this, the rest of the propo= sal can be implemented in userland
using debug_backtrace().

#[Attribute]
class MyAttrib= ute {
    public function __construct() {
=
        $target_reflector =3D
debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS |
DEBUG_BA= CKTRACE_PROVIDE_OBJECT, 3)[1]['object'];
    }<= /div>
}

Yes it feels dirty, but now it is n= ow longer something we have to
argue about in this list :)


Why do we have to call= a constructor before we initialize a property? https://3v4l.org/srdM6

You = can do this in regular php, but it is even simpler in the engine.
<= div>
=E2=80=94 Rob
--c7f721f602964766a31e095596de52f6--