Ассиметричная видимость свойств в PHP 8.4
В PHP 8.4 появилась новая возможность — асимметричная видимость свойств. Она позволяет задавать разные уровни доступа на чтение и запись. В статье — обзор синтаксиса, примеры из реального кода и рекомендации по применению.

Всем привет! Меня зовут Никита, и я Backend-разработчик в YCLIENTS. Как и многие современные компании, мы стремимся идти в ногу со временем, поэтому активно внедряем актуальные и стабильные технологии. Например, не так давно мы перешли на PHP 8.4 — и, надо сказать, этот релиз принёс немало интересных нововведений.
Сегодня я хочу рассказать вам об одной из его фишек — асимметричной видимости свойств. Кратко, это возможность задавать разные уровни доступа для чтения и записи у одного и того же свойства. Пока что функция не слишком распространена в реальных проектах, но, думаю, со временем ситуация изменится.
Давайте разберёмся, как это работает и где может пригодиться!
Что такое ассиметричная видимость?
В PHP 8.4 вы можете задать разные уровни видимости для геттера и сеттера свойства. Например, свойство может быть доступно для чтения публично, но доступно для записи только внутри класса или его подклассов. Это позволяет скрыть детали реализации, предоставляя при этом доступ к данным.
Кстати, если вы не хотите, чтобы значение свойства можно было изменить после установки, возможно, вам лучше использовать свойства с модификатором readonly
.
Асимметричная видимость свойства задаётся с использованием следующего синтаксиса:
Синтаксис
[GETTER_VISIBILITY] [SETTER_VISIBILITY(set)] [TYPE] $propertyName;
Где:
GETTER_VISIBILITY
— может бытьpublic
,protected
илиprivate
.SETTER_VISIBILITY(set)
— может бытьpublic
,protected
илиprivate
.TYPE
— тип свойства (например,string
,int
,array
и т.д.).
Например, чтобы определить строковое свойство с protected видимостью для чтения и приватной видимостью для записи, мы бы написали:
protected private(set) string $propertyName;
В этом случае это означает, что свойство может быть прочитано внутри класса или его подклассов, но может быть изменено только внутри самого класса.
Как использовать асимметричную видимость в PHP
Чтобы полностью понять, как использовать асимметричную видимость в PHP, давайте рассмотрим пример класса, в котором она применяется.
Мы создадим простой класс Appointment
(мы же всё-таки про онлайн-запись 😀) с тремя свойствами: clientName
(имя клиента), phone
(номер телефона клиента) и comment
(комментарий к записи). Каждое свойство будет иметь разные уровни видимости для геттера и сеттера.
Класс Appointment
будет выглядеть следующим образом:
Пример
class Appointment
{
public function __construct(
public protected(set) string $clientName,
public private(set) string $phone,
protected private(set) string $comment,
) {}
}
Теперь давайте возьмём наш класс Appointment
и посмотрим, как можно читать и записывать свойства с асимметричной видимостью.
Чтение свойств с асимметричной видимостью
Начнём с того, как можно читать свойства, у которых заданы разные уровни доступа для чтения и записи. Для этого мы создадим экземпляр класса Appointment
и попытаемся получить доступ к его свойствам:
Чтение свойств с ассиметричной видимостью
$appointment = new Appointment(
clientName: 'Никита',
phone: '+79001234567',
comment: 'Опоздает на 5 минут',
);
echo $appointment->clientName; // ✅ Доступно публично
echo $appointment->phone; // ✅ Доступно публично
echo $appointment->comment; // ❌ Ошибка: свойство protected
Как видно из примера, мы можем получить доступ к свойствам clientName
и phone
публично. Однако мы не можем получить доступ к свойству comment
извне класса, потому что оно имеет защищённую (protected) видимость. Это означает, что читать его можно только внутри самого класса или в его подклассах.
Попытка обращения к свойству comment
снаружи приведёт к ошибке со следующим сообщением:
Fatal error: Uncaught Error: Cannot access protected property Appointment::$comment in /in/qskLk:20
Чтобы получить доступ к свойству comment
, нам нужно создать метод внутри класса Appointment
(или в классе, который наследует Appointment
), который будет возвращать значение этого свойства. Например:
class Appointment
{
public function __construct(
public protected(set) string $clientName,
public private(set) string $phone,
protected private(set) string $comment,
) {}
public function getComment(): string
{
return $this->comment;
}
}
Теперь мы сможем получить содержимое статьи, вызвав метод getComment
:
$appointment = new Appointment(
clientName: 'Никита',
phone: '+79001234567',
comment: 'Опоздает на 5 минут',
);
echo $appointment->getComment(); // Комментарий к записи
Запись в свойства с асимметричной видимостью
На примере нашего класса Appointment
, давайте посмотрим, как можно записывать значения в свойства с асимметричной видимостью.
Мы определили свойство comment
как protected private(set) string $comment
, что означает, что у него приватный сеттер. Это значит, что если мы попытаемся напрямую изменить значение свойства comment
из вне класса, возникнет ошибка. Например:
$appointment = new Appointment(
clientName: 'Никита',
phone: '+79001234567',
comment: 'Опоздает на 5 минут',
);
// ❌ Мы не можем публично изменить значение свойства comment, так как у него protected видимость
$appointment->comment = 'Успевает во время';
Попытка выполнить приведённый выше код вызовет ошибку со следующим сообщением:
Fatal error: Uncaught Error: Cannot access protected property Appointment::$comment in /in/ZQuG4:19
Вместо этого, в таком случае, нам стоит создать метод-сеттер внутри класса Appointment
, который позволит устанавливать значение свойства comment
. Например:
class Appointment
{
public function __construct(
public protected(set) string $clientName,
public private(set) string $phone,
protected private(set) string $comment,
) {}
public function setComment(string $comment): void
{
$this->comment = $comment;
}
}
После этого мы сможем использовать этот метод следующим образом:
$appointment = new Appointment(
clientName: 'Никита',
phone: '+79001234567',
comment: 'Опоздает на 5 минут',
);
$appointment->setComment('Ну точно успевает во время');
Пропуск публичного геттера
Если свойство имеет публичную видимость для чтения, можно опустить указание видимости геттера и объявить только видимость сеттера. Это удобно для свойств, которые должны быть доступны для чтения публично, но изменяться только внутри класса или его подкласса.
Например, рассмотрим следующий класс:
class Appointment
{
public function __construct(
public protected(set) string $clientName,
public private(set) string $phone,
protected private(set) string $comment,
) {}
}
Мы можем переписать определения свойств следующим образом:
class Appointment
{
public function __construct(
protected(set) string $clientName,
private(set) string $phone,
protected private(set) string $comment,
) {}
}
В приведённом выше примере мы убрали явное указание публичной видимости геттера у свойств clientName
и phone
, поскольку они и так читаются публично. Однако для свойства comment
пришлось оставить protected видимость геттера.
Особенности асимметричной видимости в PHP
При использовании асимметричной видимости в PHP стоит учитывать несколько важных моментов:
Только типизированные свойства
Асимметричную видимость можно задавать только для типизированных свойств. Это значит, что данная возможность не работает с нетипизированными свойствами.
Например, рассмотрим класс, который объявляет свойство phone
с асимметричной видимостью, но без указания типа:
class Appointment
{
public function __construct(
protected(set) string $clientName,
private(set) $phone,
protected private(set) string $comment,
) {}
}
Попытка выполнить приведённый выше код вызовет ошибку со следующим сообщением:
Fatal error: Property with asymmetric visibility Appointment::$phone must have type in /in/YHqBi on line 4
set
Видимость должна быть более ограниченной чем get
Видимость
Видимость сеттера должна быть такой же или более ограниченной, чем видимость геттера. Например, нельзя объявить свойство с видимостью protected public(set)
, так как это означало бы, что сеттер (public
) доступен шире, чем геттер (protected
), что недопустимо.
Рассмотрим пример класса, в котором свойство phone
объявлено с неправильной асимметричной видимостью:
class Appointment
{
public function __construct(
protected(set) string $clientName,
private protected(set) string $phone,
protected private(set) string $comment,
) {}
}
Попытка выполнить приведённый выше код вызовет ошибку со следующим сообщением:
Fatal error: Visibility of property Appointment::$phone must not be weaker than set visibility in /in/3LFGh on line 4
Свойства с private(set)
являются final
Если свойство объявлено с приватным сеттером (private(set)
), оно автоматически считается final. Это значит, что такое свойство нельзя переопределить в классе-наследнике.
Например, рассмотрим следующий пример, где мы пытаемся переопределить свойство phone
в дочернем классе:
class Appointment
{
public function __construct(
public string $clientName,
private(set) string $phone,
protected private(set) string $comment,
) {}
}
class SubAppointment extends Appointment
{
private string $phone;
// ...
}
$appointment = new SubAppointment(
clientName: 'Никита',
phone: '+79001234567',
comment: 'Записывает сына',
);
В приведённом выше примере мы попытались переопределить свойство phone
в классе SubAppointment
. Однако, поскольку свойство phone
имеет видимость private(set)
, оно считается final, и при попытке запуска такого кода будет выброшена ошибка:
Fatal error: Cannot override final property Appointment::$phone in /in/FJ8DF on line 11
Заключение
Асимметричная видимость в PHP 8.4 — это небольшой, но очень полезный инструмент в арсенале разработчика. Он позволяет делать код более гибким и безопасным, чётко разделяя доступ к свойствам на чтение и запись. Пока что фича не получила массового распространения, но, уверен, со временем её начнут применять чаще — особенно в проектах, где важна инкапсуляция и контроль доступа.
Если вы ещё не пробовали асимметричную видимость в деле — самое время поэкспериментировать! Возможно, именно она поможет вам сделать код чище и удобнее.