Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:120456 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 45291 invoked from network); 30 May 2023 14:49:51 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 30 May 2023 14:49:51 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id CB1411804DF for ; Tue, 30 May 2023 07:49:50 -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,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-yb1-f170.google.com (mail-yb1-f170.google.com [209.85.219.170]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 30 May 2023 07:49:50 -0700 (PDT) Received: by mail-yb1-f170.google.com with SMTP id 3f1490d57ef6-bad142be0ebso6227966276.3 for ; Tue, 30 May 2023 07:49:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech-net.20221208.gappssmtp.com; s=20221208; t=1685458189; x=1688050189; 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=0fN/oQ4Rc5cqQjx2wJQ2Knc257I4iv03cIzqjvI6sws=; b=Px/mKhavpgmmeEi/ybVA37jRKEIgC80We26HgBkwzLrVXCIwhgsKgqpenBIF7L0NQA xJ0eySZxwaGH9eeIWvpdLGu12l0HXeDQYwM3N1+84LdaHI1ZfEER9bxJMVVckQQmHE4U oh1mW5d2g4fP+h3MM55tbySBg/vsd2PflyZjICpktYU4p+0CI1QIU+B2n8l9rdMY5CzI Kaizgi1ZBPmHXxEz8MW9THxnYY+QYp535KvZt9jUBZCPVWGMZZuUq/hJj9FnQimzRNov 2wtdPcQGpQwDuWF47W5B0E/Bk8Dm906eeVQclYAr5rB5+ldUbqfIOEw0rTLiMeaPZOZ7 POIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1685458189; x=1688050189; 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=0fN/oQ4Rc5cqQjx2wJQ2Knc257I4iv03cIzqjvI6sws=; b=Yl481dTznvPiBpniLFj3BHVJ//iF8d+qLc1DzPAN68zbQxwuF9lNgKgiWn+Pzst/OD dOSCi3NrPT+FDLphR0m10vc/7/Z9x3gfT5KBl1pX4GnetzPE9Y94Phq+e8ZfMNRfuxQs xq8LeYtqAmFrJJfG3QkoOBDrOb1D+yG9jJQzeKxzjjvffkVSwjSil7bO/ybDcW8T2cv2 wWc7acADc8hngENO9qPi3w4YFrQUXFrBfmvftYBR2uhBvnEfP6XoDywOd90Dxt34gquW tuTZ0dxu88YrUTQ/jtXcfi8kkQCFVNeoQORRexNKkomMYEJDvIq9a0NPDfr/56Avoeqc KN3A== X-Gm-Message-State: AC+VfDxmB1d76HkWFcwktVrHO04a9q3+1n1WdsWMzFGLgS9sNjzsjl5t Nlw+ZNRujEn+Qalegb64RMmUQD8U9EsRhEHEndhqzWvXoGFb+GkN6LTXYw== X-Google-Smtp-Source: ACHHUZ42pQwH1WdahPGmTfAp3Ody1b/9KdyB0NDcOCAGPnIDVWz8tMQcuvIQpVjQSCRWqdnQCfxe/ZjtwAvC9QJ/+PQ= X-Received: by 2002:a81:4803:0:b0:55a:7c7:6ff7 with SMTP id v3-20020a814803000000b0055a07c76ff7mr2369342ywa.11.1685458189531; Tue, 30 May 2023 07:49:49 -0700 (PDT) MIME-Version: 1.0 References: <64EAB991-7082-47B8-B546-73CD08243C6E@koalephant.com> <23DF3A9A-9F11-4786-A011-41AE0FA9CFA9@koalephant.com> In-Reply-To: <23DF3A9A-9F11-4786-A011-41AE0FA9CFA9@koalephant.com> Date: Tue, 30 May 2023 16:49:38 +0200 Message-ID: To: Stephen Reay Cc: PHP internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] Declaration-aware attributes From: andreas@dqxtech.net (Andreas Hennings) On Tue, 30 May 2023 at 15:14, Stephen Reay wrote= : > > (Resending to the list without all the history because qmail complained a= bout message size) > > > >> > >> Hi Andreas, > >> > >> I too have wondered (and I think asked in room11?) about such a concep= t. >From memory the general response was =E2=80=9Cjust do it in userland wi= th 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` instanc= e is available in the constructor, I=E2=80=99m not keen on the proposed mag= ic =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 re= ason to be instantiated directly in code) > > > > Good point! Almost made me change my mind completely. But I already > > changed it back :) > > > > When instantiating in code, the "real" signature would have to be > > used, and the reflector argument passed explicitly. > > > That=E2=80=99s kind of my point: it=E2=80=99s not super intuitive why (or= the specifics of how) it=E2=80=99s being skipped when it=E2=80=99s an attr= ibute, vs when it=E2=80=99s instantiated from code. What if someone specifi= es an argument with the same name? If they specify args without names, can = they just use null for that? Etc. I agree it could be confusing. But for the named args, I think it is quite obvious: #[Attribute] class A { public readonly array $moreArgs; public function __construct( public readonly string $x, // Reflector parameter can be last required parameter, BUT #[AttributeDeclaration] public readonly \ReflectionClass $class, // Optional parameters have to be after the required reflector paramete= r. public readonly ?string $y =3D NULL, // Variadic parameter must be last. string ...$moreArgs, ) { $this->moreArgs =3D $moreArgs; } } #[A('x', 'y', 'z')] // -> new A('x', $reflector, 'y', 'z') #[A(x: 'x', y: 'y')] // -> new A('x', $reflector, 'y') #[A(x: 'x', class: new ReflectionClass('C'))] // -> new A('x', new ReflectionClass('C')) We _could_ say that explicitly passing a value for the reflector in an attribute declaration is forbidden. Or we allow it, then an explicit value would simply overwrite the implicit value. If we use placeholder syntax, the above examples would look like this: #[A('x', ?, 'y', 'z')] // -> new A('x', $reflector, 'y', 'z') #[A(x: 'x', class: ?, y: 'y')] // -> new A('x', $reflector, 'y') #[A(x: 'x', class: new ReflectionClass('C'))] // -> new A('x', new ReflectionClass('C')) > > > This would be > > useful for unit tests that want to replicate the realistic behavior. > > Also it guarantees that the code of the attribute class can really > > count on this value to not be null, no matter how the class is > > instantiated. > > > > > > I would expect that whether the Reflector object is required is simply a = matter of whether or not the parameter is nullable. > If it=E2=80=99s not nullable, then yes, the explicit instantiation call w= ill need to supply it at the correct location. If it=E2=80=99s only require= d when created from attribute usage, then it would accept null, and the con= structor would have appropriate logic to handle that. > Yes. But I would expect the common practice to be to make it required, because then the constructor code will be simpler. > > > >> > >> I think a better approach would be to suggest authors put the paramete= r at the *end* of the parameter list, so that no =E2=80=98skipping' is requ= ired when passing arguments without names (or put it where you like if you= =E2=80=99re always using named arguments) > > > > If I understand correctly, the proposal would technically not change, > > we just add a recommendation. > > Technically, yes =E2=80=9Cmy way=E2=80=9D would work fine with the propos= al you=E2=80=99ve suggested, if I choose to always put the parameter marked= by #[ReflectionContext] last. As above, the problem with this would be optional and variadic parameters, which have to come after a required reflector parameter. > > I=E2=80=99m just concerned about confusing usage if =E2=80=9Cinsert this = parameter anywhere=E2=80=9D is the =E2=80=98recommended=E2=80=99 (i.e. docu= mented example) way to use this feature. > > Even with that concern, I still prefer this to most other solutions menti= oned so far, for the same reasons: they=E2=80=99re all some degree of magic= . > > The only other solution I can think of that=E2=80=99s less =E2=80=9Cmagic= =E2=80=9D and more explicit, is (and I have no idea if this is even feasibl= e technically) to introduce a builtin trait for attribute classes to use, p= roviding a protected method or property that gives access to the Reflector = (how the trait has access is not really important, I assume it can be assig= ned to the object somehow before the constructor is called). I guess this c= ould also be an abstract class, but a trait makes it much easier to adopt s= o that would be my preferred approach. > > So something like > > trait AttributeReflector { > protected function getReflector(): \Reflector { > // do internal stuff > } > } > > #[Attribute] > class Foo { > Use \AttributeReflector; > > public readonly string $name; > > function __construct(?string $name =3D null) { > $this->name =3D $name ?? $this->getReflector()->name; > } > } I was also considering this, but there is a problem. When you instantiate the attribute class outside an attribute declaration, how do you tell it about a required reflector? This would be relevant e.g. during unit tests. The constructor parameter provides a clean way to pass a custom reflector. But with the ->getReflector(), the reflector would already be magically added to the object before the constructor is executed. This would be impossible to replicate in custom code outside an attribute declaration. --- Cheers Andreas > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >