Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127879 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 251031A00BC for ; Fri, 4 Jul 2025 15:21:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1751642404; bh=1UTCbZYlvDNR+4FlguUQRRrUS5KHCRWSskmgEtpUmJ4=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=J/oGsA8S/dY3/P5QHc7yB/SJWOrnWZbn9pGYqVLhpR1Sl6z77/sWwApJJZw8buuZq YZCOGmJHGW7FKhsMaQ5N4Byv4+MnsG0td2t5V4CplteyG8yMVM0AFhJInu+7I9v05w FJq3DbOYr9HSuar5zowgb5ldENVAdl61m+2tHaI1Gy/sccd4dvwJXyLCTo8ko2huS6 bNoqq3/8EK5ZTTdj6ikNl2x0+a1mJ2KCjALsuPW6xxrzomItSTzmPdxEEYY+g9b+qq RPmmsSU9CaHUVrORXYP8CbmKgS/GpX238UHO5EzWBYd/p7GabpZ5FDXMkzIRCtLUm4 cGHGFBKWNa+Dg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id CCAC018002F for ; Fri, 4 Jul 2025 15:20:03 +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=-3.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,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 mail-yw1-f171.google.com (mail-yw1-f171.google.com [209.85.128.171]) (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 15:20:03 +0000 (UTC) Received: by mail-yw1-f171.google.com with SMTP id 00721157ae682-70e1d8c2dc2so9087867b3.3 for ; Fri, 04 Jul 2025 08:21:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech.net; s=google; t=1751642515; x=1752247315; darn=lists.php.net; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=6Xm5FaMmWc10r5o2+h7OtnB67aglT+UXs2bjKQo9WAY=; b=lXB2pJYAgdpmQKpzY/BOIakFVmVjkfroP4N/Z9X1MW7cI6vWp0R7HvDnjw4TgsUGAI EiqSbGnERVMxDPECsB+0mtRUgM2TT/3fCik7hnlEyCkMtPa5Lob/r7HUZYm9r6EH7BSt GGqh5pmm36KmulpDWn88kZk5eXWp9o0Oo5/3x4qEzUr59ttMGdJ8Ku+hl3owiyYyjHHk ExleRIS1DicHn2rEdtat5HuRJIH/DXABPbNWvaWe3ubiyMU5wiS1w6tPoWyMcqBZhhfw TdZ8lckKumqONuZKX4jdCDPfRqSqz0XFeRci0ju6AG4Hr1CSGZOQ92pViAB5V8SHS5pr Voeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751642515; x=1752247315; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6Xm5FaMmWc10r5o2+h7OtnB67aglT+UXs2bjKQo9WAY=; b=KlZVdtJWc9BNwIs4qzkUs3gHYMBFfWSPuv8NoTgXvDZprBgrCeY5Ab+ViY9YOcA0g6 2UHaK/0PB1ulxdmkHn9Mg7WGB4J4eHpzSSsFLRbRw5P7H0KfacF8H2vYISwPBpk/5b/1 GIJJCEfcCuXgrC9Qvd7H4ETP2WEYJXJwL7IXbjQRTE+C/5OEepK8iTmMqmh4hGFRdtu7 jr1ch4wBxGgZYfod10DubLqgHi109RLD8AXBX2md2VKXNoUXqPha3HEDf8uaZQruamDf Gp/t8XzVOiYNXCfH/HCOzHSqR95aXT5cwQ+wbbX2t+e+tHaoHsJuruPeDplDPw/SbxmB ugBA== X-Forwarded-Encrypted: i=1; AJvYcCUT+rtc+kqAfoRX8amp6uo3IL0/bmOqNN1uganGQ7q6zwpmNexmggMMKQOIMqbaPO/UvUnuoi20Hak=@lists.php.net X-Gm-Message-State: AOJu0YwuBCDEm+I54N4cySwv7NiOocFY8CvE1YsrYHh4VeF+SRTdZnyP DDxFx4vzMoUjF8bJOGP1XEKUftUu6+4G7Kfk43ZymC8urhKfjLhemNgtG4tjrGDxtwmyjbnmwcx +T3zrV4hbOqk8X6f0zXa6Z4MztX2dxXFG/udujDZH2e9ZGqDE0rIzaB7UNOKf X-Gm-Gg: ASbGncuYLFP2knjtiPFGjzc6hloKQTP2RQ3d+polUDdoluOH85w9J0LcDDQK51f71Vs S4KOvokGOGyYIFLhHweVaKWjSkQMrzyfXVnvTDkiTCRqwJxLtnI1O9VuU1mkakpsGgRnM5yRyXz 7KSD4cVXlT0KBhQDKQjddVZ0t0GKa193EiknaqzjIIS4agi2/ydcPxJNSZhezv5G4sl3GK7Tjy/ os= X-Google-Smtp-Source: AGHT+IGMN+r6ZrA1b3YNQ2IJXzGr3L62U4d2ANuueE/uzPxoK6uOrCr2PdKsYHTtoeitq544TFTsaQBlX/zLAfh4bww= X-Received: by 2002:a05:690c:670f:b0:70d:f47a:7e40 with SMTP id 00721157ae682-7166b60a627mr33662367b3.16.1751642514626; Fri, 04 Jul 2025 08:21:54 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: <1cfc477a-781f-40e9-9e37-dd748ef261be@app.fastmail.com> <20DACB49-3C84-4B23-98E2-35050D9EDAC9@koalephant.com> <3C4ED1AE-3E25-461D-8C3C-AF954499F905@koalephant.com> In-Reply-To: <3C4ED1AE-3E25-461D-8C3C-AF954499F905@koalephant.com> Date: Fri, 4 Jul 2025 17:21:43 +0200 X-Gm-Features: Ac12FXy7CBTYPICZ7WnKU7B5i5s9eFspOFbiboNpRPfjrwSSvAgOaquKnV_I7ho Message-ID: Subject: Re: [PHP-DEV] [RFC idea] Target-aware attributes To: Stephen Reay Cc: Larry Garfield , php internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable From: andreas@dqxtech.net (Andreas Hennings) On Fri, 4 Jul 2025 at 06:30, Stephen Reay wrote: > > > > On 4 Jul 2025, at 00:54, Andreas Hennings wrote: > > On Thu, 3 Jul 2025 at 19:17, Stephen Reay wrot= e: > > > > > > Sent from my iPhone > > On 3 Jul 2025, at 23:40, Larry Garfield wrote: > > =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 (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 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 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 > =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/FromReflectionCla= ss.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 inn= umerable 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 th= e attribute. > > However, I am very skeptical about a stateful global value as the solutio= n. We've tried very hard to remove those from PHP, mostly successfully. A= dding another one back in feels like a major step backwards, and a great pl= ace 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 se= tter injection (either a magically named property like $__reflector, or a p= roperty 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 allo= w processing either immediately or later in the constructor. The downside = here is that Attribute are, generally, serializable, but a Reflection objec= t 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 proble= m, I have an alternative solution, based somewhat on an internalised conce= pt of "never store Reflectors". > > Introduce an interface "ReflectorAttribute" (bike shedding to come); whic= h accepts a single Reflector argument. > > If the attribute implements the interface, the method is called immediate= ly 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 dis= cussion keeps referencing it as a "setter" - which I think from user land a= t least will generally be understood to mean setting a property on an objec= t - which then adapted into Larry's mention of specifically setting a prope= rty 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 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. 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 ->injectTargetReflect= or()? > > 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 o= bjects longer than absolutely necessary, so I don't think this feature shou= ld then result in people doing that due to the impression it gives (i.e. if= it was referred to as "setReflection()" or if the description for= it is "allows an Attribute to store the Reflector target it was declared o= n" etc) > -------- Now, to resolve the controversial part of this discussion. We could reduce the RFC to the uncontroversial part: Provide a ReflectionAttribute->getTargetReflector(). With this, the rest of the proposal can be implemented in userland using debug_backtrace(). https://3v4l.org/Ilrqm#vnull #[Attribute] class MyAttribute { public function __construct() { $target_reflector =3D debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT, 3)[1]['object']; } } Yes it feels dirty, but now it is now longer something we have to argue about in this list :)