rfc:automatic_property_initialization

This is an old revision of the document!


PHP RFC: Automatic Property Initialization

Introduction

When writing constructors, most constructor bodies usually just assign passed arguments to some properties. While technically not a problem, it is somewhat tedious and could be simplified with some syntactic sugar.

Proposal

This proposal is an alternative to https://wiki.php.net/rfc/constructor-promotion. It uses a different syntactical approach to shorthand constructors borrowed from the Dart Language, in addition to suggesting variations of that approach.

Instead of writing

  class Point 
  {
    private $x, $y;
    
    public function __construct($x, $y)
    {
      $this->x = $x;
      $this->y = $y;
    }
  }

you can just write

  class Point
  {
      private $x, $y;
      
      public function __construct($this->x, $this->y);
  }    

The two snippets are functionally equivalent and achieve the same.

Quoting from the Dart Manual:

If an argument has this. before it in a constructor argument list, the field with that name will automatically be initialized with that argument’s value. This example shows another little feature too: if a constructor body is completely empty, you can just use a semicolon (;) instead of {}.

The proposal suggests to adopt this functionality to PHP, which is really two features:

  • allow for $this->foo as constructor arguments
  • allow for methodless constructors

More examples

  // would assign $this->x and $this->y
  public function __construct($this->x, $this->y)
  {
      // can be left empty
  }
  // would assign properties and provide $z as a local variable
  public function __construct($this->x, $this->y, $z)
  {
      // do something with $z locally
  }
  // valid, but $z is obviously superfluous here
  public function __construct($this->x, $this->y, $z);
  //valid, but $z is obviously superfluous here
  public function __construct($this->x, $this->y, $z)
  {}
  // would limit arguments to their respective typehints
  public function __construct(Foo $this->x, Bar $this->y);
  // would assign default values if none are passed to the constructor
  public function __construct($this->x = 10, Bar $this->y = null);
  // would create a new public property foo
  public function __construct($this->foo = 42);

Interface Methods

Since Interfaces should not contain implementation details, they should not contain constructors. PHP currently does not prevent you from putting constructors into an interface though. This will remain unchanged. However, putting

  public function __construct($this->foo = 42);

would leak even more implementation details into the interface. Namely, it suggests the concrete implementation to have a property named foo. This is clearly against what Interfaces are supposed to be, so this proposal suggests to raise an error when a developer attempts to do so.

However, if (for some odd reason) you are having a constructor in the interface, like

  public function __construct($foo = 42);

the concrete implementation would still allow for

  public function __construct($this->foo = 42);

because PHP does not care for variable name changes from interfaces in concrete implementations. It just wouldn't allow you write the shorthand into the interface in the first place.

Abstract Methods

Using the shorthand syntax in an abstract method will raise an error

  abstract public function __construct($this->foo);

Writing this is equivalent to having an abstract method with a body, which makes no sense.

Inheritance

The proposal will not redefine how PHP currently handles Inheritance. The same rules for constructors and inheritance apply, regardless of which syntax you use.

Reflection

Since this is really just syntactic sugar, reflecting on

   public function __construct($this->foo, $this->bar);

would be treated like the non-abbreviated version, e.g. it would give $foo and $bar as argument names.

Alternate Syntax

Since the equivalent of this. in Dart is $this-> in PHP (3 characters more), having to write $this-> can quickly exceed common coding standards line length limitations, especially when using Typehints as well. Thus, an alternative would be omit $this-> altogether and make the auto assignment dependent on the existence of a method body. This means

  public function __construct($x, $y);

will automatically assign $x and $y to $this->x and $this->y while

  public function __construct($x, $y) 
  {}

will 'not' automatically assign $x and $y to the corresponding properties. This also means that if you have to do additional work in the constructor, you will have to assign arguments just like you always did. Likewise, this variant does not allow mixing of property arguments and regular arguments. Whether such a methodless constructor should then create public properties for non-existing properties would be subject to debate.

Methodless Constructors

Some people brought up concerns that a methodless constructor looks too much like interfaces or abstract methods. I don't think this is much of an issue though because the context is sufficiently clear. Abstract methods require the abstract keyword at the method and the class. And constructors do not appear in Interfaces since they cannot be instantiated. Likewise, putting a constructor into an interface would denote a concrete implementation which is not what interfaces are meant for.

If this turns out to be an issue, we could make the context more explicit by using a keyword, for instance

  public default function __construct($this->foo, $this->bar);

The other option would be not allowing methodless constructors and only implementing/allowing $this->foo as arguments.

General Automatic Assignment

When discussing this feature prior to opening this RFC several people I spoke to suggested not to limit the proposed functionality to constructors but extend it to the entire class scope allowing for methods like

  public function setFoo($this->foo);

If the general consensus is that this is desired, we can still extend the RFC to a more general scope later. On a side note: Dart does not allow for this.

Backward Incompatible Changes

None.

Proposed PHP Version(s)

This feature is proposed for inclusion in PHP 5.6

SAPIs Impacted

All

Impact to Existing Extensions

None. The change is fully backwards compatible.

New Constants

None.

php.ini Defaults

None.

Patches and Tests

The patch currently just allow for $this->foo as constructor arguments, since this is the desired core functionality. Any of the other suggested features, like methodless constructors or alternative syntax or using a keyword are subject to discussion. They are not part of this patch.

References

Reception

There was little feedback on internals regarding this RFC but the few responses that have been made were generally in favor of this feature. The notable exception being the HHVM team that would prefer their own implementation of constructor promotion. No particular extra features have been rejected or strongly favored.

Vote

The vote is for the current patch. You are solely voting on allowing $this->foo as constructor arguments. None of the other suggested features in this document are subject to vote.

The vote is to allow the following syntax, nothing more:

  public function __construct($this->foo)
  {
  }

Notes: The curly braces must be present. This syntax is only available for constructors. Only $this-> is allowed, not $blah->.

Vote start: 2013/01/31 01:00 UTC Vote end: 2014/02/10 01:00 UTC

Automatic Property Initialization
Real name Yes No
ajf (ajf)  
andrey (andrey)  
brianlmoon (brianlmoon)  
bwoebi (bwoebi)  
derick (derick)  
dm (dm)  
Fabien Potencier (fabpot)  
kalle (kalle)  
krakjoe (krakjoe)  
lstrojny (lstrojny)  
malukenho (malukenho)  
mfonda (mfonda)  
salathe (salathe)  
stas (stas)  
toby (toby)  
weierophinney (weierophinney)  
willfitch (willfitch)  
zeev (zeev)  
Final result: 7 11
This poll has been closed.
rfc/automatic_property_initialization.1394116080.txt.gz · Last modified: 2017/09/22 13:28 (external edit)