rasmus

Rasmus Lerdorf

Contents

PHP RFC: Property type-hints

Introduction

With the addition of type-checked function return values in PHP 7, we are one step closer to PHP being a gradually-typed language. This RFC proposes to close that gap with the addition of optional type-checked properties, by allowing the use of static type-hints for properties.

Given that objects expose two types of public members, methods and properties, with optional type-checking for function arguments already present, and with the addition of type-checking for function return-values in PHP 7, the addition of type-checking for public properties is natural, and makes OOP in PHP fully gradually-typed.

The significance of type-checked properties is a given, as demonstrated by it's inclusion in Hack, as well as other recent scripting languages, including Typescript, Dart and ActionScript. The need for type-hinting is demonstrated by the widespread use of php-doc, and support for such type-hinting in modern PHP IDEs.

The proposed syntax is compatible with that of Hack, and is a natural addition to the language, resembling the syntax set forth by other gruadually-typed (and statically-typed) languages.

Proposal

The only proposed change to the syntax, is the addition of an optional type-hint in property declarations - for example:

class BoxedInt {
    public int $value;
}

This declares a public property $value, which can only be initialized or set with an integer value.

Scalar type hints (e.g. int, float, bool, string as supported by PHP 7) are permitted, as well as any class or interface name.

The precise behavior as far as type-checking and type-conversions is dependent on the |strict_types directive - that is, the precise behavior when setting a property, is consistent with that of scalar type-hints in function parameters.

Violating the type-check when initializing or setting a property, will result in a catchable fatal error:

$i = new BoxedInt;
$i->value = 'oops';
// Catchable fatal error: property BoxedInt::$value must be of the type integer, string given

Consistent with return type-check violations, and scalar type-hinting, this example will generate an E_RECOVERABLE_ERROR.

Property References

Type-checking is performed even if the property is indirectly modified via a property-reference. For example:

$i = new BoxedInt();
$ref = &$i->value;
$ref = 'oops'; // Catchable fatal error

This behavior guarantees the integrity of a type-checked property, even if modified indirectly.

Property Initialization

If a given property is initialized with a scalar value, the constant scalar expression will be type-checked at construction time. For example:

class BoxedInt {
    public int $value = 'zero'; // NOTE: does not generate an error at load-time
}
 
$i = new BoxedInt(); // Catchable fatal error

Note that, while some languages would generate a parse error for the declaration itself, such behavior would not be practical in PHP, where, for example, initialization with a scalar expression such as Foo::BAR would trigger immediate auto-loading of Foo, which could have surprising performance implications.

Also note that the proposed behavior is consistent with initialization of constants, in the sense that e.g. const FOO = Bar::BAZ will not evaluate at load-time (which would cause auto-loading of Bar) but rather will initialize when first accessed. Similarly, property initializations will evaluate (with type-checking) when the new operator is used.

ReflectionProperty

ReflectionProperty needs an addition (consistent with ReflectionParameter) to reflect the type-hint, e.g.:

ReflectionParameter implements Reflector {
    ...
    public ReflectionType getType ( void )
    ...
}

Note that type-checking (and/or scalar type-conversion, depending on the strict_mode directive) will also be performed if a property is modified via the |ReflectionProperty::setValue() method.

TBD: Another RFC is in the works, which could affect the Reflection API changes proposed by this RFC.

Non-features

The inclusion of a var keyword (to be used in place of a type-hint, which would infer the property type-hint from the initialization) was considered, but is not part of this RFC. As per the description of property initialization behavior above, the expression used for initialization of a property is not evaluated until the new keyword is applied - hence, explicit type-inference in this manner isn't possible, since the property type needs to be available for reflection immediately after loading, e.g. prior to creation of an instance.

Backward Incompatible Changes

None.

Proposed PHP Version(s)

Next PHP 7.x.

RFC Impact

To Opcache

The impact on opcache needs to be examined.

To Zend Engine

Static type-hints could enable some engine optimizations - this should be investigated.

Open Issues

None

Unaffected PHP Functionality

The introduction of an optional type-hint does not affect legacy PHP code.

To preserve backwards compatibility, existing standard library classes will not have property type-hints added to them.

Future Scope

A future version of this proposal might include the addition of a new pseudo-type (“any” or “mixed”) which would allow developers to indicate that they have thought about the type-safety of a given property; having no type-hint could indicate that you simply didn't think about it. (Currently, developers may deal with this issue by explicitly type-hinting as “mixed” using php-doc.)

Proposed Voting Choices

TBD

Patches and Tests

This RFC needs a volunteer to help with prototyping and implementation.

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

None

Rejected Features

None