PHP Readonly
В PHP 8.1 появилось Readonly свойства класса (только для чтения). Свойства только для чтения позволяют определять свойства, которые могут быть инициализированы только один раз в пределах класса.
Чтобы определить свойство, доступное только для чтения, используйте readonly
ключевое слово в типизированном свойстве:
<?php
class MyClass
{
private readonly type propertyName;
}
Или
<?php
class MyClass
{
public function __construct(private readonly type propertyName)
{
}
}
Например, следующий код определяет User
класс с $username
свойством, доступным только для чтения:
<?php
class User
{
public readonly string $username;
public function __construct(string $username)
{
$this->username = $username;
}
}
В этом примере вы можете инициализировать только $username
один раз при создании нового User
объекта следующим образом:
$user = new User('joe','secure');
Если вы попытаетесь изменить $username
свойство после этого, вы получите сообщение об ошибке:
$user->username = 'john';
Ошибка:
Fatal error: Uncaught Error: Cannot modify readonly property User::$username
PHP позволяет инициализировать $username
свойство только изнутри самого класса, либо из конструктора , либо из метода.
Следующий пример также вызывает ошибку, поскольку он пытается инициализировать $username
свойство извне класса User:
<?php
class User
{
public readonly string $username;
}
$user = new User();
$user->username = 'joe';
Ошибка:
Fatal error: Uncaught Error: Cannot initialize readonly property User::$username from global scope
Чтобы исправить это, вы можете добавить конструктор, как в примере выше. В качестве альтернативы вы можете определить метод для инициализации свойства readonly из класса следующим образом:
<?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');
Обратите внимание: если вы вызовете setUsername()
метод второй раз, вы получите ошибку, поскольку он изменяет свойство username, которое уже было инициализировано.
<?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');
Ошибка:
Fatal error: Uncaught Error: Cannot modify readonly property User::$username
Readonly и типизированные свойства
Свойство без типа имеет значение по умолчанию null
. Например:
<?php
class User
{
public $username;
}
$user = new User();
var_dump($user->username);
Вывод:
NULL
В этом примере значение свойства $username
будет null
по умолчанию, пока вы не присвоите ему значение.
Из-за этого PHP поддерживает только readonly для типизированного свойства. Если вы попытаетесь использовать readonly
ключевое слово со свойством без типа, вы получите ошибку. Например:
<?php
class User
{
public readonly $username;
}
Ошибка:
Fatal error: Readonly property User::$username must have type
Изменчивость
Свойство readonly не гарантирует неизменяемости объектов. Давайте рассмотрим следующий пример.
Сначала определите класс UserProfile
имеющий два свойства name и phone
<?php
class UserProfile
{
public function __construct(private string $name, private string $phone)
{
}
public function changePhone(string $phone)
{
$this->phone = $phone;
}
}
Во-вторых, определите User
класс, имеющий свойство, тип которого является объектом класса UserProfile
:
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
объект и назначьте ему профиль:
$user = new User('joe');
$user->setProfile(new UserProfile('Joe Doe','(408)-555-6666'));
Язык кода: PHP ( php )
$profile
свойство класса , доступное только для чтения User
. И вы можете инициализировать его один раз. Однако вы можете изменить свойства свойства, доступного только для чтения, следующим образом:
$user->profile()->changePhone('(408)-999-9999');
var_dump($user->profile());
Вывод:
object(UserProfile)#2 (2) {
["name":"UserProfile":private]=> string(7) "Joe Doe"
["phone":"UserProfile":private]=> string(14) "(408)-999-9999"
}
Соединим все вместе:
<?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());
Краткое содержание
- Свойство
readonly
, можно инициализировать один раз внутри класса. - Используйте
readonly
ключевое слово в типизированном свойстве, чтобы сделать свойство доступным только для чтения.