Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127955 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 DA4AD1A00BC for ; Mon, 7 Jul 2025 23:55:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1751932389; bh=7X2ZHoZNAmV7/WLnrjc7D2DR6dq+6jrAOeovHHmSTe4=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=LrucdtszXeWsMnv91yrWnW42KLNW4Ngi9yHqGNEHlyh1ZlXdXofDTTuTvacytQLjz rrK7ijOLQtMECbJyKRMPMDlKK1TlLAZoHKvDjHvkwmtW0Yx9a9LgBdsFZinmBTlc/o 2on5vojtmX8uIAkEPMbARc+jJZzQBI+oarZdb1La5I3sLJDK+4nvi4Nphmz8EQguOq mpqUmoIj+cp7gTku+ozhxeZIChHm6nJxVn5sD7WGxukD2JwfnlGFZt/j1q8rXiMQjR 4fRu8KwxsYVT7k5rW3j4JGg8sRStydl3iVCibmmRIDlTa2Ly8FFIDQVGXy8fIfPlPZ 1+8RTmnDs5KUA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 0A91E1805CC for ; Mon, 7 Jul 2025 23:53:09 +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-f175.google.com (mail-yw1-f175.google.com [209.85.128.175]) (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 23:52:58 +0000 (UTC) Received: by mail-yw1-f175.google.com with SMTP id 00721157ae682-70e5d953c0bso39432957b3.1 for ; Mon, 07 Jul 2025 16:54:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech.net; s=google; t=1751932488; x=1752537288; 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=smV6ync2mewkkfiA25UkDIPymLl/9E6bZybt02XwQa4=; b=Cu7E2rkcJMvd0B/pjjXLBUurI2eHfRn8nijqkEmYNPWBXTttFxzGGmUIhcjyAHinUh QH0fbZPCsdk60iX0pHO6zQECqvV5Oh+78tgtgKcMz8EMljmxsKDqIT+Ht7xG6Rh532MX mPasEsrIVZ3+ioFTuymkNdnBQ+W00KLkemUcu8lkfbjsKtm/EQW9vFSeTqyp/9CPeS5K K6DskMVEVCFEqhku2O4Z4J9z87PG6tJzz2ohxf/5ohnQuuCD50HMN8cUaDVRZ5v/AO1J bSBIgEU/YF71UXicc5UpVEL+CXj7HaVcCv1kPN76sRl88uY4lmxEtK2BvwrAqeOxr58P iOuQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751932488; x=1752537288; 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=smV6ync2mewkkfiA25UkDIPymLl/9E6bZybt02XwQa4=; b=IosqebOynLp5437+KKMrkrILr33UZVKNe8xevgpbfMp8wtTrbAON7uqPFCCnCR2Htd bWdI9bhJHGXZWjoU6TwvrOM06LMUMwPrTjn/5F3r2ZwG5Lfjkvzs2TzTPbce6F7afRIf Hf7QRjenh57b6aYkxABoMZRSvA8NULEKhJrlvKA/ECRpcfNHfsKX3y+C3tgpHyA+rNbX 7lW7Y0lS7IanuaQI95SsdNAULgvMEeYzAG58oHq+XdVDfxj5PTx6wJSA0X8VunBo5xRA ASwNNpDsAUqDhDR2IQCj6Nd1mHlw2SIwp9UObX/NKEY1fthEoHyoljyaAPnIhg9vS+Et T66w== X-Gm-Message-State: AOJu0Yw9iEMdhCF6s/F4xpw164XpIrW8m8QXGVLT1Bi7D8YRoTXNgkDB 81fN8H32RZDndUthGbGI+om5xcPyxyzvWHo5pZevjWtzFU7vv9rn1Nrt/u6DQ+bp/O0Wgp48ON8 j4h0EQvZrEUDMqxxlMsE3NV63S6DLERyxKfyN6M7azrjpQ+4n/vW7G0bwfw== X-Gm-Gg: ASbGncuo0U5X+W6OktKZNXJDMrnLU+VUJvQs2vSItEACRJM1PGpLJfz4jumfPng/NBL aOL4P55MQ9oyiw44bWWsxkE9o0mtB7Vgj5DUza263LB6wGJxH9hQkf/iwInqDRJwuIMGWx/NLP7 HJ1yRiG4+rhEfjnKuZrpQZJis1SfaEQntPXpt8ix1rM/qM1Abvu2TcZoe1CC/JbMyTmIjFO1pvL qn1 X-Google-Smtp-Source: AGHT+IHKGJoqv9bfmIFXhJ48A+qDQUS+uthC0FML2oF0jU58w96i65nGUTDc4Ls38eSNr51dns57buJGmHRorGzwnJ4= X-Received: by 2002:a05:690c:3502:b0:711:6419:5ce1 with SMTP id 00721157ae682-7179e43ae4dmr23243477b3.31.1751932488590; Mon, 07 Jul 2025 16:54:48 -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> <926ca0e2-13f4-47fc-aec3-f7fa265cfdb5@app.fastmail.com> In-Reply-To: Date: Tue, 8 Jul 2025 01:54:37 +0200 X-Gm-Features: Ac12FXyTgyBpcUHWnW1bAHScdBQHm5IoFqSuEnmfXoL11Ml9m0hrM40cRXvbpDg 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 Mon, 7 Jul 2025 at 15:57, Larry Garfield wrote: > > On Fri, Jul 4, 2025, at 6:50 PM, Andreas Hennings wrote: > > On Thu, 3 Jul 2025 at 23:44, Larry Garfield wr= ote: > >> > >> 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 value= s, > >> > which are then replaced in that separate method. > >> > (I call that a "setter", but only because it behaves like one from t= he > >> > 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 m= y case, it's not just reflection; there's a whole bunch of other things tha= t 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 be= nefits. > > > > I did play around with dedicated interfaces for attributes per applicat= ion. > > 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 al= l, well, AttributeUtils interfaces. You use them to opt-in to specific AU = functionality. AU is framework agnostic, but the interface are naturally li= brary-specific. I may have be wrong on this one. What I think to understand now is that your library allows e.g. your class attribute to know about attributes on other symbols (e.g. properties) in the same class. This is ok, but other libraries might want to do these mechanics outside of the attribute. > > >> 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 d= o 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 m= ostly 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 p= eriod 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 consist= ent. > > > 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. If you have a box with a cat in it, it can be useful to know whether that cat is dead or alive, and the sooner you know it the better. All I do here is advertise for having the target reflector available in the constructor. There is some bad taste of global state, but I don't see relevant scenarios where it would manifest as something bad, with that proposed self-clearing mechanism. This said, I can easily imagine more than one solution to co-exist. For many cases, the method injection will be just fine. -- Andreas > > >> While passing the reflection target in is one such multi-step case, th= ere 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 cla= ss; 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 th= e 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