rfc:constructor_promotion

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:constructor_promotion [2020/04/02 10:02] nikicrfc: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 <nikic@php.net>   * Author: Nikita Popov <nikic@php.net>
-  * Status: Under Discussion+  * Status: Implemented
   * Target Version: PHP 8.0   * Target Version: PHP 8.0
   * Implementation: https://github.com/php/php-src/pull/5291   * Implementation: https://github.com/php/php-src/pull/5291
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);
 } }
 </PHP> </PHP>
  
 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 ''var'' is not supported:
 +
 +<PHP>
 +class Test {
 +    // Error: "var" keyword is not supported.
 +    public function __construct(var $prop) {}
 +}
 +</PHP>
  
 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:
 </PHP> </PHP>
  
-It is also not possible to use the ''callable'' type, even though it would ordinarily be permitted for parameters:+It is also not possible to use the ''callable'' type, because it is [[https://wiki.php.net/rfc/typed_properties_v2#callable_type|not supported]] as a property type:
  
 <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 expression in parameter and property defaults:+The first is a possible future extension to allow arbitrary expressions in parameter and property defaults:
  
 <PHP> <PHP>
Line 213: Line 222:
 ==== Reflection ==== ==== Reflection ====
  
-Property declarations promoted from a constructor are available through reflection (and any other introspection meansas usualThey are indistinguishable from explicitly declared properties.+Reflection (and other introspection mechanismswill observe the state after desugaringThis 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: While PHP does not expose doc comments on parameters, doc comments on promoted properties will be retained:
Line 230: Line 239:
  
 As the example indicates, this allows using doc comment based annotations with promoted properties. As the example indicates, this allows using doc comment based annotations with promoted properties.
 +
 +Additionally, two new methods are added:
 +
 +  * ''ReflectionProperty::isPromoted()'' returns true for properties that have been implicitly generated as part of constructor promotion.
 +  * ''ReflectionParameter::isPromoted()'' returns true for parameters that have resulted in the generation of an implicit property as part of constructor promotion.
 +
 +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, but has no special interaction with it beyond what is implied by the desugaring. A typical use-case involving inheritance is shown in the following, based on an abstract syntax tree representation:
 +
 +<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::__construct($startLoc, $endLoc);
 +    }
 +}
 +</PHP>
 +
 +The ''ParamNode'' class declares a number of promoted properties (those prefixed with ''public'') and additionally takes two normal parameters (those not prefixed with ''public''), which are simply forwarded to the parent constructor. The code is equivalent to the following desugaring:
 +
 +<PHP>
 +abstract class Node {
 +    protected Location $startLoc;
 +    protected Location $endLoc;
 +
 +    public function __construct(
 +        Location $startLoc = null,
 +        Location $endLoc = null,
 +    ) {
 +        $this->startLoc = $startLoc;
 +        $this->endLoc = $endLoc;
 +    }
 +}
 +
 +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->name = $name;
 +        $this->default = $default;
 +        $this->type = $type;
 +        $this->byRef = $byRef;
 +        $this->variadic = $variadic;
 +        parent::__construct($startLoc, $endLoc);
 +    }
 +}
 +</PHP>
 +
 +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:attributes_v2|attributes]], we need to consider how these features interact. Attributes are allowed both on properties and on parameters.
 +
 +<PHP>
 +class Test {
 +    public function __construct(
 +        <<ExampleAttribute>>
 +        public int $prop,
 +    ) {}
 +}
 +</PHP>
 +
 +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(
 +        <<ExampleAttribute>>
 +        int $prop,
 +    ) {}
 +}
 +
 +// Option 2: Attribute applies only to property.
 +class Test {
 +    <<ExampleAttribute>>
 +    public int $prop;
 +    
 +    public function __construct(
 +        int $prop,
 +    ) {}
 +}
 +
 +// Option 3: Attribute applies to both
 +class Test {
 +    <<ExampleAttribute>>
 +    public int $prop;
 +    
 +    public function __construct(
 +        <<ExampleAttribute>>
 +        int $prop,
 +    ) {}
 +}
 +
 +// Option 4: Error, cannot use attributes with constructor parameter promotion.
 +</PHP>
 +
 +This RFC proposes to use option 3 (applying the attribute to both property and parameter), as it is the most flexible. The ''isPromoted()'' Reflection APIs can be used by attribute validation code to discard the property or parameter attribute, if necessary.
 +
 +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 252: 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 283: 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 301: Line 449:
  
 Larry provided some broader vision on how this feature can be combined with other features to improve our object initialization story in https://hive.blog/php/@crell/improving-php-s-object-ergonomics. Larry provided some broader vision on how this feature can be combined with other features to improve our object initialization story in https://hive.blog/php/@crell/improving-php-s-object-ergonomics.
- 
-Because constructor signatures that include promoted properties are likely to become long enough to require line-breaks, it would be beneficial to allow a trailing comma in function parameter lists: 
- 
-<PHP> 
-class Point { 
-    public function __construct( 
-        public float $x = 0.0, 
-        public float $y = 0.0, 
-        public float $z = 0.0, // <-- Allow this comma. 
-    ) {} 
-} 
-</PHP> 
- 
-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. 
  
 ===== Prior Art ===== ===== Prior Art =====
Line 332: Line 466:
 ===== Vote ===== ===== Vote =====
  
-Yes/No.+Voting started 2020-05-15 and closes 2020-05-29. 
 + 
 +<doodle title="Add support for declaring properties in the constructor signature?" auth="nikic" voteType="single" closed="true"> 
 +   Yes 
 +   No 
 +</doodle>
  
rfc/constructor_promotion.1585821730.txt.gz · Last modified: 2020/04/02 10:02 by nikic