Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:118604 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 7303 invoked from network); 10 Sep 2022 21:35:09 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 10 Sep 2022 21:35:09 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id AE4B01804AC for ; Sat, 10 Sep 2022 14:35:08 -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=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS, 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-f180.google.com (mail-yw1-f180.google.com [209.85.128.180]) (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 ; Sat, 10 Sep 2022 14:35:08 -0700 (PDT) Received: by mail-yw1-f180.google.com with SMTP id 00721157ae682-3450a7358baso57434527b3.13 for ; Sat, 10 Sep 2022 14:35:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date; bh=57wnPF0kRtxeF+G9ERfsW5Cu30Azoi4cAiWVrf73lNA=; b=mbbaiDZJoAaBoO6gxpKnZpQ7SF3vES26tiPMQB5oZ/FrAa/djfnmzBmoVIjrZKynod vOedmDUU8MI4VaFD3845ztLvlSqAGqBbJVZBc7yQr/gaFXiBQyHBqCTA9yBEK+/7yndL tERtgEce/6I99lXHAJeEUqEXHaxkU6nRM+7WV1wPiY9cwZqKP09roa2I1+/fPqIA9nz2 wlN2vT1MMGfDQFE/wfzpV0dosSFaUqgMVEPyxH+IKxG/5drMR3w72+t0t2meUOZsuzDF FGJ3q5hIS8Ihfnl9BPC0+wbq6eo6ineS8ZkCNvVdlsX0VXgyVsUsckTrYFY81pUaP+rJ NWEw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date; bh=57wnPF0kRtxeF+G9ERfsW5Cu30Azoi4cAiWVrf73lNA=; b=tv5GW5WBm4T27YHgEjByntA5uvHqL2O6GwaXJ1NDvsyiXMO52mBz3y9QqRI2lyA5CU mACRMvFDfRRHXPCZkol6VYrw0BD078DkD1ALhA6zqSfR0nAccpzXkpkTeqyfaMhPYsZr /3trrtHY5UTrKOSw39Oh7dBfZH3g3NLipN/k0x32ZkuqpGw+OgSBssViJrVYbS+CJaDn BTYtWQ/nEVlhE8CtjMTbMrXBux2fkrbL4/qEjEjsPXqs97U/7NozM9+5fVoK+QgoR11o SMRuqfPr6H/UiLVwmOl3Lmh8FMYtolejj/j2wlVHBav3QIk16HlU0vIglM+yo+TnRDv3 56/Q== X-Gm-Message-State: ACgBeo23H6BFzVn++G7lb+ueicBtMn7IaGYe3LznS6LPt/v2UhYRBN+k sowhl/h9kLp5T1iHBis8+aR2SdrHij0+eQOdejY= X-Google-Smtp-Source: AA6agR5LvfDvIs4U7h7acGvjHa1CnjL4eYkOUaas/V5X/8IXLzn871KQzMOpsq/uTlD/+OQEPwBHQxAJS6kdkSZcig4= X-Received: by 2002:a0d:e210:0:b0:345:107c:613a with SMTP id l16-20020a0de210000000b00345107c613amr16966448ywe.473.1662845707243; Sat, 10 Sep 2022 14:35:07 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Sat, 10 Sep 2022 23:34:55 +0200 Message-ID: To: Larry Garfield Cc: php internals Content-Type: multipart/alternative; boundary="000000000000b2556d05e8596fa8" Subject: Re: [PHP-DEV] Re: Issues with readonly classes From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --000000000000b2556d05e8596fa8 Content-Type: text/plain; charset="UTF-8" Hello Larry, > Regarding your main question: I understand your problem with readonly > > classes, and I'd be happy if we found a solution which fits your > use-cases > > and keeps consistency for the engine at the same time. To give you more > > context about the inheritance related restriction in the RFC: I went with > > forbidding the extension of readonly classes by non-readonly ones so that > > the invariants of the parent (no dynamic or mutable properties are > allowed) > > don't occasionally get violated by the child. However, I cannot think > about > > a proper scenario now where this would cause any problems... I'm > wondering > > if anyone could come up with one? > > I don't have a real-world example, but a general pattern. The advantage > of a readonly object (either conventionally like PSR-7 or enforced by the > `readonly` keyword) is that you can store a reference to it without having > to worry about it changing out from under you. That means you can code on > the assumption that any data derived from that object is also not subject > to change. > > If you accept a readonly object of type Foo, you would write code around > it on that assumption. > > If you're then passed an object of type Bar extends Foo, which has an > extra non-readonly property, that assumption would now be broken. Whether > that counts as a Liskov violation is... a bit squishy, I think. > > Where that might be a problem is a case like this: > > readonly class Person { > public function __construct(public string $first, public string $last) {} > > public function fullName() { > return "$this->first $this->last"; > } > } > > class FancyPerson extends Person { > public string $middle = ''; > > public function setMiddle(string $mid) { > $this->middle = $mid; > } > > public function fullName() { > return "$this->first $this-middle $this->last"; > } > } > > class Display { > public function __construct(protected Person $person) {} > > public function hello() { > return "Hello " . $this->person->fullName(); > } > } > > Now, Display assumes Person is readonly, and thus fullName() is > idempotent, and thus Display::hello() is idempotent. But if you pass a > FancyPerson to Display, then fullName() is not safely idempotent (it may > change out from under you) and thus hello() is no longer idempotent. > > Whether or not that problem actually exists in practice is another > question. And to be fair, the same risk exists for conventionally-readonly > classes today (PSR-7, PSR-13, etc.), unless they're made final. Arguably > the safety signaling of a lexically readonly class is stronger than for a > conventionally readonly class, so one would expect that to not be broken. > But that's the case where a non-readonly child of a readonly parent can > cause problems. > Thanks for constructing this example, that's good food for thoughts. Unfortunately, The code following readonly child class shows that this safety you describe doesn't exist: readonly class FancyPerson extends Person { private readonly stdClass $middle; public function setMiddle(string $mid) { $this->middle ??= new stdClass; $this->middle->name = $mid; } public function fullName() { return "$this->first $this-middle $this->last"; } } > Personally I'm undecided at the moment whether or not it should be > allowed. I'm sympathetic to the "it's easier to disallow and allow later > than vice versa" argument, but still not sure where I stand. The above at > least gives a concrete example of where the problem would be. > If we want the safety you describe, we might want a stronger version of it. Let's name it immutable. An immutable property/class would be like a readonly property with the additional restriction that only an immutable value could be assigned to it (scalars + array + immutable classes.) But that's another topic. Your example made me doubt for a moment, but without any convincing purpose, this should help decide that child classes shouldn't have to be readonly. Nicolas --000000000000b2556d05e8596fa8--