Skip to content

PHP Tutorial Modern PHP Features

Constructor Property Promotion

Lesson 224 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

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.

index.php
PHP
<?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();
Output
OUTPUT
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.

index.php
PHP
<?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();
Output
OUTPUT
Isha

Same class, much shorter. The empty braces {} mean the constructor has nothing else to do.

Rules

  1. A promoted parameter needs a visibility keyword (public, protected, private), readonly, or both.
  2. You can mix promoted and normal parameters.
  3. Default values work as usual.
  4. A trailing comma after the last parameter is allowed (and recommended).

With readonly

index.php
PHP
<?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;
Output
OUTPUT
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.

index.php
PHP
<?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();
}
Output
OUTPUT
Invalid email
index.php
PHP
<?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();
Output
OUTPUT
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.

index.php
PHP
<?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");
Output
OUTPUT
127.0.0.1:3306 ssl=yes
lightbulb
The default shape of a modern class
Most new classes look like this: public function __construct(private readonly Dependency $dependency) {}. Short, typed and safe.
info
Why this matters in Laravel
Controllers and services in Laravel use promotion for dependency injection: public function __construct(private UserRepository $users) {}. That one line declares the property and tells Laravel what to supply.