rfc:mixed_vs_untyped_properties
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:mixed_vs_untyped_properties [2023/11/16 18:30] – imsop | rfc:mixed_vs_untyped_properties [2023/11/20 23:02] (current) – imsop | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Harmonise " | ====== PHP RFC: Harmonise " | ||
- | * Version: 0.2 | + | * Version: 0.5 |
- | * Date: 2023-11-16 | + | * Date: 2023-11-20 |
* Author: Rowan Tommins < | * Author: Rowan Tommins < | ||
* Status: Draft | * Status: Draft | ||
Line 8: | Line 8: | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | This RFC proposes to remove the distinction between " | + | This RFC proposes to remove |
PHP currently has three primary ways of adding properties to an object: | PHP currently has three primary ways of adding properties to an object: | ||
Line 16: | Line 16: | ||
- Declared with a type. In addition to being allocated on every instance, the property is covered by extra guards on assignment to guarantee its type. If it is never assigned a value, or passed to '' | - Declared with a type. In addition to being allocated on every instance, the property is covered by extra guards on assignment to guarantee its type. If it is never assigned a value, or passed to '' | ||
- | The different behaviours of these properties are largely a result of the history of the language, rather than a consistent design. In particular, with [[rfc: | + | The different behaviours of these properties are largely a result of the history of the language, rather than a consistent design. In particular, with [[rfc: |
===== Current Behaviour ===== | ===== Current Behaviour ===== | ||
+ | |||
+ | ==== Initial State and Unset ==== | ||
The three types of property vary in their //initial// state, and in their state //after calling '' | The three types of property vary in their //initial// state, and in their state //after calling '' | ||
Line 24: | Line 26: | ||
The states can be summarised in this table: | The states can be summarised in this table: | ||
- | ^ Property Declaration | + | ^ Property Declaration |
- | | '' | + | | <php># |
- | | '' | + | | <php>private $foo;</ |
- | | '' | + | | <php>private mixed $foo;</ |
\\ | \\ | ||
Where: | Where: | ||
Line 45: | Line 47: | ||
^ ^ '' | ^ ^ '' | ||
- | ^ True undefined | + | ^ Not declared on class |
^ Declared then '' | ^ Declared then '' | ||
^ Typed and uninitialized | ^ Typed and uninitialized | ||
+ | ==== Variance under inheritance ==== | ||
+ | |||
+ | In a " | ||
+ | |||
+ | * A method parameter with no type specified is equivalent to '' | ||
+ | * A method which specifies no //return type// may return any type, or the pseudo-types '' | ||
+ | |||
+ | Properties are // | ||
+ | |||
+ | <code php> | ||
+ | class A { | ||
+ | /** @var mixed $untyped */ | ||
+ | public $untyped; | ||
+ | | ||
+ | public mixed $mixed; | ||
+ | | ||
+ | /** @param mixed $a */ | ||
+ | public function acceptsUntyped($a) {} | ||
+ | | ||
+ | public function acceptsMixed(mixed $a) {} | ||
+ | | ||
+ | public function returnsMixed(): | ||
+ | | ||
+ | /** @return mixed|void|never */ | ||
+ | public function returnsUntyped() {} | ||
+ | } | ||
+ | |||
+ | class B extends A { | ||
+ | # Not Allowed: considers " | ||
+ | public mixed $untyped; | ||
+ | # Also Not Allowed | ||
+ | public $mixed; | ||
+ | | ||
+ | # Allowed: unspecified parameter type is implicitly " | ||
+ | public function acceptsUntyped(mixed $a) {} | ||
+ | # Also Allowed | ||
+ | public function acceptsMixed($a) {} | ||
+ | | ||
+ | # Not Allowed: widens return type from " | ||
+ | public function returnsMixed() {} | ||
+ | | ||
+ | # Allowed: narrows return type from implicit " | ||
+ | # explicit return types of " | ||
+ | public function returnsUntyped(): | ||
+ | } | ||
+ | </ | ||
===== Proposal ===== | ===== Proposal ===== | ||
Line 69: | Line 117: | ||
> Property %s::%s must not be accessed before initialization | > Property %s::%s must not be accessed before initialization | ||
- | ==== Declared properties will no longer default to null ==== | + | ==== Properties with no declared type will be analysed as '' |
- | The remaining difference | + | If no type is specified for a property, its type will be analysed as '' |
- | - Initialize any property to '' | + | Consequently, the following code will be valid: |
- | - Never initialize a property to '' | + | |
- | Option 1 has the advantage of not causing errors in any currently valid code; but it goes against the general trend of making the language stricter and more explicit. | + | <code php> |
+ | class Parent { | ||
+ | public $foo; | ||
+ | public mixed $bar; | ||
+ | } | ||
+ | class Child extends Parent { | ||
+ | public mixed $foo; | ||
+ | public $bar; | ||
+ | } | ||
+ | </ | ||
- | Option 2 is a non-trivial breaking change, for which a deprecation period | + | ==== Properties with no declared type will continue |
- | > Accessing | + | Whereas we are already committed to introducing an error for accessing an '' |
- | The calling code will continue to see the value of the property as '' | + | In other words, given the following class: |
- | + | ||
- | (**TODO**: How can this be implemented efficiently? | + | |
- | + | ||
- | In PHP 9.0, untyped properties will start in the uninitialized state unless an initial value is provided, and attempting to access them will give the error shown in the previous section. | + | |
- | + | ||
- | ===== Backward Incompatible Changes ===== | + | |
- | + | ||
- | Un-typed properties will no longer have an implicit initial value of '' | + | |
<code php> | <code php> | ||
- | // Before | + | class A { |
- | class Example | + | |
public $foo; | public $foo; | ||
+ | private $bar; | ||
+ | protected $other = 42; | ||
} | } | ||
+ | </ | ||
- | // After | + | Act as though this was specified: |
- | class Example | + | |
- | public $foo = null; | + | <code php> |
+ | class A { | ||
+ | public | ||
+ | private mixed $bar = null; | ||
+ | protected mixed $other = 42; | ||
} | } | ||
</ | </ | ||
- | ===== Proposed PHP Version(s) ===== | + | Note that this means adding the keyword '' |
- | In PHP 8.next: | + | ===== Backward Incompatible Changes ===== |
- | * Add deprecation notice for untyped properties accessed without initialization | + | It is possible to write code relying directly on the current behaviour of '' |
- | In PHP 9.0: | + | ===== Proposed |
- | * All declared properties will start " | + | Since the new '' |
- | * All declared properties will become " | + | |
- | * The message | + | |
===== RFC Impact ===== | ===== RFC Impact ===== | ||
Line 120: | Line 172: | ||
==== To Opcache ==== | ==== To Opcache ==== | ||
- | The only behaviour which is not already present | + | No new functionality |
+ | |||
+ | ==== To Reflection ==== | ||
- | The other | + | **TODO** Reflection currently shows the implicit '' |
===== Open Issues ===== | ===== Open Issues ===== | ||
- | * [[rfc: | ||
* Are there other differences between typed and untyped properties to address? | * Are there other differences between typed and untyped properties to address? | ||
- | * Can uninitialized untyped properties be implemented efficiently during | + | * Is there a better compromise than keeping |
===== Unaffected PHP Functionality ===== | ===== Unaffected PHP Functionality ===== | ||
* The behaviour of //dynamic// properties, that is those not defined at all in the class definition, is not changed by this RFC. | * The behaviour of //dynamic// properties, that is those not defined at all in the class definition, is not changed by this RFC. | ||
- | * The interaction of '' | + | * The interaction of '' |
+ | * Unlike `var_dump($object)`, | ||
+ | |||
+ | ===== Rejected Features ===== | ||
+ | |||
+ | ==== Changing initial value of properties ==== | ||
+ | |||
+ | When considering the //initial// value of the property, to be fully consistent, we should do one of two things: | ||
+ | |||
+ | - Initialize any property to '' | ||
+ | - Never initialize a property to '' | ||
+ | |||
+ | Option 1 has the advantage of not causing errors in any currently valid code; but it goes against the general trend of making the language stricter and more explicit. | ||
+ | |||
+ | Option 2 is a non-trivial breaking change; although the edits to be made can be trivially automated (changing code of the form < | ||
+ | |||
+ | ==== Allowing '' | ||
+ | |||
+ | As currently defined, [[rfc: | ||
+ | |||
+ | Unfortunately, | ||
===== Future Scope ===== | ===== Future Scope ===== | ||
Line 156: | Line 229: | ||
* [[rfc: | * [[rfc: | ||
- | ===== Rejected Features | + | Previous mailing list threads touching on this issue: |
+ | |||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | ===== Significant revisions | ||
- | None yet. | + | * [[https:// |
+ | * [[https:// |
rfc/mixed_vs_untyped_properties.1700159455.txt.gz · Last modified: 2023/11/16 18:30 by imsop