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.