Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127891 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 3305A1A00BC for ; Fri, 4 Jul 2025 22:48:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1751669182; bh=Ketv9scwnPkWDVJulbhqFoXMx9vNW99a1K3/Qk0ouS4=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=inscNlVy5IpWmk6LmsMoD0x98bGHwijGdyYuufPNCxt30qiXM+ZuC4ot8F6FGyQWH R6XO3qAVNA3GfnieeORuq4NtzX5UcnVxtG8GS8jWNCt/XnY97UXCquWkotny/3G7qR BLssTfHnkwgQB600/GkLqvZp1dcuU2e6SLIPAxo25Q4wUIukHrazI20jy8UU31aEP+ +9ELIsxNeHlQfMsGBlhAbNOmuPjz0QIaIwfIHaRK+I0DwUQLm8OGjsSyxtj2XYkrAy BIIfqWQystJg8VddgOsJAIZcahYPI7oApQdJJ5hWnp5eZd38htGCCuiQmfgaEuoq0f dxcvopiZU3f+w== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 8CF5418005D for ; Fri, 4 Jul 2025 22:46:20 +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-yb1-f180.google.com (mail-yb1-f180.google.com [209.85.219.180]) (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:46:10 +0000 (UTC) Received: by mail-yb1-f180.google.com with SMTP id 3f1490d57ef6-e85e06a7f63so1168520276.1 for ; Fri, 04 Jul 2025 15:48:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech.net; s=google; t=1751669281; x=1752274081; 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=vK+QymMY7X8nQCF1kAfG4D8n4O5wYe6KrWzk5qFMLEo=; b=TUF6yARnT6quaR10yKS2O/EY+8A8Xit5DvOUl5Ef9JCQWkJFdyLn4e6waEX5XE3BnL elWEMi9t+Wrq21/9TC6tAclFFRF8Cjd13u6+EaM+HypbFTLadPS4RocbKl2BEBubhXVg JUOllQRkYH+6pJZ9BMSGuJRhXn0WemWcD+a5CUqxFArMCRXVuulsKIFiS0J3VMufPxA3 LkAHY6Hx1ec3/d/ectTLTUMOH0KtBjITGFqEt/tfMmyH7Ki4995e6vWTZZ5R6B3aOZxl k4EkqhdnD8HYFZcOdE0KAS9uhRvG7qkp0EBOlew/8BGZ96c2Rxhr0uMBRP0Pn69+lj9Q GQYg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751669281; x=1752274081; 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=vK+QymMY7X8nQCF1kAfG4D8n4O5wYe6KrWzk5qFMLEo=; b=IyOALb1SRpYoJfqt4hxP8QXjkHwWsPaRIb3cWr9FWEoJfPm7AcxpG2q1V4yY85VV/z BgYY0yj/Rvhp/yqqXI1rNsRMsNu31k+DjkbONLYwsjDWIymSQyCEmLSH/rf6F9z3yKjL a9QPwp5KtUUaRqC5ob2eo2d4jTjW/2zJevTOvpi2sLBeAftuOVfvK7s1OweevhUhxmw0 TxeIGpaDwnU/PLc6dPzzU7KJlw3Sgp4pmsmlgcDWx1854CKH3+vRKW3K1IojcIB87Tq+ gxgsA2RPGkfHEFGkwBrUMRNkticgNskmFKeqMrFMqN+xHnrpOZT2oxI/5dNpR7p1YG/e 8znA== X-Forwarded-Encrypted: i=1; AJvYcCWM8LzNzMwTecEzhXDPmNwd3m116+1mUZqcv7Y83QM3vf3Q/TzlxkdrzGJLZZnM+FVZTllUSQVgPfI=@lists.php.net X-Gm-Message-State: AOJu0YxHu6oSZ+o+Vi89zz51vLP+5WGRb4g7iJyPszT30AwfBZeLWiOz NjWZn4d8Lk+Cld2pY+Vm0a01UOpK9njumbTcWbqM5pYbAAgn6jGg8OJfEILHjcg5lFSw/SK85em QnBx/JFUYnQoR0GaQMaNKCHmg04sNm89KJ8moIMcixpI/gQg4Uqb2DFBcHQ== X-Gm-Gg: ASbGncs2Q1mcaXCeba94vxuk3kSTip06uW5jCw1rdRVgsI2WiUEk207Tf4bGtZE9xh4 oCRjkrBoZHS53rRIo67xb6vZ9ShwUq9vxQzoo5Mmmdyi1gARDmdkrRLNaG80OOUo7dZdkidMO6c dGE503KzRk5s1n+xNqBCwvfYm+SkgWnijwXLL/gHoGMJdu5Mx1111n8tyHLWAR52uglNOz5iuVy rE= X-Google-Smtp-Source: AGHT+IE/VW82rWC2yZsuuz+FxudfO4r7TqC0I/zwBcSXFBJ+Z77vrjPe7K3UkanBNm9No2nI6HiOAE6ukbtZgfxbvQY= X-Received: by 2002:a05:6902:10c9:b0:e82:99:efaf with SMTP id 3f1490d57ef6-e899e176160mr4924129276.31.1751669281090; Fri, 04 Jul 2025 15:48:01 -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> <4839a478-9b65-4ac5-8444-ca286ddc72fd@app.fastmail.com> In-Reply-To: <4839a478-9b65-4ac5-8444-ca286ddc72fd@app.fastmail.com> Date: Sat, 5 Jul 2025 00:47:50 +0200 X-Gm-Features: Ac12FXxMlVNqRY0Ay4qqySJl_poy47SlKIBVYdRt7PjWVVs2mZK42bNPvghYTIo Message-ID: Subject: Re: [PHP-DEV] [RFC idea] Target-aware attributes To: Rob Landers Cc: Stephen Reay , Larry Garfield , php internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable From: andreas@dqxtech.net (Andreas Hennings) On Sat, 5 Jul 2025 at 00:11, Rob Landers wrote: > > > > On Fri, Jul 4, 2025, at 17:21, Andreas Hennings wrote: > > On Fri, 4 Jul 2025 at 06:30, Stephen Reay wrot= e: > > > > > > > > On 4 Jul 2025, at 00:54, Andreas Hennings wrote: > > > > On Thu, 3 Jul 2025 at 19:17, Stephen Reay wr= ote: > > > > > > > > > > > > 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 (donquixo= te)") > > > > ----- > > > > 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/FromReflectionC= lass.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 o= ne) > > 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 i= nnumerable cases where you need the reflectable that the attribute is on; t= he 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 solut= ion. 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 w= as 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 t= o be set before the constructor is called, and with property hooks would al= low processing either immediately or later in the constructor. The downsid= e here is that Attribute are, generally, serializable, but a Reflection obj= ect 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 prob= lem, I have an alternative solution, based somewhat on an internalised con= cept of "never store Reflectors". > > > > Introduce an interface "ReflectorAttribute" (bike shedding to come); wh= ich accepts a single Reflector argument. > > > > If the attribute implements the interface, the method is called immedia= tely 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 d= iscussion 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 obj= ect - which then adapted into Larry's mention of specifically setting a pro= perty 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 ->injectTargetRefle= ctor()? > > > > > I think it's a bad idea to reference this concept as "setting a propert= y" - 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 sh= ould 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 f= or it is "allows an Attribute to store the Reflector target it was declared= on" 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 :) > > > Why do we have to call a constructor before we initialize a property? htt= ps://3v4l.org/srdM6 > > You can do this in regular php, but it is even simpler in the engine. > > =E2=80=94 Rob This is interesting, but I still add some logistical clutter to the attribute class. It does bring some advantage over post-construction method injection, because you can do the logistics in a trait, and then in the constructor you have all the information available. But, even if that part is hidden away in the engine, now we have a property that we probably want to unset later. -- Andreas