Class properties are a great way to store statefull data in object. Sometimes we accept and set them via __construct(), sometime we set them via a setter, some other time we initiate them internally in the class.
When we accept class properties in __construct(), the resulting class code becomes like this:
<?php
class Car
{
protected string $brand;
protected string $model;
protected int $price;
public function __construct(string $brand, string $model, int $price){
$this->brand = $brand;
$this->model = $model;
$this->price = $price;
}
}
As we see, We had to repeat all the property names four times just to declare and initiate them as class properties. If the class is simple DTO or value object and there are more properties, class code can really be big enough to seem like clunky and duplicated. Declaring a new property in that class may be annoying task to many lazy people like me.
Welcome to Class constructor property promotion
Class constructor property promotion lets you just define properties in one place and PHP will take care of all the define and initiate tasks itself.
Above boilerplate code becomes like below code:
class Car
{
public function __construct(
protected string $brand,
protected string $model,
protected int $price
)
{
}
}
$car = new Car('Ford', 'Mustang', 100000);
var_dump($car);
// object(Car)#1 (3) { ["brand":protected]=> string(4) "Ford" ["model":protected]=> string(7) "Mustang" ["price":protected]=> int(100000) }
We have declared our properties in __construct() and PHP sets them as class properties. This way, we don't need to repeat ourself, just declare properties in one place and we are done. Sometimes, Syntax sugars are so sweet ;)
Usage Details
class Car {
// we are using all public, protected, private visibility here
public function __construct(public string $brand, protected string $model, private int $price)
{
}
}
class Player {
// We do not need to decalre any type for the properties
public function __construct(public $name, public $score){
}
}
$ronaldo = new Player('Ronaldo', 50);
echo $ronaldo->score;
// 50
class Car
{
public int $price;
public function __construct(public string $brand, public string $model)
{
// let's hardcode model property. You know... for fun :p
$this->model = 'Hardcoded';
// We can even set other properties
$this->price = 500000;
}
}
$car = new Car('Ford', 'Mustang', 100000);
echo $car->price;
// 500000
properties are promoted before the execution of __construct body.
class User {
public function __construct(public $name)
{
// $this->name is available here
}
}
function promote(public $name){
// does not work here
// because this is not a class construct
}
abstract class AbstractUser {
// not allowed in abstract
abstract public function __construct(public $name);
}
// Fatal error: Cannot declare promoted property in an abstract constructor
interface UserInterface {
// __construct in Interface is considered as abstract
public function __construct(public $name);
}
// Fatal error: Cannot declare promoted property in an abstract constructor
We can however use promoted properties in traits. We can even overload the whole __construct in child class.
class User {
public $name;
public function __construct(public $name){}
}
// Fatal error: Cannot redeclare User::$name on line 4
class User {
public function __construct(var $name){}
}
// Parse error: syntax error, unexpected 'var' (T_VAR), expecting variable
class User {
// This does not work
public function __construct(public string $name = null){}
}
class User {
// works fine
public function __construct(public ?string $name){}
}
class EventHandler {
public function __construct(public callable $event){}
}
// Fatal error: Property EventHandler::$event cannot have type callable
class EventHandler {
public function __construct(public ...$events){}
}
// Fatal error: Cannot declare variadic promoted property
Reflection:
When using Reflection API, Both ReflectionProperty and ReflectionParameter classes will have a new method isPromoted() which returns true if the property was promoted via a constructor.
Write comment about this article: