Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:120449 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 593 invoked from network); 30 May 2023 03:22:45 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 30 May 2023 03:22:45 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 66C50180089 for ; Mon, 29 May 2023 20:22:44 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS17378 206.123.64.0/18 X-Spam-Virus: No X-Envelope-From: Received: from mail1.25mail.st (mail1.25mail.st [206.123.115.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Mon, 29 May 2023 20:22:43 -0700 (PDT) Received: from smtpclient.apple (unknown [49.48.216.76]) by mail1.25mail.st (Postfix) with ESMTPSA id 410CE60379; Tue, 30 May 2023 03:22:36 +0000 (UTC) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3731.600.7\)) In-Reply-To: Date: Tue, 30 May 2023 10:22:23 +0700 Cc: PHP internals Content-Transfer-Encoding: quoted-printable Message-ID: <64EAB991-7082-47B8-B546-73CD08243C6E@koalephant.com> References: To: Andreas Hennings X-Mailer: Apple Mail (2.3731.600.7) Subject: Re: [PHP-DEV] Declaration-aware attributes From: php-lists@koalephant.com (Stephen Reay) > On 30 May 2023, at 07:48, Andreas Hennings = wrote: >=20 > Hello internals, > I am picking up an idea that was mentioned by Benjamin Eberlei in the = past. > https://externals.io/message/110217#110395 > (we probably had the idea independently, but Benjamin's is the first > post where I see it mentioned in the list) >=20 > Quite often I found myself writing attribute classes that need to fill > some default values or do some validation based on the symbol the > attribute is attached to. > E.g. a parameter attribute might require a specific type on that > parameter, or it might fill a default value based on the parameter > name. >=20 > Currently I see two ways to do this: > 1. Do the logic in the code that reads the attribute, instead of the > attribute class. This works ok for one-off attribute classes, but it > becomes quite unflexible with attribute interfaces, where 3rd parties > can provide their own attribute class implementations. > 2. Add additional methods to the attribute class that take the symbol > reflector as a parameter, like "setReflectionMethod()", or > "setReflectionClass()". Or the method in the attribute class that > returns the values can have a reflector as a parameter. >=20 > Both of these are somewhat limited and unpleasant. >=20 > I want to propose a new way to do this. > Get some feedback first, then maybe an RFC. >=20 > The idea is to mark constructor parameters of the attribute class with > a special parameter attribute, to receive the reflector. > The other arguments are then shifted to skip the "special" parameter. >=20 > #[Attribute] > class A { > public function __construct( > public readonly string $x, > #[AttributeContextClass] > public readonly \ReflectionClass $class, > public readonly string $y, > ) {} > } >=20 > $a =3D (new = ReflectionClass(C::class))->getAttributes()[0]->newInstance(); > assert($a instanceof A); > assert($a->x =3D=3D=3D 'x'); > assert($a->class->getName() =3D=3D=3D 'C'); > assert($a->y =3D=3D=3D 'y'); >=20 > Note that for methods, we typically need to know the method reflector > _and_ the class reflector, because the method could be defined in a > base class. >=20 > #[Attribute] > class AA { > public function __construct( > #[AttributeContextClass] > public readonly \ReflectionClass $class, > #[AttributeContextMethod] > public readonly ReflectionMethod $method, > ) {} > } >=20 > class B { > #[AA] > public function f(): void {} > } >=20 > class CC extends B {} >=20 > $aa =3D (new ReflectionMethod(CC::class, = 'f))->getAttributes()[0]->newInstance(); > assert($a->class->getName() =3D=3D=3D 'CC'); > assert($a->method->getName() =3D=3D=3D 'f'); >=20 > --- >=20 > Notice that the original proposal by Benjamin would use an interface > and a setter method, ReflectorAwareAttribute::setReflector(). >=20 > I prefer to use constructor parameters, because I generally prefer if > a constructor creates a complete and immutable object. >=20 > ---- >=20 > Thoughts? >=20 > -- Andreas >=20 > --=20 > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >=20 Hi Andreas, I too have wondered (and I think asked in room11?) about such a concept. = =46rom memory the general response was =E2=80=9Cjust do it in userland = with a wrapper=E2=80=9D so its good to see someone else is interested in = this being part of the language. While I agree that it=E2=80=99s most useful if the `Reflector` instance = is available in the constructor, I=E2=80=99m not keen on the proposed = magic =E2=80=9Cskipping=E2=80=9D of arguments as you suggest. It seems = way too easy to confuse someone (particularly if the attribute class = itself has reason to be instantiated directly in code) I think a better approach would be to suggest authors put the parameter = at the *end* of the parameter list, so that no =E2=80=98skipping' is = required when passing arguments without names (or put it where you like = if you=E2=80=99re always using named arguments) Cheers Stephen=20