PHP 8.0: Class constructor property promotion
Constructor Property Promotion is a new syntax in PHP 8 that allows class property declaration and constructor assignment right from the constructor.
A typical class that declares a property, and then assigns a value to it in the class constructor is quite verbose.
class User {
private string $name;
public function __construct(string $name) {
$this->name = $name;
}
}
With the Constructor Property Promotion syntax, the class declaration above can be minimized to avoid boilerplate:
class User {
- private string $name;
- public function __construct(string $name) {
+ public function __construct(private string $name) {
- $this->name = $name
}
}
This results in a much simplified and minimal syntax that is functionally identical to the verbose class declaration:
class User {
public function __construct(private string $name) {}
}
What it does
Constructor Property Promotion is a shorthand syntax to declare and assign class properties from the constructor. This avoids having to type the class property name from four times to just once, and property type from twice to just once.
Your constructor can still run additional code within the constructor. Properties will be assigned before the rest of the constructor code is executed.
class User {
public function __construct(private string $name) {
echo $this->name;
}
}
new User('Ayesh');
// echoes "Ayesh"
If you change the constructor argument, you need to re-assign it to the class property:
new User('Ayesh');
class User {
public function __construct(public string $name) {
echo $this->name; // "Ayesh"
$name = 'foo' . $name;
echo $this->name; // "Ayesh"
$this->name = $name;
echo $this->name; // "fooAyesh"
}
}
AST and Reflection
Constructor Property Promotion is not internally handled as an Abstract Syntax Tree transformation. This means if you inspect the AST (with an extension such as php-ast), you will still see the code as written with property visibility modifiers right in the constructor.
Reflection API, it will seamlessly return information about both standard and constructor-promoted properties all the same.
In Reflection API, ReflectionProperty
and ReflectionParameter
classes will have a new method isPromoted
to determine if the parameter/property was declared in a constructor.
Syntax
Mixing constructor properties with standard properties
A class is allowed to have constructor-promoted properties and standard properties:
class User {
private int $uid;
public function __construct(public string $name, int $uid) {
$this->uid = $uid;
}
}
However, note that the same property must not be declared as a standard property and a constructor-promoted property.
Property Type is not required
It is possible to use Typed Properties and un-typed properties, although it's often a good idea to always strict type class properties.
class User {
public function __construct(private string $name, private $uid) {}
}
In the example above $uid
will be a class property, but without a type declared to it.
Nullable types are supported, but not implicit syntax
Nullable properties can be promoted at constructor, however, the PHP 7.0-style nullable types are not supported.
class User {
public function __construct(
public ?string $name,
public ?string $fullname = null,
) {}
}
While the syntax above is allowed, implicit declaration of types (without the ?
prefix, but = null
default value) is not allowed:
class User {
public function __construct(
public string $name = null,
public string $fullname = null,
) {}
}
Fatal error: Cannot use null as default value for parameter $name of type string in ... on line ...
Both property promotions above are not allowed because they are implicitly declaring nullable types, but without the ?string
syntax. Although it works, this practice is frowned upon.
No duplicate properties
It is not allowed to duplicate a property with explicit declaration and constructor property promotion.
The following code is not allowed:
class User {
public string $name;
public function __construct(public string $name) {}
}
Fatal error: Cannot redeclare User::$name in ... on line ...
Not supported in interfaces and abstract classes, allowed in traits
Constructor Properties cannot be used in interfaces because they simply don't allow properties in the first place.
Not allowed:
interface User {
public function __construct(public string $name);
}
Abstract classes can have constructor properties promoted, but not if the constructor itself is marked abstract
.
abstract class User {
abstract public function __construct(public string $name) {}
}
Fatal error: Cannot declare promoted property outside a constructor in ... on line ...
Traits can contain both properties and methods, including constructors. This means it is possible for a trait for make use of Constructor Property Promotion too.
trait UserTrait {
public function __construct(public string $name) {}
}
var
is not allowed
The legacy syntax of declaring a property with var
keyword is not allowed. All constructor-promoted properties must be declared with public
, protected
, or private
visibility modifiers.
The following code is not valid:
class User {
public function __construct(var $username) {}
}
Parse error: syntax error, unexpected 'var' (T_VAR), expecting variable (T_VARIABLE) in ... on line ...
callable
type is not allowed
callable
is a valid type in PHP, but it is not allowed as a property type. Thus, it is not allowed to declare a constructor property with type callable
either:
class User {
public function __construct(public callable $logoutFn) {}
}
Fatal error: Property User::$logoutFn cannot have type callable in ... on line ...
Only constructor supports property promotion
It probably goes without saying, but only the __construct
method can have properties promoted. Other methods cannot:
class User {
public function setUser(public string $name) {}
}
Fatal error: Cannot declare promoted property outside a constructor in ... on line ...
Backwards compatibility impact
Constructor Property Promotion is a syntax change in PHP 8, and so it's not possible to backport this feature to older PHP versions. If you use Constructor Property Promotion, that code will not be compatible with PHP 7 and older versions.
Attempting to run constructor-promoted classes will throw a parse error:
Parse error: syntax error, unexpected 'public' (T_PUBLIC), expecting variable (T_VARIABLE) in ... on line ...
Derick Rethans interviewed Nikita Popov (author of the RFC) in PHP Internals News podcast, which you can list here.