Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127927 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 88A461A00BC for ; Mon, 7 Jul 2025 13:56:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1751896473; bh=IBcGhg2T/SGVtyG78sp+QoXz9eHnD4jpCIU2bfNF/zs=; h=Date:From:To:In-Reply-To:References:Subject:From; b=DRaQCvYe0YXeJ13SGYf2qgJREunpAODlhHZnP7nhKn8uMLeqX5YJyOtDgF0AgUJeb eht+s9C4a43cXKSeL+rwx0mjwpMxZ60y3WoGufriszWk80vjQOmYJpTx6eqn3x5f37 CnjXTVqDeaSrF6mNEDUdlfRCOrAl16oD3oTkY2pBGfshxnZI0wi6cVQLsVzLpC8gD5 WWkWHThCL75q0+AnewrZbwf32rp7Yp+hRTRQDL/OSGi0vvylREb2r9mAZEPz7BCRPD xAouxc25Axj73h9WFQJ9dt2chxo1jmw5F/FgjAhTov+gv/O173uGlX7xw/R/wn+Ukc GiuwEjfHFjg+Q== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 8CDE7180084 for ; Mon, 7 Jul 2025 13:54:32 +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,RCVD_IN_DNSWL_LOW, SPF_HELO_PASS,SPF_NONE 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 fout-a4-smtp.messagingengine.com (fout-a4-smtp.messagingengine.com [103.168.172.147]) (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 ; Mon, 7 Jul 2025 13:54:32 +0000 (UTC) Received: from phl-compute-10.internal (phl-compute-10.phl.internal [10.202.2.50]) by mailfout.phl.internal (Postfix) with ESMTP id 767C5EC0BB1 for ; Mon, 7 Jul 2025 09:56:22 -0400 (EDT) Received: from phl-imap-02 ([10.202.2.81]) by phl-compute-10.internal (MEProxy); Mon, 07 Jul 2025 09:56:22 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= garfieldtech.com; h=cc:content-transfer-encoding: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=fm2; t=1751896582; x=1751982982; bh=id9QZCOgjGSsZ76PSMuvt kpLicIsn7pgnQKH0aVTR5E=; b=DkadsSJEbDZSZcAgSJXC8cMKn+H60KaVtpp9H BNFIqn8PtKWMELMhskqZJ/oiDhlRtSm0AsFEAiPL1bVTCUk0ASHa9yuD1bvUPP7U x9mr53Dtt/qZgSqHL8XFVovgwY/cNhjpu1BTFujh4vpz1bzwGqUQ8EGLl8Q2kBnT BwHKT2oTlxLdSIX6kBp3JGYZRIXwhlbP/E2gHe4ICvokh1eVNyEhmSQoOpUWun/3 4qUpBmcs8XQvQqqr4L6tdJDz/r1NiGKE0p0uEn61OE96V+eNy77IclnfovrA+PP3 jyXu78aeGxnzoYovy1y652033L2Z6DKPFF0AI0qY4JyfN7aIw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding: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=1751896582; x=1751982982; bh=i d9QZCOgjGSsZ76PSMuvtkpLicIsn7pgnQKH0aVTR5E=; b=dHid7jMw1yC6DvUTS VQT6hjvLSi0abAaU2LNR3vn8v1y8cQLbR7zFrh1vmR4PJlKSXxgd7AaIZk4NWF59 VMsls7h38uRihFkVGCjkSpqBC6NxF5KhbBs9Yel88wQKUr7KGSawUlNGLMK54SIN C6Ocq2Xyj3ys22qJqDIfC0JIOqPh5lS4YsswMwhgZvRSExBqx+eE87FoXpwaowFw WL20Yh0ri1XQwQ3jl/+2uzHbA0l3p1gKy9JUJkN0BGvEFnQC1J9AAvtntRvMjLtD dZphIGFQZZtFY4fPHJzY1t7l9ev5YWFLy44mCME3Btw/skdv6d3STsJUfYtYMEd1 hjQzw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgdefudelkecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjug hrpefoggffhffvkfgjfhfutgfgsehtjeertdertddtnecuhfhrohhmpedfnfgrrhhrhicu ifgrrhhfihgvlhgufdcuoehlrghrrhihsehgrghrfhhivghlughtvggthhdrtghomheqne cuggftrfgrthhtvghrnhepudegvdelgfeugeehfeejteffudevleethfefgeejffffleeg tddtveekgeekudfgnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilh hfrhhomheplhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhmpdhnsggprhgtphht thhopedupdhmohguvgepshhmthhpohhuthdprhgtphhtthhopehinhhtvghrnhgrlhhsse hlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 0ED78700068; Mon, 7 Jul 2025 09:56:22 -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: T13a9b6ebd1dfd435 Date: Mon, 07 Jul 2025 08:55:38 -0500 To: "php internals" Message-ID: In-Reply-To: References: <1cfc477a-781f-40e9-9e37-dd748ef261be@app.fastmail.com> <926ca0e2-13f4-47fc-aec3-f7fa265cfdb5@app.fastmail.com> Subject: Re: [PHP-DEV] [RFC idea] Target-aware attributes Content-Type: text/plain Content-Transfer-Encoding: 7bit From: larry@garfieldtech.com ("Larry Garfield") On Fri, Jul 4, 2025, at 6:50 PM, Andreas Hennings wrote: > On Thu, 3 Jul 2025 at 23:44, Larry Garfield wrote: >> >> On Thu, Jul 3, 2025, at 12:49 PM, Andreas Hennings wrote: >> >> >> 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. >> > > I somehow missed this reply earlier... > >> I ran into the "completeness problem" in AttributeUtils as well. In my case, it's not just reflection; there's a whole bunch of other things that can be passed back to the attribute to give it more context. > > There is a trade-off how much logic should live in the attribute vs > the code that parses the attribute. > In symfony DI there is a pattern where you register a callback for an > attribute class, which would allow to make the attribute agnostic of > the application or framework details. > E.g. you could have the same attribute for different DI systems. > (Perhaps this is not the reason why symfony does it in this way.) > On the other hand, keeping logic in the attribute class also has its benefits. > > I did play around with dedicated interfaces for attributes per application. > I am not sure I like the overly generic interfaces I see in > AttributeUtils. But I would have to look deeper into it. Overly generic? I don't follow. The interfaces in AttributeUtils are all, well, AttributeUtils interfaces. You use them to opt-in to specific AU functionality. AU is framework agnostic, but the interface are naturally library-specific. >> My eventual solution was to also have a `Finalizable` interface that is guaranteed to be the last thing called, so the attribute can handle any last-minute defaults. I don't love it, but given the severe limitations of `readonly` it was the best I could do. (With aviz today, I could likely do better.) > > The attribute object does not have to be your final "collectable". > You can have an attribute with a method that returns something else. > In fact I strongly prefer to _not_ pass attribute instances around > everywhere, because they often contain clutter that becomes useless > after the initial discovery. Possibly, but my experience with AU is that it's quite convenient to have the attribute be the stored metadata. The methods on the attributes are mostly just to precompute some derived information (which could be a lot or a little). The stored/cached object is only the size of its properties, so as long as there's no set temporary properties there's no clutter to worry about. >> Basically, any time you have multi-step construction you will have a period where the state is potentially incomplete. > > It is not just about possible incompleteness, it is about being > deterministic as early as possible. It is always deterministic, as long as the order things happen is consistent. > E.g. if two people enter a room, it could make _some_ things much > easier if you know whether they are brother and sister, or a married > couple. ... I have no earthly idea what you just said. >> While passing the reflection target in is one such multi-step case, there are lots of others, so in advanced cases (most of what I do)... > > When looking at existing attributes in e.g. symfony or Drupal, or my > own experiments, I don't see the huge need to inject other things into > the attribute. > The reflector seems really the most commonly useful to me. > > E.g. an attribute on a method/function might have a parameter that is > optional or required depending on the method's return type. > Checking that in the constructor allows us to fail early, and makes > everything more deterministic. A key part of the design of AttributeUtils is that you always parse a class; if there are attributes on a method in that class, they get parsed and then passed into the class attribute. Similarly for properties, constants, etc. You can also have "sub attributes", which allow multiple attributes on the same target to be smushed together. All of those involve passing the attributes of the child-target to the parent-target's attribute. I agree, the reflector is the most critical and the part that would most benefit from help from the engine. But there are other valid use cases for multi-step build up. --Larry Garfield