PHP RFC: Code free constructor
- Version: 0.2
- Date: 2019-01-29
- Author: Andrey Gromov, andrewgrom@rambler.ru, rjhdby@php.net
- Proposed version: PHP 8
- Status: draft
- First Published at: https://wiki.php.net/rfc/code_free_constructor
- ML thread: https://externals.io/message/103793
Changelog
v 0.2
- Added visibility modificators syntax.
- Changed behavior for parameters that need to be forwarded to parent.
- Added “anonymous class” section.
- Added “Q&A, Discussion” section.
- Improved description of realization.
Introduction
“Code free” constructor is constructor with only purpose to directly set object properties from received parameters and, optionally, call parent constructor.
They used for:
- for DTO-classes declaration
- developers, that believe it is good OOP-practice, use them everywhere
- useful in some cases of inheritance
Unfortunately, php syntax enforces to write unnecessary boilerplate.
Proposal
Proposal is to add alternate syntax for “code free” constructors.
There is two generals parts of proposal.
First. Automatic property initialization
Current syntax:
class MotorCycle { protected $vendor; public $driver; private int $cc; public function __construct(string $vendor, Person $driver, int $cc = 600) { $this->vendor = $vendor; $this->driver = $driver; $this->cc = $cc; } }
Proposed syntax:
class MotorCycle(protected string $vendor, Person $driver, private int $cc = 600){ };
If no visibility modifier defined it casts as “public”.
Under the hood, at AST-creation phase, will be silently created right properties declaration and added “_ _ construct” method similar to showed above. So following compilation and runtime will work standard way.
Second. Automatic passing args to parent constructor
For this example I will use class “MotorCycle” from previous section.
Current syntax:
class MyCustomMotorCycle extends MotorCycle { public $passenger; public function __construct(Person $driver, Person $passenger) { parent::__construct("Custom", $driver); $this->passenger = $passenger; } }
Proposed syntax:
class MyCustomMotorCycle(Person $driver, Person $passenger) extends MotorCycle("Custom", $driver) { };
In this case, if some params must be forwarded to parent then they will not be declared and set in child. This behavior is disputable.
Realisation is similar to described above with additional checks and parent call.
Note that you can use all standard syntax constructions like default parameters and splat operator (“...”)
Anonymous classes
Since the syntax for declaring an anonymous class is what it is, the only option that I can imagine is not very beautiful.
$a = new class($mDriver, $mPassenger) extends MotorCycle { private $passenger; public function __construct(Person $driver, Person $passenger) { parent::__construct("Custom", $driver); $this->passenger = $passenger; } }; // |------arguments------||-----------constructor params------------| $a = new class($mDriver, $mPassenger)(Person $driver, private Person $passenger) extends MotorCycle("Custom", $driver){};
Backward Incompatible Changes
Do not know. Looks like no BI.
Proposed PHP Version(s)
PHP 8.x
RFC Impact
Not thinking so
Implementation
Draft implementation, need to be reviewed. Outdated! https://github.com/rjhdby/php-src/compare/master...rjhdby:constructor
Q&A, Discussion
Stanislav Malyshev
This looks like unobvious magic. PHP approach has traditionally been to avoid unobvious magic, and be explicit about what is happening. This functionality does not seem no be enabling anything different, and seems to be pretty obscure as to what is going on. Also, $prop can not be documented this way, and assigning default value to it may be a bit weird too. I am all for paving the walkways, but this particular case seems to be a bit too narrow to have a special language syntax for, and the syntax seems to be not exactly obvious as to what's going on there.
Nikita Popov
Two alternatives you might want to consider:
https://wiki.php.net/rfc/automatic_property_initialization => Proposed function public function _ _ construct(int $this->x, int $this->y) {}, which avoids the need for explicit property assignments in the ctor. However, the property still needs to be declared separately.
https://docs.hhvm.com/hack/other-features/constructor-parameter-promotion => Uses public function _ _ construct(public int $x, public int $y) {} to declare properties in-line in the constructor.
I think that *if* we want to add some kind of sugar of this type, then I'd strongly prefer the syntax used by Hack than the one proposed here. It makes a lot more sense to me intuitively, probably because the property declarations still looks like normal property declarations, they just occur in-line in the ctor.
A matter of habit and documentation. There is a lot of really strange and magical behavior in the language. It seems to me that the proposed concept is quite simple and transparent for understanding. IMHO this syntax make behavior more strict. No need to read constructor's body, because you know exactly what's going on.