php / php-src

The PHP Interpreter
https://www.php.net
Other
37.97k stars 7.73k forks source link

Assigning property value through readonly array results in "Cannot modify readonly property" #15912

Open SunMar opened 4 days ago

SunMar commented 4 days ago

Description

The following code:

<?php
class A {
    public function __construct(
        public readonly array $a,
    ) {}
}

class B {
    public function __construct(
        public string $b,
    ) {
    }
}

$a = new A([ new B('foo') ]);
$a->a[0]->b = 'bar';

echo $a->a[0]->b;

Resulted in this output:

PHP Fatal error:  Uncaught Error: Cannot modify readonly property A::$a in /test.php:17

But I expected this output instead:

bar

If you try to change the property of an object that is inside a readonly array, based on syntax it thinks you're change the array itself and throws a Fatal, even though that property we're actually trying to change ($a->a[0]->b) is not readonly and can be changed. If the code to change the B::$b property is refactored like this the code works as expected:

$a = new A([ new B('foo') ]);
$b = $a->a[0];
$b->b = 'bar';

PHP Version

8.2.23, 8.3.11

Operating System

No response

iluuu1994 commented 4 days ago

This is related to https://github.com/php/php-src/pull/12244. For $foo['bar']->baz = 'baz';, both ['bar'] and ->baz are executed with BP_VAR_W. This used to be necessary, because -> would do autovivification itself. See PHP <8.0. https://3v4l.org/Q7RMT Since readonly disallows modification of arrays themselves, the fetch fails. Nowadays, we could switch to using BP_VAR_R for the ['bar'] fetch itself, but as https://github.com/php/php-src/pull/5250 has originally shown this breaks ext-simplexml. It also occurred to me that the current behavior is really useful for structs, which is why that PR was closed.

For now, I would consider this a wontfix.