Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127867 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 4F0F21A00BC for ; Thu, 3 Jul 2025 17:50:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1751564894; bh=rAc8V4O4+VWI/qnlPt5L0hVLK0lw1paUk0vHMLnWahI=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=P/M4G1Bs3/z/u/l9KjHvU9yVs+dhSvIU4cuzOWT3pBuvVrT+unYdtkV4EVaKQ0lqa 0tfRRjfkJVBGBK8laaRXrNs1UbDPt8qX0pUu1Tvqp8GStIBBZGZdHa/nr/3hL1OtNc yZsUDtX/NHXUqFHDY4u0SOAMjr43W5OtFyasJ13zo35RcVtKGFBHzfz6Sn0yn/N8Fq Ci4urnyDJ/u9uv8fHHBaPIlOoKAZ18qm2oWoUCRG9hTkf49I8NsfgXqAvRvXChyko4 5hI1HSjuEAL7tgVZn1lHSDhtRHEBW1fX4vuso7MD8qS22wIC/4ooFWS0S15KLZodHX XMzWJcp3XDw+g== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 711F3180589 for ; Thu, 3 Jul 2025 17:48:13 +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-f170.google.com (mail-yw1-f170.google.com [209.85.128.170]) (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 ; Thu, 3 Jul 2025 17:48:13 +0000 (UTC) Received: by mail-yw1-f170.google.com with SMTP id 00721157ae682-70e3980757bso1793227b3.1 for ; Thu, 03 Jul 2025 10:50:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech.net; s=google; t=1751565005; x=1752169805; 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=1Tuxdhscsqgc2NbeJv8jSuoLfHDwfnQ585m+G/ccVsw=; b=giyh0GBMR7etMtDN8VDI4m3DThQ+7KbcYIPt9TI8+irJkigb9hdOtBxOv0UH0U184k 4jxfvp/Z0Km/zUOZ7Xa7RzAWRgbtxODOJbQ0R+VPzjkaKLlDLkdv7nHs8sBV4j08ki+5 7VzP/L6CrjHwMyEqaRezR/QdUFrHUGEMLZS/EiHVHmVMYKcHcbasE2pQOrZR3D7+EFO9 EakmYE6o4wYA6kaJWnY8S/kHLDdvD3LF/VJqOYJ26v6HOuz3bSz7A+6c7xk9ESzC7vMc xMWChm/Qwt4SUFl0/dk3kwUCqwPx1AWazDYZ6z00GpCW+2Eu/x6vlUgZwMEDpUfMBxeD u0+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751565005; x=1752169805; 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=1Tuxdhscsqgc2NbeJv8jSuoLfHDwfnQ585m+G/ccVsw=; b=bbdtYJlxcBkhaGU5C0AyFZyuRFY27qW485WUypYETqxTrdDrv1gFk0/HOEx5h0PKp2 cZWNoqQXbnWAJsXm+G3UVc2nKEjrgndKKzFfItfU+r6i6+WXqdIqZgsMQqR8T9JYX0Mm +63/QLfnjTb7EHA+ILu6mw51eI8KDz7btLLk6XH0MhJG4td5nuPBhjFIi5OrFPJEnxdP lgRMAFv+i2RLDm7mg+SVYNCMk9pWW9XtSRFptLRbpQBACyFRqrmNSAFGGvh4qhlGzueJ 1o/YAAkZY1a5ORCjb9mNLAluVCB/KwexZ+42RGar2ZIycbfGLiOg0NyFWKvArhaJHzWb tYXQ== X-Gm-Message-State: AOJu0YzqR/2XGYaEpez45kItjlTbQq1H0WqEPzaOHyCk+0E0kdLCgZBc EbPNg19yOcfgaIc7upL1c2EbZ85xxXSqAe/mkMjQe2AZ/L19wmT411WnLMyOYYu6Em/KBQOYOzh A105cSA05l84KRWCnev+uab/VYZRUahOjUevs0Tcsn+DHZc7P4/7v3RLtH2cl X-Gm-Gg: ASbGncst8CIJnG7KAbAvQgvbHIqHaD4aYMzZWsaDnyeqH9qSMF59pKcg3hKHXxd71sj IYZCp47I1y0xXOb1npiL6LXPLeSjjBqeE/YB/nnylhYE8QNfkmGTROCU+LGG45D02buiYFCtxkQ g5kP0DqB5THPxYoZWT46wgVwVjSllD7LkxJ/IeXWE+bHGshMOD/W4lO5eaICrv5ezWjBN5YhuFy ddy X-Google-Smtp-Source: AGHT+IF8Q1mrGfUQI/f/IITtbV/Es9nIsuxrsc9xk2LSCcxwQSIxMyvfK5EAYO5T8gvIc2xSal8DW1SwJoOH3nek4nc= X-Received: by 2002:a05:690c:5207:10b0:712:ca60:c5fd with SMTP id 00721157ae682-716672102bfmr1338347b3.1.1751565004661; Thu, 03 Jul 2025 10:50:04 -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> In-Reply-To: <1cfc477a-781f-40e9-9e37-dd748ef261be@app.fastmail.com> Date: Thu, 3 Jul 2025 19:49:53 +0200 X-Gm-Features: Ac12FXzVgJ7CjLOaFjMl2IQnBPrHv83zG4KvFrZZNh3PGxvfENFpCri9D_kj_yo Message-ID: Subject: Re: [PHP-DEV] [RFC idea] Target-aware attributes To: Larry Garfield Cc: php internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable From: andreas@dqxtech.net (Andreas Hennings) On Thu, 3 Jul 2025 at 18:29, Larry Garfield 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 (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 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. Of course you would say that :) And I might normally agree. In this case I find the tradeoff to be acceptable, when compared to the alternatives, and when considering the most common ways this would be used. Typically this would be called in a way similar to func_get_args() or to using the $this pointer. A typical global state problem is with set_error_handler() and restore_error_handler(). Here you need to work with try/finally to fully avoid side effects. In the current proposal, that try/finally is already built-in. In general, this state will be the same before and after any operation. (I have not fully thought about generators and fibers, but I don't see this becoming a problem.) One benefit of the current proposal is that it does not introduce new language features, but really quite boring static methods and properties. > > A setter method injection is what I did in AttributeUtils, because it was= the only real option. In my experience, this alternative leads to more complexity in attribute classes. The constructor needs to populate the attribute with temporary values, which are then replaced in that separate method. (I call that a "setter", but only because it behaves like one from the outside, internally we usually don't want to store the reflector). Also, we can never be fully sure whether an attribute instance is "complete" or not. If the attribute instance is coming from $reflection_attribute->newInstance(), we can be confident that the ->injectReflector() or has been called. But if the attribute was instantiated manually with "new ...", it could as well be incomplete. With ReflectionAttribute::getCurrentTargetReflector(), we can fail early in the attribute constructor, by throwing on NULL. > Alternatively, I suppose core could use property setter injection (eithe= r a magically named property like $__reflector, or a property that itself h= as an attribute on it, etc.). That would allow it to be set before the con= structor is called, and with property hooks would allow processing either i= mmediately or later in the constructor. The downside here is that Attribut= e are, generally, serializable, but a Reflection object is not. So if some= one 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. Exactly. -- Andreas > > --Larry Garfield