Readonly Properties in PHP 8.1

Since PHP 8.1, class properties can be declared as readonly. This means that class property can be initialized once and cannot be modified later. Readonly properties are declared with readonly keyword.

Let’s say we have User class which has readonly property.

User.php

<?php

class User
{
    private readonly int $id;

    public function setId(int $id): void
    {
        $this->id = $id;
    }
}

Next, we created an instance of User class. A second call of setId method produces a fatal error because id property already set by the first setId method call.

<?php

require_once 'User.php';

$user = new User();
$user->setId(10); // OK
$user->setId(20); // Error
Fatal error: Uncaught Error: Cannot modify readonly property User::$id in ...

There are some notes for readonly properties.

Only allowed on typed properties

Readonly properties should be declared with type. The reason is that properties declared without a type has null value by default and is not compatible with readonly properties.

<?php

class User
{
    private readonly int $id; // OK

    private readonly $name; // Error
}
Fatal error: Readonly property User::$name must have type in User.php on line 7

Only initialized inside the class

Readonly properties can only be initialized inside the class, either from the constructor or method. Readonly properties cannot be initialized outside the class (from global scope).

User.php

<?php

class User
{
    private readonly int $id;

    private readonly string $name;

    public readonly int $age;

    public function __construct(int $id)
    {
        $this->id = $id;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }
}
<?php

require_once 'User.php';

$user = new User(10); // OK
$user->setName('John'); // OK
$user->age = 25; // Error
Fatal error: Uncaught Error: Cannot initialize readonly property User::$age from global scope in main.php:7

Allowed with promoted properties

Readonly properties can be declared in constructor as promoted properties as well.

<?php

class User
{
    public function __construct(private readonly int $id) // OK
    {
    }
}

Default value is not allowed

Readonly properties cannot have default value.

<?php

class User
{
    private readonly string $role = 'Viewer'; // Error
}
Fatal error: Readonly property User::$role cannot have default value in User.php on line 5

Unset is not allowed

Readonly properties cannot be unset after initialization.

User.php

<?php

class User
{
    private readonly int $id;

    public function __construct(int $id)
    {
        $this->id = $id;
    }

    public function unsetId(): void
    {
        unset($this->id); // Error
    }
}
<?php

require_once 'User.php';

$user = new User(10);
$user->unsetId();
Fatal error: Uncaught Error: Cannot unset readonly property User::$id in User.php:14

Redeclaration is not allowed in inheritance

When child class extends the parent class, it is not allowed to override a readonly property with a read-write property.

User.php

<?php

class User
{
    public readonly string $id;
}

Student.php

<?php

class Student extends User
{
    public string $id; // Error
}
Fatal error: Cannot redeclare readonly property User::$id as non-readonly Student::$id in ...

This rule applies in opposite way as well. It is not allowed to override a read-write property with a readonly property.

User.php

<?php

class User
{
    public string $id;
}

Student.php

<?php

class Student extends User
{
    public readonly string $id; // Error
}
Fatal error: Cannot redeclare non-readonly property User::$id as readonly Student::$id in ...

Leave a Comment

Your email address will not be published. Required fields are marked *