Php read only properties

PHP Readonly Properties?

In using PHP’s DOM classes (DOMNode, DOMEElement, etc) I have noticed that they possess truly readonly properties. For example, I can read the $nodeName property of a DOMNode, but I cannot write to it (if I do PHP throws a fatal error). How can I create readonly properties of my own in PHP?

It seems that «readonly» is a special keyword that can only be used with classes that are compiled into PHP. Unfortunate, because «readonly public» would be an excellent way to avoid using __get() and __set().

This was considered as an RFC in 2014 (wiki.php.net/rfc/readonly_properties) but was withdrawn after a fair amount of contention (markmail.org/message/7l3ci3sboma2nlzq). I would love to have seen readonly as a keyword for properties, would make life a lot easier instead of constantly defining getters or using the Proxy Pattern

There is a draft rfc currently (Jun 27, 2020) to propose adding readonly features to PHP 8.0: «This is a early draft, currently looking for feedback.» The author’s email is listed & I believe you can email them with suggestions.

readonly was oficially added in PHP8.1 and yes it can be used as @shadowhand suggested + even more it can be used in the constructor-style definition of properties — see stackoverflow.com/a/68376398/1835470 below

7 Answers 7

class Example < private $__readOnly = 'hello world'; function __get($name) < if($name === 'readOnly') return $this->__readOnly; user_error("Invalid property: " . __CLASS__ . "->$name"); > function __set($name, $value) < user_error("Can't set property: " . __CLASS__ . "->$name"); > > 

Only use this when you really need it — it is slower than normal property access. For PHP, it’s best to adopt a policy of only using setter methods to change a property from the outside.

Indeed! The __get method is extremely slow. I had a config class that used something like this, so that every private variable could be accessed but not changed. When I ran my code through a profiler I was shocked at the time it consumed 🙁 Really sad. I wished php had the readonly attribute.

Some years have passed now. New PHP versions are released. Is __get method still too slow in PHP? I’m using PHP 7.0

Читайте также:  Aim bot css v91

If the properties are all private, then the __set() method is not needed, since PHP will catch the property being private by itself. An alternative approach is to make the properties public then use only the __set() method to catch any attempt to set the properties. The __get() method is then not needed since the properties can be read directly.

Since PHP 8.1 there are implemented native readonly properties

You can initialize readonly property only once during the declaration of the property.

Trying to modify the readonly propety will cause following error:

Error: Cannot modify readonly property Test::$prop 

Update PHP 8.2

Since PHP 8.2 you are able to define as readonly a whole class.

readonly class Test < public string $prop; public function __construct(string $prop) < $this->prop = $prop; > > 

The biggest difference in using the new readonly keyword against the const keyword will be visible when it comes to inheritance. Before 8.1 the constant would not be inherited and therefore could only be accessed by it own class. Any inherited class has to implement this constant again or use the parent keyword to call the parent class and wasn’t able to set a different value at initialisation.

this answer should be marked as the correct answer in the current version of PHP! but some frozen, immutable or even data keyword for the classes would be beautiful to apply in things like VO, DTO and to whatever u want to apply whole class immutability

But private properties exposed only using __get() aren’t visible to functions that enumerate an object’s members — json_encode() for example.

I regularly pass PHP objects to Javascript using json_encode() as it seems to be a good way to pass complex structures with lots of data populated from a database. I have to use public properties in these objects so that this data is populated through to the Javascript that uses it, but this means that those properties have to be public (and therefore run the risk that another programmer not on the same wavelength (or probably myself after a bad night) might modify them directly). If I make them private and use __get() and __set(), then json_encode() doesn’t see them.

Wouldn’t it be nice to have a «readonly» accessibility keyword?

Источник

Свойства, доступные только для чтения в PHP 8.1

Написание DTO и VO на PHP с годами стало значительно проще. Взгляните, например, на DTO в PHP 5.6:

title = $title; $this->status = $status; $this->publishedAt = $publishedAt; > /** * @return string */ public function getTitle() < return $this->title; > /** * @return Status */ public function getStatus() < return $this->status; > /** * @return \DateTimeImmutable|null */ public function getPublishedAt() < return $this->publishedAt; > >

И сравните с аналогом в PHP 8.0:

 public function getTitle(): string < return $this->title; > public function getStatus(): Status < return $this->status; > public function getPublishedAt(): ?DateTimeImmutable < return $this->publishedAt; > >

Видна огромная разница, хотя я думаю, что есть ещё одна большая проблема: все эти методы чтения. Лично я их больше не использую, начиная с PHP 8.0, в котором добавили определение свойств в конструкторе. Я предпочитаю использовать общедоступные свойства вместо написания методов чтения:

Читайте также:  Find one element in array javascript

Однако объектно-ориентированным пуристам такой подход не нравится: внутренний статус объекта не должен быть раскрыт напрямую и определённо не может быть изменён извне.

В наших проектах в Spatie есть внутреннее руководство по написанию кода, согласно которому DTO и VO с общедоступными свойствами не должны изменяться извне. Подход, который, кажется, работает вполне неплохо, мы используем его уже довольно давно, не сталкиваясь с какими-либо проблемами.

Однако да, я согласен с тем, что было бы лучше, если бы язык гарантировал, что общедоступные свойства вообще не могут быть переопределены. Что ж, в PHP 8.1 решили эту проблему, добавив ключевое слово readonly :

Как и предполагает его название, смысл ключевого слова в том, что после того, как свойство установлено, его больше нельзя переопределить:

title = 'Какой-то другой заголовок'; // Ошибка: Нельзя переопределить readonly-свойство BlogData::$title

Знание, что когда объект инициализирован, он больше не будет меняться, даёт нам определённый уровень уверенности и спокойствия при написании кода: целый ряд непредвиденных изменений данных просто не может произойти.

Конечно, по-прежнему нужна возможность клонировать объект и, возможно, изменять некоторые свойства в процессе. Далее мы обсудим, как это сделать со свойствами, доступными только для чтения. Для начала, давайте рассмотрим их подробнее.

Только типизированные свойства

Свойства, доступные только для чтения могут быть только типизированными:

Однако вы можете использовать тип mixed для указания типа:

Причина этого ограничения заключается в том, что, опуская тип свойства, PHP автоматически устанавливает значение null , если в конструкторе не было определено явное значение. Такое поведение в сочетании со свойством, доступным только для чтения вызовет ненужную путаницу.

Обычные объекты и объекты с определением свойств в конструкторе

Вы уже видели примеры и того и другого: readonly можно добавить как к обычному, так и к свойству, определяемому в конструкторе:

Читайте также:  Java javax script scriptengine

Нет значения по умолчанию

У свойств, доступных только для чтения не может быть значения по умолчанию:

Точнее, если это не свойство, определяемое в конструкторе:

Причина, по которой это разрешено для свойств, определяемых в конструкторе, заключается в том, что значение по умолчанию в этом случае используется не в качестве значения по умолчанию для свойства класса, а только для аргумента конструктора. Под капотом приведённый выше код будет преобразован в этот:

Посмотрите, как фактическому свойству не присваивается значение по умолчанию. Причина запрета использования значений по умолчанию для свойств, доступных только для чтения, заключается в том, что в таком виде они ничем не будут отличаться от констант.

Наследование

Нельзя изменять флаг readonly при наследовании:

Правило действует в обоих направлениях: вам не разрешено добавлять или удалять флаг readonly при наследовании.

Unset не допускается

После того как свойство, доступное только для чтения установлено, вы не можете его изменить и даже сбросить:

prop); // Ошибка: Нельзя сбросить свойство, доступное только для чтения

Reflection

Добавлен новый метод ReflectionProperty::isReadOnly() , а также флаг ReflectionProperty::IS_READONLY .

Клонирование

Итак, если нельзя изменить свойства, доступные только для чтения, и, если нельзя их сбросить, каким образом можно создать копию своих DTO или VO и изменить какие-то данные? Также нельзя использовать clone , потому что вы не сможете перезаписать их значения.

На самом деле есть идея добавить в будущем конструкцию clone with , которая допускает такое поведение, но сейчас проблема не решена.

Что ж, можно клонировать объекты с изменёнными свойствами, доступными только для чтения, если полагаться на магию Reflection. Создавая объект без вызова его конструктора (что возможно с помощью Reflection), а затем вручную копируя каждое свойство, иногда перезаписывая значение, вы фактически можете «клонировать» объект и изменить его свойства, доступные только для чтения.

Для этого я разработал небольшой пакет, вот как он выглядит:

 > $dataA = new BlogData('Title'); $dataB = $dataA->with(title: 'Another title');

Также я написал специальный пост в блоге, объясняющий всю механику.

Вот и всё, что можно сказать о свойствах, доступных только для чтения. Я думаю, что это отличная возможность, при работе над проектами со множеством DTO и VO и требующими от вас тщательного управления потоком данных во всем коде. Неизменяемые объекты со свойствами, доступными только для чтения очень в этом помогут.

Источник

Оцените статью