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 there are still some common scenarios where you'll need to use magic methods or methods for properties. Namely disconnected write vs read visibility for properties, like readonly, writeonly or immutable like semantic. This requires unnecessary boilerplate, makes usage less ergonomic and in the case of magic methods 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 what was proposed on internals mailing list in “RFC Proposal - Attributes read/write visibility” by Amaury Bouchard, on 15 July 2012. The proposal was made as part of the ongoing discussions around Readonly properties and Property Accessors Syntax, who where proposing different ways to deal with among others write visibility, and in the latter case also type hints.

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.

Write once

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 (2012, Declined) does.

However with what being proposed here and with typed properties, most common use cases needed for properties are covered in a simpler, arguably more PHP like way.

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.

In order to avoid backwards compatibility issue, the following methods will get updated behavior:

- ReflectionProperty::isPrivate() — Checks if property is private for read and write visibility, or one of them - ReflectionProperty::isProtected() - Checks if property is protected for read and write visibility, or one of them with the remaining being public - ReflectionProperty::isPublic() - Checks if property is public for read and write visibility

The following methods needs to be added to detect different read vs write visibility:

- 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::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

TODO: Reflection::getModifiers() and Reflection::getModifierNames() will need adaption too

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.

If we where to just have two separate visibility keywords on properties, where second one will denote write visibility, that can cause issues for API's such as Reflection::getModifierNames(), and arguably lowers readability.

One other alternative would be to align with, or take inspiration from |Swift setter access level syntax.

Backward Incompatible Changes

Code that acts on Reflection to check for visibility, should be adapted to take advantage of the more fine grained read or write visibility check methods.

Proposed PHP Version(s)

Next PHP version, 8.0 suggested.

Impact on extensions

More 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 to be adapted.

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-28 Simplify Reflection API proposal, add syntax alternatives for discussion
  • 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.1593426466.txt.gz · Last modified: 2020/06/29 10:27 by andrerom