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)

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)

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::$username
Code 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 scope
Code 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');
Code language: PHP (php)

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::$username
Code 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(); var_dump($user->username);
Code language: PHP (php)

Output:

NULL
Code 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 type
Code 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?