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::$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)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)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
readonlykeyword in a typed property to make the property readonly.