Constructor Property Promotion in PHP 8.0

Properties are class member variables that stores values like strings, integers, booleans, etc. We can set properties via class constructor, setter method or directly if property is public.

When we set properties via constructor, the class can be written as follows:

<?php

class User
{
    private string $username;

    private int $roleId;

    private bool $active;

    public function __construct(string $username, int $roleId, bool $active)
    {
        $this->username = $username;
        $this->roleId = $roleId;
        $this->active = $active;
    }
}

As we can see, properties names was repeated four times: in class property declaration, in constructor parameters, and two times when assigning property value in the body of the constructor.

Since PHP 8.0, we can use constructor property promotion. This is a new syntax that allows to combine properties declaration, constructor parameters and properties assignments in one place.

We can rewrite the previous code as follows:

<?php

class User
{
    public function __construct(
        private string $username,
        private int $roleId,
        private bool $active
    ) {

    }
}

As we can see, properties was declared and assigned via class constructor. We need to write properties names just once instead of four times.

There are some notes for constructor property promotion.

The visibility of a property

Property is promoted when it declared in the constructor with visibility. We can use public, protected or private visibility keywords. The visibility of a properties don’t need to be the same, it can be mixed.

<?php

class User
{
    public function __construct(
        public string $username,
        protected int $roleId,
        private bool $active,
        string $email
    ) {}
}

Note: $email is just constructor parameter and not promoted property because visibility is not specified.

Standard constructor parameters

Promoted properties can be declared together with standard constructor parameters. The order doesn’t matter.

<?php

class User
{
    private string $email;
    
    public function __construct(
        private string $username, // Promoted property
        string $email             // Standard constructor parameter
    ) {
        $this->email = $email;
    }
}

No duplicates

Property cannot be declared in the constructor as promoted property if property already declared in the class.

<?php

class User
{
    private string $username;

    public function __construct(private string $username) {}
}
Fatal error: Cannot redeclare User::$username in User.php on line 7

Reassign promoted property

Promoted properties can be reassigned in the body of the constructor or do other logic.

User.php

<?php

class User
{
    public function __construct(private string $username)
    {
        echo $this->username; // john

        $this->username = 'james';
        echo $this->username; // james
    }
}

main.php

<?php

require_once 'User.php';

$user = new User('john');

Default value

Promoted property can be declared with default value.

User.php

<?php

class User
{
    public function __construct(
        public string $username,
        public int $roleId = 1
    ) {}
}

main.php

<?php

require_once 'User.php';

$user = new User('john');
echo $user->username; // john
echo $user->roleId;   // 1

The var keyword

To declare property using legacy syntax with var keyword is not allowed for property promotion.

<?php

class User
{
    public function __construct(var string $username) {}
}
Parse error: syntax error, unexpected token "var", expecting variable in User.php on line 5

The type of a property

To be property promoted, the type of a property is not required.

<?php

class User
{
    public function __construct(
        private string $username, // Promoted property
        private $email            // Promoted property too
    ) {}
}

The callable type

The callable type is not allowed as a property type. So it is not allowed for property promotion too.

<?php

class User
{
    public function __construct(private callable $callback) {}
}
Fatal error: Property User::$callback cannot have type callable in User.php on line 5

Variadic parameters

Variadic parameters cannot be used for property promotion.

<?php

class User
{
    public function __construct(private string ...$params) {}
}
Fatal error: Cannot declare variadic promoted property in User.php on line 5

Nullable types

Nullable types must be explicitly declared by specifying ? before type definition.

<?php

class User
{
    public function __construct(
        private ?string $username,
        private ?int $roleId = null
    ) {}
}

Implicit nullable types (without ?, but has null as default value) are not supported for property promotion.

<?php

class User
{
    public function __construct(
        private string $username = null,
        private int $roleId = null
    ) {}
}
Fatal error: Cannot use null as default value for parameter $username of type string in User.php on line 5

Only constructor

Promoted properties can be declared only in the constructor. In other methods cannot.

<?php

class User
{
    public function setUsername(private string $username) {}
}
Fatal error: Cannot declare promoted property outside a constructor in User.php on line 5

Abstract constructor

Promoted properties cannot be declared in an abstract constructor.

<?php

abstract class AbstractUser
{
    abstract public function __construct(private string $username);
}
Fatal error: Cannot declare promoted property in an abstract constructor in AbstractUser.php on line 5

Interface constructor is considered as abstract. So promoted properties cannot be declared in the interface constructor.

<?php

interface UserInterface
{
    public function __construct(private string $username);
}
Fatal error: Cannot declare promoted property in an abstract constructor in UserInterface.php on line 5

Trait constructor

It is allowed to declare promoted properties in the trait constructor.

<?php

trait UserTrait
{
    public function __construct(private string $username) {}
}

Doc comments

Doc comments can be added on promoted properties and can be retrieved using the Reflection API.

User.php

<?php

class User
{
    public function __construct(
        /** @var string */
        private $username
    ) {}
}

main.php

<?php

require_once 'User.php';

$property = new ReflectionProperty(User::class, 'username');
echo $property->getDocComment(); // /** @var string */

Attributes

Attributes are allowed on promoted properties.

User.php

<?php

class User
{
    public function __construct(
        #[Column('string')]
        private string $username
    ) {}
}

main.php

<?php

require_once 'User.php';

$property = new ReflectionProperty(User::class, 'username');

foreach ($property->getAttributes() as $attribute) {
    echo $attribute->getName(); // Column

    foreach ($attribute->getArguments() as $arg) {
        echo $arg; // string
    }
}

Leave a Comment

Your email address will not be published. Required fields are marked *