Re: [RFC] [Discussion] Readonly class amendments
| From: | Marco Pivetta | Date: | Thu, 17 Nov 2022 14:47:33 +0000 |
| Subject: | Re: [RFC] [Discussion] Readonly class amendments | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-119016@lists.php.net to get a copy of this message | ||
Hey Máté,
On Tue, 15 Nov 2022 at 07:30, Máté Kocsis <kocsismate90@gmail.com> wrote:
> Hi Everyone,
>
> Following Nicolas' thread about "Issues with readonly classes" (
> https://externals.io/message/118554), we created an RFC to fix
> two issues
> with the readonly behavior:
> https://wiki.php.net/rfc/readonly_amendments
>
>
Since I was reviewing https://github.com/lcobucci/jwt/pull/979 today,
I had
to put the "why immutability is an LSP requirement" in examples.
As explained in
https://github.com/lcobucci/jwt/pull/979#discussion_r1025280053,
a consumer
relying on a
readonly implementation of a class probably also expects
replacement implementations as read-only.
I am aware that we lack the readonly marker at interface level (would be
really neat), but the practical example is as follows:
```php
<?php
/* readonly */ class ImmutableCounter {
public function __construct(private readonly int $count) {}
public function add1(): self { return new self($this->count + 1); }
public function value(): int { return $this->count; }
}
// assuming the proposed RFC is in place, this is now possible:
class MutableCounter extends ImmutableCounter {
public function __construct(private int $count) {}
public function add1(): self { return new self(++$this->count); }
public function value(): int { return $this->count; }
}
$counter1 = new ImmutableCounter(0);
$counter2 = $counter1->add1();
$counter3 = $counter2->add1();
var_dump([$counter1->value(), $counter2->value(), $counter3->value()]);
$mutableCounter1 = new MutableCounter(0);
$mutableCounter2 = $mutableCounter1->add1();
$mutableCounter3 = $mutableCounter2->add1();
var_dump([$mutableCounter1->value(), $mutableCounter2->value(),
$mutableCounter3->value()]);
```
This prints ( https://3v4l.org/IDhRY )
```
array(4) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
}
array(4) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(2)
}
```
I took a simplified example on purpose, just to demonstrate the problem
with readonly disappearing in child classes.
That's really really confusing, buggy, and, from a consumer PoV, broken
(LSP violation too, transitively).
That said, allowing mutation in __clone() is fine.
Marco Pivetta
https://twitter.com/Ocramius
https://ocramius.github.io/