Before PHP 8, a class with three properties repeated each name three times: once in the property list, once as a parameter and once in the assignment.
<?php
class UserOld
{
private string $name;
private string $email;
private bool $active;
public function __construct(string $name, string $email, bool $active = true)
{
$this->name = $name;
$this->email = $email;
$this->active = $active;
}
public function name(): string
{
return $this->name;
}
}
echo (new UserOld("Isha", "i@example.com"))->name();
Isha
Constructor property promotion (PHP 8.0) lets you write the visibility in front of the parameter. PHP then declares the property and assigns it for you.
<?php
class User
{
public function __construct(
private string $name,
private string $email,
private bool $active = true,
) {}
public function name(): string
{
return $this->name;
}
}
echo (new User("Isha", "i@example.com"))->name();
Isha
Same class, much shorter. The empty braces {} mean the constructor has nothing else to do.
Rules
- A promoted parameter needs a visibility keyword (
public,protected,private),readonly, or both. - You can mix promoted and normal parameters.
- Default values work as usual.
- A trailing comma after the last parameter is allowed (and recommended).
With readonly
<?php
class Point
{
public function __construct(
public readonly float $x = 0,
public readonly float $y = 0,
) {}
}
$p = new Point(1.5, 2.5);
echo $p->x . ", " . $p->y;
1.5, 2.5
Extra logic in the constructor
You can still write code in the body. The promoted properties are already set when it runs.
<?php
class Email
{
public function __construct(public readonly string $address)
{
if (!filter_var($this->address, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email");
}
}
}
try {
new Email("nope");
} catch (InvalidArgumentException $e) {
echo $e->getMessage();
}
Invalid email
Promoted and normal properties together
<?php
class Cart
{
private array $items = []; // a normal property
public function __construct(private string $owner) {} // promoted
public function add(string $item): void
{
$this->items[] = $item;
}
public function summary(): string
{
return $this->owner . " has " . count($this->items) . " item(s)";
}
}
$cart = new Cart("Ana");
$cart->add("Pen");
echo $cart->summary();
Ana has 1 item(s)
Limits
- Not allowed in abstract constructors or in interfaces.
- A promoted property cannot be of type
callable. - You cannot declare the same property again in the class body.
With named arguments
Promotion and named arguments work very well together.
<?php
class Connection
{
public function __construct(
public string $host = "127.0.0.1",
public int $port = 3306,
public bool $ssl = false,
) {}
}
$c = new Connection(ssl: true);
echo "{$c->host}:{$c->port} ssl=" . ($c->ssl ? "yes" : "no");
127.0.0.1:3306 ssl=yes
The default shape of a modern class
public function __construct(private readonly Dependency $dependency) {}. Short, typed and safe.Why this matters in Laravel
public function __construct(private UserRepository $users) {}. That one line declares the property and tells Laravel what to supply.