PHP RFC: Automatic Property Initialization
- Version: 0.2
- Date: 2013-09-27
- Author: Gordon Oheim, gooh@php.net (patch provided by NikiC, nikic@php.net)
- Status: Declined
- First Published at: https://wiki.php.net/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
- patch provided by NikiC: https://github.com/php/php-src/pull/474
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