PHP 8.1 introduces an interesting feature: readonly member variables:
Let's start with an example of how to use it:
class Test
{
public readonly string $prop;
public function setProp(string $prop): void
{
$this->prop = $prop; // legal initialization
}
}
$obj = new Test;
$obj->setProp('abc');
echo $obj->prop; // legal read
$obj->prop = 'foo'; // throws exception: Cannot modify readonly property Test::$prop
Once initialized, a variable cannot be overwritten with another value.
Scope
Interestingly, attempting to assign a value to $obj->prop
will also throw an exception even if the variable hasn't been initialized:
$obj = new Test;
$obj->prop = 'foo';
// throws exception too: Cannot initialize readonly property Test::$prop from global scope
This will even throw an exception:
class Child extends Test
{
public function setProp(): void
{
$this->prop = 'hello';
// throws exception: Cannot initialize readonly property Test::$prop from scope Child
}
}
$obj = new Child;
$obj->setProp();
A readonly variable simply cannot be written from anywhere other than the class that defined it. Quite peculiar.
Immutability
The fact that the content of readonly variables cannot be changed doesn't mean the data written to them is immutable. If an object is written to such a variable, its internal variables can still be modified. The object does not become immutable.
The same applies to arrays. Although the behavior is slightly different here. Changing elements in the array is considered a change to the entire array and as such is impermissible in a readonly variable. However, if the array contains an element that is a reference, changing its content is not considered a change to the entire array and thus can occur in a readonly element. This, however, is standard PHP behavior as always.
In other words, this is possible:
class Test
{
public readonly array $prop;
public function setProp(): void
{
$item = 'foo';
$this->prop = [1, &$item, 2];
var_dump($this->prop); // [1, 'foo', 2]
$item = 'bar'; // legal
var_dump($this->prop); // [1, 'bar', 2]
}
}
But this is not possible:
class Test
{
public readonly array $prop;
public function setProp(): void
{
$this->prop = ['a', 'b'];
$this->prop[1] = 'c'; // throws exception!
}
}
Type
Since readonly variables utilize the ‘uninitialized’ state, which exists for variables with a defined type, it is only possible to declare a variable as readonly in conjunction with a data type.
Leave a comment