Hey Máté,

On Tue, 15 Nov 2022 at 07:30, Máté Kocsis <kocsismat...@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/

Reply via email to