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 ключевое слово в типизированном свойстве, чтобы сделать свойство доступным только для чтения.