Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127900 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 4ABB21A00BC for ; Fri, 4 Jul 2025 23:50:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1751672941; bh=3nm833dXiJfoTb5R5EzB+SocZNHLZPJhGlu+dJml06c=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=ipxMWqLlreSMnHiix50DDg0BBeGyBqqVXgXBm5DeW4/mjp0UrZvuRK6yj3+q9IHvs f2Ey8Twl5eoMGZvVANY2zDyjdovq7cJpHWmXZpZMNJLIGadcTRrwpK1syEMXiNTm7Y WjkMPcoAFhjnsbUrtaCde8lG8cgG637/RJ91YT2mn4J2G0SUenb54VBUNkzQmmnBs+ 4Ne3gSut6QlP1EQpd2q6d8BfAx7t4L22tcY8Vues5cPsD1JBtXLuCixMKLy4ooX6gb IJs77HtMpIq5k2NT6jcpT1yINtXBYvoTfwl26mmy/FdtzJNoWHoeU7RvkmiZv6JzNu NIlAYrED8Cavg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 49BDD1801D4 for ; Fri, 4 Jul 2025 23:49:00 +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-f169.google.com (mail-yb1-f169.google.com [209.85.219.169]) (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 23:49:00 +0000 (UTC) Received: by mail-yb1-f169.google.com with SMTP id 3f1490d57ef6-e81877c1ed6so2250977276.0 for ; Fri, 04 Jul 2025 16:50:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech.net; s=google; t=1751673051; x=1752277851; 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=uSv0RkPuGtlTjoo0LY58LcUiK169J6LnKC4BztD4RQo=; b=KGU8by5B+3CutQciCBpQZ+/cAC+aaR9jbz2spySQDmKNk1SbGkn3L8W+8ohcW+eksu /gRAMxK/59/1vnwq1r0/Oc4yquh6yGtZf3Ac52CIg2kRRRTgo3wphzARb8nQwN6oYi/E ihGG+w17iWBuGEGsUH9n2qriucjL+sgg+WzXdSWHt+OepIAgRmSDOoKc8zXDnFOBcigk 8qE5Chl6Cj6rn/44O0ZDInoLFieo7IG25J1argVphxEijwuHfEbJgWn1YrFRzE/5FiiG rTILi9K9z4hFImSwjlABg8RVUC231g32yJW3a5adlRVVFNiqw1WtQRqxsGif7vvJ5uEE Jg5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751673051; x=1752277851; 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=uSv0RkPuGtlTjoo0LY58LcUiK169J6LnKC4BztD4RQo=; b=XWvXV3UCqDfW4JBWnhCam9xFil64ETt/VnUfFmalsTrdhz4MMry7kT3RcOudoGlRUx bECPExaRahbZERBxsqnJQEd6CeQ3cLY89x1i9ZHRzgB8OIv0CHPKlM/AmQ2YSyQZDAJd wq9xGbXXqI8zis2qWUbOeULlR8qOUCpKuUhJE4eFBUts8Zd69VD6wcz+BeN2T11ycVbD TAHUzj1LlhvyvBOI8z/5gHD7QzlJE8HD9XML71xOqu5/Tv8HvZfL7gxEst3FqLgVbSIM UYozQhlLVOMayNqvF4WH3r5SkEfJUdUkQk2g/IGNdLkoQfQfRv/6IyRf2F72MzgXVDQQ Xy2g== X-Gm-Message-State: AOJu0YxDAzbcKUNh821Y5owi2HS26cpsQ6O0ghyteFvKv70Qqsgel9qt nhcTloj7rdjqll2KhTq09Z5oLQLBSaTaiR6yF5lfvbOsaRd+SGuU4eHfrcToeaOVRIsxoHTq3Ww tkmPzs6h6ciXcy9gTvqDnyos3YEhqG4+1YdCNyzggbsHEnK9tqUtDjsIg73YM X-Gm-Gg: ASbGnctufciYT2k+1W8QLaHpellbdgAGMxKnZHWeLsd/gecO1DTNoqfFqE0+SSqLcwB WfwuC7OR/xCWk0q6OBgcYx0acJu1qVr2RpTkcGugPZHcj+/6uXUOuvKiZPIn1Q6A6P4wAwQ2q+m 3tyvasBcpKNnWJUFrL0WHwT5v3Nqni24Y2aKyFsGL0aJ9xQTI1+cR1lBwdmfdaDsrE/TE/fPJTf 7A= X-Google-Smtp-Source: AGHT+IFS1rGAlZu/UsX5P5tzRGXSYq6ECdDHRWhXXir2T5mn1oLpnfEuQo4Bd6uXONo9n0acDZVorVgs3xZmM11Qy6o= X-Received: by 2002:a05:690c:4491:b0:70e:61b:afed with SMTP id 00721157ae682-71667e2423emr62362117b3.7.1751673051232; Fri, 04 Jul 2025 16:50:51 -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: <926ca0e2-13f4-47fc-aec3-f7fa265cfdb5@app.fastmail.com> Date: Sat, 5 Jul 2025 01:50:40 +0200 X-Gm-Features: Ac12FXxp78_KZe5t4vxlsbh_J67CHGJhQ5De1CJIpjleOywFfFGquvVNuZ1vpkY 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 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 c= ase, it's not just reflection; there's a whole bunch of other things that c= an 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 benefi= ts. 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. > 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 las= t-minute defaults. I don't love it, but given the severe limitations of `r= eadonly` it was the best I could do. (With aviz today, I could likely do b= etter.) 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. E.g. $reflection_attributes =3D $reflection_class->getAttributes(ServiceDefinition::class)->getAttributes()= ; $attribute_instance =3D $reflection_attributes[0]->newInstance(); $service_definitions =3D $attribute_instance->getServiceDefinitions(...$more_args); Or $reflection_attributes =3D $reflection_method->getAttributes(RouteModifier::class)->getAttributes(); $route =3D new Route(); foreach ($reflection_attributes as $reflection_attribute) { $reflection_attribute->newInstance()->modifyRoute($route, ...$more_args); } return $route; This way, you have a complete object in each step, but only the final object is the one you want to "collect". > > Basically, any time you have multi-step construction you will have a peri= od where the state is potentially incomplete. It is not just about possible incompleteness, it is about being deterministic as early as possible. 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. > 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. > ... the object will always have an incomplete phase, and that's unavoidab= le. So I'm not *that* worried about there being a brief incomplete-gap in = time in core, because I'll always have one anyway, and I've already figured= out how to handle it. I find that in most cases it can actually be avoided. But I have not really looked into your code much. I have seen some packages in packagist that use attributesutil, but I would need more time to study these. -- Andreas > > --Larry Garfield