This is an old revision of the document!
PHP RFC: Property write visibility
- Version: 0.3.1
- Date: 2020-06-25
- Author: André Rømcke andre.romcke+php@gmail.com
- Proposed PHP version: PHP 8.0
- Status: Draft
- First Published at: http://wiki.php.net/rfc/readonly_and_immutable_properties
- Discussion: https://externals.io/message/ coming
Introduction
With the introduction of typed properties in PHP 7.4, properties have become far more powerful. However it is currently not possible to specify disconnected write vs read visibility for properties, such as for readonly, writeonly or immutable like semantic, without having to resort to magic methods (getters and setters). This requires unnecessary boilerplate, makes usage less ergonomic and hurts performance.
This RFC resolves this issue by proposing to allow classes to optionally declare property write visibility, disconnected from read visibility.
Under this RFC, code like
class User { private int $id; protected string $name; public function __construct(int $id, string $name) { $this->id = $id; $this->name = $name; } public function __get($property) { if (property_exists($this, $property)) { // We return value here as non public properties are "readonly" in this class return $this->$property; } throw new PropertyNotFoundException($property, static::class); } public function __set($property, $value) { if (property_exists($this, $property)) { // Here private/protected property is attempted accessed outside allowed scope, so we throw throw new PropertyReadOnlyException($property, static::class); } throw new PropertyNotFoundException($property, static::class); } public function __isset($property) { return property_exists($this, $property); } public function __unset($property) { $this->__set($property, null); } }
might be written as
class User { public:private int $id; public:protected string $name; public function __construct(int $id, string $name) { $this->id = $id; $this->name = $name; } }
Main differences to previous proposals
This RFC is inspired by past RFCs and the discussions that happened around them. Especially credit to Readonly properties by Andrea Faulds, and discussions that happened around the different flavors of Property Accessors Syntax.
Readonly
This RFC allows for among others semantics proposed in Readonly properties (2014, Withdrawn), by using public:protected
.
Immutability
This RFC allows for use cases similar to what was proposed in Immutability (2018, Stale), by using for instance public:private
.
This RFC does not align with the semantics of the recent Write once properties (2020, Declined), which is targeting a different problem.
Property Accessors Syntax
This RFC does not try to solve as wide use case as the different iterations of Property Accessors Syntax does.
However Accessors overcomplicates uses cases which only cares about having disconnected read and write visibility, and introduces completely different semantics to properties (a change from C#'s “Fields” to “Properties”).
Proposal
This proposal adds support for enforced write visibility checks for declared properties. The following example illustrates the basic syntax:
class User { // Property is readonly in protected and public scope public:private int $id; // Property is readonly in public scope public:protected string $name; // Property is write-only in public and protected scope private:public string $newName; public function __construct(int $id, string $name) { $this->id = $id; $this->name = $name; } }
The format is <read_visibility>:<write_visibility>
, and if you omit the last visibility value you will like before implicit set both read and write visibility at once.
References
Attempting to pass a property value outside of allowed writable scope as a reference, results in an error.
Reflection
When using reflection, methods such as ReflectionProperty::setAccessible()
will work as before, it will implicit set visibility for both read and write.
However with this proposal the following existing methods will represent only read visibility for cases where it differs: ReflectionProperty::isPrivate()
, ReflectionProperty::isProtected()
and ReflectionProperty::isPublic()
.
AS such the following methods should be added:
- ReflectionProperty::isWritePrivate()
— Checks if property is writable in private
- ReflectionProperty::isWriteProtected()
— Checks if property is writable in protected
- ReflectionProperty::isWritePublic()
— Checks if property is writable in public
- ReflectionProperty::setReadAccessible()
— Set property read accessibility
- ReflectionProperty::setWriteAccessible()
— Set property write accessibility
TODO: Reflection::getModifiers()
and Reflection::getModifierNames()
will need adaption too, for latter method whats most natural is that it returns format as written in language syntax.
Discussions
Language Syntax
The format being proposed here <read_visibility>:<write_visibility>
is open for discussion if for instance this causes problems for parser, or is deemed otherwise in-practical. A possible other alternative is to simply allow two separate visibility keywords on properties, where second one will denote write visibility.
Reflection API
One clear alternative for this part of the proposal is to deprecate todays isPublic()
, isProtected()
and isPrivate()
methods. And introduce new methods both for write and read. If so the following methods should also be added:
- ReflectionProperty::isReadPrivate()
— Checks if property is readable in private
- ReflectionProperty::isReadProtected()
— Checks if property is readable in protected
- ReflectionProperty::isReadPublic()
— Checks if property is readable in public
Backward Incompatible Changes
Code that expects to be able to make properties writeable via reflection will in some cases have to adapt, once code is start to take advantage of this new feature.
While ReflectionProperty::setAccessible()
will still work like before, checks using isPublic()
', 'isProtected() or
isPrivate()'' won't detect if class has other visibility for write.
Proposed PHP Version(s)
Next PHP version, 8.0 suggested.
Impact on extensions
More of future extension code, and possible SPL code, can be written in PHP instead. This is in-line with other features already accepted for PHP 8.0.
Besides that existing PHP extensions working with visibility on properties will need adaptions.
Performance
Performance tests will need to be done once there is an implementation of this. Both for overhead on properties, as well as measuring benefit over using magic methods.
Vote
As this is a language change, a 2/3 majority is required.
References
- C# readonly fields, semantically similar to what is proposed as “immutable” here.
Errata
If there are any edge-cases found during implementation, they will appear here.
Changelog
Significant changes to the RFC are noted here.
- 2020-06-25 Focus on write visibility proposal
- 2020-06-20 Initial early draft to get feedback on direction between visibility, readonly/immutable keywords or attributes