On Mon, Sep 5, 2022, at 4:01 AM, Máté Kocsis wrote: > 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. 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. --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php