Table of Contents

PHP RFC: Readonly Properties

Introduction

There is currently no way to make a property readable to everyone and writeable only to the containing object, with PHP's visibility specifiers allowing all or nothing: a scope can either both read and write, or do neither. While __get and __set exist, these do not actually allow control of the same property, merely only exposing a separate property, and they are only usable for undeclared properties, which cannot be reflected and are not performant.

Because of this, getter and setter functions to control the ability to write to properties are common. This requires developers to write boilerplate code, even if assisted by IDEs, and is not as performant as a raw property.

Proposal

To deal with this, a new modifier keyword is introduced for properties, readonly. It allows the property to be read from the normal scope of its visibility, so a public readonly property is readable from the public scope, i.e. anywhere. However, it can only being written to from the next most restrictive scope, so a public readonly property is writeable from the protected scope, and a protected readonly property only from the private scope.

Due to implementation difficulties, this doesn't support for static properties for now. This may yet change, however. Properties cannot be both private and readonly, since there is no more restrictive scope than private.

This keyword's behaviour should not be confused with that of C#'s readonly specifier, which makes a property writeable only once, from a class's constructor. While the possible confusion this could produce is unfortunate, I could not think of a better name after much discussion. I don't think this precludes inclusion of a feature similar to that of C# under another name as there are other possible names for that feature, such as immutable or final. It's also worth noting that readonly is already used for some classes (such as the DOM) in the PHP manual.

Example

Let's take this class:

class Stack {
    public readonly $size = 0;
    private $elements = [];
    public function push($elem) {
        $this->size++;
        $this->elements[] = $elem;
    }
    public function pop() {
        $this->size--;
        return array_pop($this->elements);
    }
}

If we were to create an instance of this class and try to change size we would get an error, as we cannot write to the public readonly property from the public scope:

Fatal error: Cannot write to readonly public property Stack::$size in example.php on line 1

However, calling push would not error, as it changes $size from inside the class, which falls within the protected scope.

Backward Incompatible Changes

readonly is now a reserved word, so it can't be used as a class, function, method or constant name.

It is possible that this could be changed so class, constant and function names are unaffected, but due to syntactical ambiguity, this couldn't be allowed for methods.

Proposed PHP Version(s)

This is proposed for the next major version of PHP, currently PHP 7.

Future Scope

Properties in interfaces are currently not supported. If they were later to be supported (perhaps with getters/setters being added), readonly could be used to avoid compelling implementing classes from making a property writeable, but not preventing them from expanding the interface to do so, e.g.:

interface Point {
    readonly $x, $y;
}
 
class ImmutablePoint implements Point {
    public readonly $x, $y;
    ...
}
 
class MutablePoint implements Point {
    public $x, $y;
    ...
}

RFC Impact

To Existing Extensions

Reflection has been updated, with the method ReflectionProperty::isReadonly() being added.

To Opcache

The RFC's compatibility with opcache is yet to be verified. It merely adds a new ZEND_ACC_* flag so support shouldn't be a problem, however.

Unaffected PHP Functionality

This does not affect methods or static properties.

Proposed Voting Choices

As this is a language change, a 2/3 majority is required.

Patches and Tests

A pull request against master is here: https://github.com/php/php-src/pull/879

It contains a full set of twelve different tests. There are no known issues at present.

A pull request for the language specification is here: https://github.com/php/php-langspec/pull/86

It currently lacks tests.

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged to
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature

References

Rejected Features

Keep this updated with features that were discussed on the mail lists.

Changelog

* v0.1.1 - Added Future Scope
* v0.1 - Creatd