rfc:constructor_promotion
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:constructor_promotion [2020/03/26 14:04] – nikic | rfc:constructor_promotion [2020/08/01 23:38] (current) – RFC was implemented carusogabriel | ||
---|---|---|---|
Line 2: | Line 2: | ||
* Date: 2020-03-26 | * Date: 2020-03-26 | ||
* Author: Nikita Popov < | * Author: Nikita Popov < | ||
- | * Status: | + | * Status: |
* Target Version: PHP 8.0 | * Target Version: PHP 8.0 | ||
* Implementation: | * Implementation: | ||
Line 19: | Line 19: | ||
float $x = 0.0, | float $x = 0.0, | ||
float $y = 0.0, | float $y = 0.0, | ||
- | float $z = 0.0 | + | float $z = 0.0, |
) { | ) { | ||
$this->x = $x; | $this->x = $x; | ||
Line 39: | Line 39: | ||
public float $x = 0.0, | public float $x = 0.0, | ||
public float $y = 0.0, | public float $y = 0.0, | ||
- | public float $z = 0.0 | + | public float $z = 0.0, |
) {} | ) {} | ||
} | } | ||
Line 60: | Line 60: | ||
abstract class Test { | abstract class Test { | ||
// Error: Abstract constructor. | // Error: Abstract constructor. | ||
- | abstract public function __construct(private $x) {} | + | abstract public function __construct(private $x); |
} | } | ||
interface Test { | interface Test { | ||
// Error: Abstract constructor. | // Error: Abstract constructor. | ||
- | public function __construct(private $x) {} | + | public function __construct(private $x); |
} | } | ||
</ | </ | ||
While unusual, promoted parameters may occur inside trait constructors. | While unusual, promoted parameters may occur inside trait constructors. | ||
+ | |||
+ | Promoted properties have to be prefixed by one of the visibility keywords, use of '' | ||
+ | |||
+ | <PHP> | ||
+ | class Test { | ||
+ | // Error: " | ||
+ | public function __construct(var $prop) {} | ||
+ | } | ||
+ | </ | ||
Properties declared through promoted parameters are subject to the same restrictions as normal property declarations. In particular, it is not possible to declare the same property twice: | Properties declared through promoted parameters are subject to the same restrictions as normal property declarations. In particular, it is not possible to declare the same property twice: | ||
Line 82: | Line 91: | ||
</ | </ | ||
- | It is also not possible to use the '' | + | It is also not possible to use the '' |
<PHP> | <PHP> | ||
Line 151: | Line 160: | ||
While repeating the default value on the property declaration would currently appear harmless, there are forward-compatibility reasons why it is preferable to only specify the default once. | While repeating the default value on the property declaration would currently appear harmless, there are forward-compatibility reasons why it is preferable to only specify the default once. | ||
- | The first is a possible future extension to allow arbitrary | + | The first is a possible future extension to allow arbitrary |
<PHP> | <PHP> | ||
Line 190: | Line 199: | ||
} | } | ||
</ | </ | ||
+ | |||
+ | The forwarding property assignments occur at the start of the constructor. As such, it is possible to access both the parameter and the property in the constructor, | ||
+ | |||
+ | <PHP> | ||
+ | // This works. | ||
+ | class PositivePoint { | ||
+ | public function __construct(public float $x, public float $y) { | ||
+ | assert($x >= 0.0); | ||
+ | assert($y >= 0.0); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // This also works. | ||
+ | class PositivePoint { | ||
+ | public function __construct(public float $x, public float $y) { | ||
+ | assert($this-> | ||
+ | assert($this-> | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Reflection ==== | ||
+ | |||
+ | Reflection (and other introspection mechanisms) will observe the state after desugaring. This means that promoted properties will appear the same way as explicitly declared properties, and promoted constructor arguments will appear as ordinary constructor arguments. | ||
+ | |||
+ | While PHP does not expose doc comments on parameters, doc comments on promoted properties will be retained: | ||
+ | |||
+ | <PHP> | ||
+ | class Test { | ||
+ | public function __construct( | ||
+ | /** @SomeAnnotation() */ | ||
+ | public $annotatedProperty | ||
+ | ) {} | ||
+ | } | ||
+ | |||
+ | $rp = new ReflectionProperty(Test:: | ||
+ | echo $rp-> | ||
+ | </ | ||
+ | |||
+ | As the example indicates, this allows using doc comment based annotations with promoted properties. | ||
+ | |||
+ | Additionally, | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | Most reflection code should not care whether properties are generated or not, but this information will allow reconstructing the structure or the original code more easily. | ||
+ | |||
+ | ==== Inheritance ==== | ||
+ | |||
+ | Constructor promotion can be used in conjunction with inheritance, | ||
+ | |||
+ | <PHP> | ||
+ | abstract class Node { | ||
+ | public function __construct( | ||
+ | protected Location $startLoc = null, | ||
+ | protected Location $endLoc = null, | ||
+ | ) {} | ||
+ | } | ||
+ | |||
+ | class ParamNode extends Node { | ||
+ | public function __construct( | ||
+ | public string $name, | ||
+ | public ExprNode $default = null, | ||
+ | public TypeNode $type = null, | ||
+ | public bool $byRef = false, | ||
+ | public bool $variadic = false, | ||
+ | Location $startLoc = null, | ||
+ | Location $endLoc = null, | ||
+ | ) { | ||
+ | parent:: | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The '' | ||
+ | |||
+ | <PHP> | ||
+ | abstract class Node { | ||
+ | protected Location $startLoc; | ||
+ | protected Location $endLoc; | ||
+ | |||
+ | public function __construct( | ||
+ | Location $startLoc = null, | ||
+ | Location $endLoc = null, | ||
+ | ) { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class ParamNode extends Node { | ||
+ | public string $name; | ||
+ | public ExprNode $default; | ||
+ | public TypeNode $type; | ||
+ | public bool $byRef; | ||
+ | public bool $variadic; | ||
+ | |||
+ | public function __construct( | ||
+ | string $name, | ||
+ | ExprNode $default = null, | ||
+ | TypeNode $type = null, | ||
+ | bool $byRef = false, | ||
+ | bool $variadic = false, | ||
+ | Location $startLoc = null, | ||
+ | Location $endLoc = null, | ||
+ | ) { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | $this-> | ||
+ | $this-> | ||
+ | $this-> | ||
+ | parent:: | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | It should be noted that the property assignments happen before the parent constructor is invoked. This is unusual in terms of coding style, but should not impact behavior for non-degenerate cases. | ||
+ | |||
+ | ==== Attributes ==== | ||
+ | |||
+ | As PHP 8 also introduces [[rfc: | ||
+ | |||
+ | <PHP> | ||
+ | class Test { | ||
+ | public function __construct( | ||
+ | << | ||
+ | public int $prop, | ||
+ | ) {} | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This code could desugar in one of four ways: | ||
+ | |||
+ | - The attribute is applied only to the parameter. | ||
+ | - The attribute is applied only to the implied property. | ||
+ | - The attribute is applied both to the parameter and the property. | ||
+ | - Attributes on promoted properties are forbidden, due to ambiguity. | ||
+ | |||
+ | Here are the possible transformations: | ||
+ | |||
+ | <PHP> | ||
+ | // Option 1: Attribute applies only to parameter. | ||
+ | class Test { | ||
+ | public int $prop; | ||
+ | | ||
+ | public function __construct( | ||
+ | << | ||
+ | int $prop, | ||
+ | ) {} | ||
+ | } | ||
+ | |||
+ | // Option 2: Attribute applies only to property. | ||
+ | class Test { | ||
+ | << | ||
+ | public int $prop; | ||
+ | | ||
+ | public function __construct( | ||
+ | int $prop, | ||
+ | ) {} | ||
+ | } | ||
+ | |||
+ | // Option 3: Attribute applies to both | ||
+ | class Test { | ||
+ | << | ||
+ | public int $prop; | ||
+ | | ||
+ | public function __construct( | ||
+ | << | ||
+ | int $prop, | ||
+ | ) {} | ||
+ | } | ||
+ | |||
+ | // Option 4: Error, cannot use attributes with constructor parameter promotion. | ||
+ | </ | ||
+ | |||
+ | This RFC proposes to use option 3 (applying the attribute to both property and parameter), as it is the most flexible. The '' | ||
+ | |||
+ | However, I consider this to be something of an implementation detail. If further work on attributes prior to the PHP 8 release shows that it would be advantageous to place the attribute only on the property, we should be open to such a change. | ||
==== Coding Style Considerations ==== | ==== Coding Style Considerations ==== | ||
Line 212: | Line 400: | ||
public float $x = 0.0, | public float $x = 0.0, | ||
public float $y = 0.0, | public float $y = 0.0, | ||
- | public float $z = 0.0 | + | public float $z = 0.0, |
) {} | ) {} | ||
} | } | ||
Line 243: | Line 431: | ||
float $x = 0.0, | float $x = 0.0, | ||
float $y = 0.0, | float $y = 0.0, | ||
- | float $z = 0.0 | + | float $z = 0.0, |
) { | ) { | ||
$this->x = $x; | $this->x = $x; | ||
Line 262: | Line 450: | ||
Larry provided some broader vision on how this feature can be combined with other features to improve our object initialization story in https:// | Larry provided some broader vision on how this feature can be combined with other features to improve our object initialization story in https:// | ||
- | Because constructor signatures that include promoted properties are likely to become long enough to require line-breaks, | + | ===== Prior Art ===== |
- | < | + | This feature, or something very similar, is already |
- | class Point { | + | |
- | public function __construct( | + | |
- | public float $x = 0.0, | + | |
- | public float $y = 0.0, | + | |
- | public float $z = 0.0, // <-- Allow this comma. | + | |
- | ) {} | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Optional trailing commas in this position were previously declined, but I believe we have a stronger case for them nowadays, especially in conjunction with this feature. | + | |
- | + | ||
- | ===== Other Languages ===== | + | |
- | + | ||
- | This feature, or something very similar, is also supported by a number of other languages. | + | |
* [[https:// | * [[https:// | ||
* [[https:// | * [[https:// | ||
* [[https:// | * [[https:// | ||
+ | |||
+ | There have also been three previous RFCs on related topics: | ||
+ | |||
+ | * [[rfc: | ||
+ | * [[rfc: | ||
+ | * [[rfc: | ||
===== Vote ===== | ===== Vote ===== | ||
- | Yes/No. | + | Voting started 2020-05-15 and closes 2020-05-29. |
+ | |||
+ | <doodle title=" | ||
+ | | ||
+ | | ||
+ | </ | ||
rfc/constructor_promotion.1585231478.txt.gz · Last modified: 2020/03/26 14:04 by nikic