PHP Readonly Properties

Summary: in this tutorial, you’ll learn about the PHP readonly properties that can be only initialized once.

Introduction to the PHP readonly properties #

PHP 8.1 introduced the readonly class properties. The readonly properties allow you to define properties that can be only initialized once within the class.

To define a readonly property, you use the readonly keyword in a typed property:

<?php


class MyClass
{
    private readonly type propertyName;
}Code language: PHP (php)

Or

<?php


class MyClass
{
    public function __construct(private readonly type propertyName)
    {
    }
}Code language: PHP (php)

For example, the following defines a User class with the $username as a readonly property:

<?php


class User
{
    public readonly string $username;

    public function __construct(string $username)
    {
        $this->username = $username;
    }
}Code language: PHP (php)

Try it

In this example, you can only initialize the $username once when creating a new User object like this:

$user = new User('joe','secure');Code language: PHP (php)

Try it

If you attempt to change the $username property after that, you’ll get an error:

$user->username = 'john';Code language: PHP (php)

Error:

Fatal error: Uncaught Error: Cannot modify readonly property User::$usernameCode language: PHP (php)

PHP only allows you to initialize the $username property from within the class itself, either from the constructor or a method.

The following example also causes an error because it attempts to initialize the $username property from the outside of the User class:

<?php


class User
{
    public readonly string $username;
}

$user = new User();
$user->username = 'joe';Code language: PHP (php)

Error:

Fatal error: Uncaught Error: Cannot initialize readonly property User::$username from global scopeCode language: plaintext (plaintext)

To fix it, you can add a constructor like the above example. Alternatively, you can define a method to initialize the readonly property from within the class as follows:

<?php


class User
{
    public readonly string $username;
    public string $password;

    public function setUsername(string $username): void
    {
        $this->username = $username;
    }
    
}

$user = new User();
$user->setUsername('joe');

echo $user->username;Code language: PHP (php)

Try it

Note that if you call the setUsername() method a second time, you’ll get an error because it modifies to the username property that was already initialized.

<?php


class User
{
    public readonly string $username;
    public string $password;

    public function setUsername(string $username): void
    {
        $this->username = $username;
    }
    
}

$user = new User();
$user->setUsername('joe');
$user->setUsername('john');Code language: PHP (php)

Error:

Fatal error: Uncaught Error: Cannot modify readonly property User::$usernameCode language: plaintext (plaintext)

Readonly & typed properties #

A property without a type has the default value of null. For example:

<?php

class User
{
    public $username;
}

$user = new User();
echo $user->username;Code language: PHP (php)

Try it

Output:

NULLCode language: plaintext (plaintext)

In this example, the value of the $username property is null by default until you assign a value to it.

Because of this, PHP only supports readonly on a typed property. If you attempt to use the readonly keyword with a property without a type, you’ll get an error. For example:

<?php

class User
{
    public readonly $username;
}
Code language: PHP (php)

Error:

Fatal error: Readonly property User::$username must have typeCode language: plaintext (plaintext)

Mutability #

A readonly property doesn’t ensure the immutability of objects. Let’s see the following example.

First, define a UserProfile class that has two properties name and phone:

<?php

class UserProfile
{
    public function __construct(private string $name, private string $phone)
    {
    }
    public function changePhone(string $phone)
    {
        $this->phone = $phone;
    }
}Code language: PHP (php)

Second, define the User class that has a property whose type is an object of the UserProfile class:

class User
{
    private readonly string $username;
    private readonly UserProfile $profile;

    public function __construct(string $username)
    {
        $this->username = $username;
    }

    public function setProfile(UserProfile $profile)
    {
        $this->profile = $profile;
    }

    public function profile(): UserProfile
    {
        return $this->profile;
    }
}Code language: PHP (php)

Third, create a User object and assign a profile to it:

$user = new User('joe');
$user->setProfile(new UserProfile('Joe Doe','(408)-555-6666'));Code language: PHP (php)

The $profile is a readonly property of the User class. And you can initialize it once. However, you can change the properties of a readonly property like this:

$user->profile()->changePhone('(408)-999-9999');
var_dump($user->profile());Code language: PHP (php)

Output:

object(UserProfile)#2 (2) {
  ["name":"UserProfile":private]=>  string(7) "Joe Doe"
  ["phone":"UserProfile":private]=>  string(14) "(408)-999-9999"
}Code language: plaintext (plaintext)

Put it all together:

<?php

class UserProfile
{
    public function __construct(private string $name, private string $phone)
    {
    }
    public function changePhone(string $phone)
    {
        $this->phone = $phone;
    }
}

class User
{
    private readonly string $username;
    private readonly UserProfile $profile;

    public function __construct(string $username)
    {
        $this->username = $username;
    }

    public function setProfile(UserProfile $profile)
    {
        $this->profile = $profile;
    }

    public function profile(): UserProfile
    {
        return $this->profile;
    }
}


$user = new User('joe');
$user->setProfile(new UserProfile('Joe Doe','(408)-555-6666'));
$user->profile()->changePhone('(408)-999-9999');
var_dump($user->profile());Code language: PHP (php)

Summary #

  • A readonly property can be initialized once from within the class.
  • Use the readonly keyword in a typed property to make the property readonly.
Did you find this tutorial useful?