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');
echo $user->username;
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();
echo $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.