Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:120476 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 83809 invoked from network); 30 May 2023 19:35:08 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 30 May 2023 19:35:08 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id CAD42180382 for ; Tue, 30 May 2023 12:35:04 -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-yw1-f178.google.com (mail-yw1-f178.google.com [209.85.128.178]) (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 12:35:04 -0700 (PDT) Received: by mail-yw1-f178.google.com with SMTP id 00721157ae682-5664b14966bso37563867b3.1 for ; Tue, 30 May 2023 12:35:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech-net.20221208.gappssmtp.com; s=20221208; t=1685475304; x=1688067304; 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=H0t49dqDf+9CTe4zti+Rbw7ZLl+6xKS5AxGPU0/4t+Q=; b=PRWyRa1GtnlovTB89kOJGsyrOfjlQ/Miw1bpns/j57q2XzKJvKT3qUWV/7irmI5aLD ws4AYZAI/O+uY2RKS4eqo2XfG19r3EPnQ7z48oNj+ZbXZZ9dLTFcqmTXGjCNhSDB/EG7 Sv7VQUhm8tcjox6DThmlpYkn++tcFCZwnlh/pT7KabDujvFOq0MTNJIlNA0nn+kPBHgY +1V63e9vuQkqU02wBNtTgTttluLX8YVosX3IKKe2S8qJvNNcIe0kzYME3tyCqAf2U9zz YLwLIaNZ1SFfRJliwLLGCdbeqpG8b3v7uiCOaL0851vVR9OaVC6wORfEsdnm8gor/Z26 JyOQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1685475304; x=1688067304; 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=H0t49dqDf+9CTe4zti+Rbw7ZLl+6xKS5AxGPU0/4t+Q=; b=ZBXPu+LnuoxZOab0I80tLaG/+nIkUsWOohXX4BqG+rvi4gZmKbHLfB6rIKsIhR/3SJ wh0ajMDJOmAasFXZzF5hRFwbLrh2hcmOEYLEMMjDKynNsRTVY89g9GIDxQ6sn6iiEp5/ WUYC8TWHlnwhMh/EVhrTjicfi9XuSalMUAqhlpHI27klzU1yB1I+o3yf5HAfJBnY9JQs xZFNRGqPDfNLfDSJmb4OQtTvXYhUVXql+Rk6UzhrS0v8HcjCGg8itVO6vohWDkSB53Xm pSaReHQr+tsLhw31o9etnirt9BXhFUf7u/Ehb5ggQs+bWIOPYmy1zYLI5Jhx0hFzYhZ+ Gd2g== X-Gm-Message-State: AC+VfDyVCZOklGg8lMUm1hdc2GaierH3Tmp/0qp55iYA7hBOSziCpezi dSilLdF8ljujspz0UXrRQvqg0FeTWHHNq37XcpGiww== X-Google-Smtp-Source: ACHHUZ5t7DGfk/WYDCWW/poK2+V8S6QnuFtqb5fl0oIsStc3kZwRu3FSKDMNNgi9U82EZUL6NhYFVHh9cKZVTwBk1DY= X-Received: by 2002:a81:4993:0:b0:561:e7bb:1b27 with SMTP id w141-20020a814993000000b00561e7bb1b27mr3066682ywa.52.1685475303776; Tue, 30 May 2023 12:35:03 -0700 (PDT) MIME-Version: 1.0 References: <64EAB991-7082-47B8-B546-73CD08243C6E@koalephant.com> <23DF3A9A-9F11-4786-A011-41AE0FA9CFA9@koalephant.com> <75a8db86-c91d-4f5e-b19e-e968926b4e8a@app.fastmail.com> In-Reply-To: <75a8db86-c91d-4f5e-b19e-e968926b4e8a@app.fastmail.com> Date: Tue, 30 May 2023 21:34:52 +0200 Message-ID: To: Larry Garfield 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 19:12, Larry Garfield wrote= : > > I've run into this issue in my attribute library as well (https://github.= com/Crell/AttributeUtils). What I do there is allow attributes to opt-in t= o a callback method via interface. For example: > > #[\Attribute] > class AttribWithName implements FromReflectionClass > { > public readonly string $name; > > public function __construct(?string $name =3D null) > { > if ($name) { > $this->name =3D $name; > } > } > > public function fromReflection(\ReflectionClass $subject): void > { > $this->name ??=3D $subject->getShortName(); > } > } So it is technically from outside it is a setter, whereas from inside it is not really. Technically, this means you need a version of the attribute object that can exist without the values from the reflector. The constructor needs to initialize some "stub" values, until the setter is called. Every other method also needs to support the case when the setter has not been called yet, or possibly throw an exception. Also, your property type has to allow null, so `?string`, not `string`. I usually try to avoid this, this is why I proposed the constructor paramet= er. This way, the object is never in an incomplete state. (Side note: Personally I use the naming convention from*() for static factory methods. I might use set*() for this one, but then again, it is only a setter from the outside.) > > (Side note: This is why static analysis tools that forbid writing to read= only properties except from the constructor are wrong; it's also an example= of where asymmetric visibility would be superior to readonly. But I digre= ss.) Technically there is no guarantee that the setters will be called before any other method, and only once. If these methods can write on readonly properties, then any method can. Unless we somehow mark these methods as special. On the other hand, a wither method with "clone with" should be allowed to work on readonly properties. You could rewrite your method like this, once we have clone with: (or use the old-school syntax but without readonly) public function withReflector(\ReflectionClass $subject): static { return ($this->name !=3D=3D NULL) ? $this : clone $this with (name: $subject->getShortName(); } Then in the discovery code: $attribute =3D $reflectionClass->getAttributes()[0]->newInstance(); $attribute =3D $attribute->withReflector($reflectionClass); > > My preference would be for something along those lines to be implemented = in core. > > Importantly, we *MUST NOT* design it such that the reflection object gets= assigned to a property of the attribute. Reflection objects are not seria= lizable. Attributes will frequently be cached. That means it forces the a= ttribute author to make the property nullable AND then unset it sometime be= fore the attribute gets serialized, or it will break. That's a no-go. There could be ways around this problem, but I agree we should avoid it on design level. > > That's why I think an opt-in interface is the way to go. It also avoids = any confusion around the constructor parameters, which is, based on this th= read, a confusing area. :-) What do you think about a placeholder syntax to avoid confusion with a skipped parameter? Like #[A('x', ?', 'y')] > > My second preference would be the ReflectionAttribute::inProgress() call = in the constructor, or something like that. I like that less because it's = a stateful call, but it would also reduce the issue with readonly propertie= s (as in the example above) by making both alternatives available in the co= nstructor, so maybe it's an acceptable tradeoff. I would like to avoid anything that is stateful or that leaves an incomplete stub object. (But I think I made this clear enough, so..) > > I can see an argument either direction here. > > --Larry Garfield > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >