rfc:property_write_visibility

This is an old revision of the document!


PHP RFC: Property write visibility

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

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
rfc/property_write_visibility.1593111409.txt.gz · Last modified: 2020/06/25 18:56 by andrerom