PHP 8.2: Dynamic Properties are deprecated
In PHP classes, it is possible to dynamically set and retrieve class properties that are not declared. These properties do not adhere to a specific (similar to typed properties), and it requires the use of __get()
and __set()
magic methods to effectively prevent or control how dynamic properties are set and retrieved.
class User {
private int $uid;
}
$user = new User();
$user->name = 'Foo';
In the snippet above, the User
class does not declare a property with name name
, but because dynamic properties are allowed, PHP allows setting it.
While dynamic properties provide the flexibility in creating classes such as value objects without a strict class declaration, it opens the possibility of potential bugs and unexpected behaviors in applications . For example, a typo in a statement that sets a property could go unnoticed because PHP silently allows all dynamic properties.
In PHP 8.2 and later, setting a value to an undeclared class property is deprecated, and emits a deprecation notice the first time the property is set during the lifetime of the application execution.
class User {
private int $uid;
}
$user = new User();
$user->name = 'Foo';
Deprecated: Creation of dynamic property User::$name is deprecated in ... on line ...
Setting the properties from within the class also emits the deprecation notice:
class User {
public function __construct() {
$this->name = 'test';
}
}
new User();
Deprecated: Creation of dynamic property User::$name is deprecated in ... on line ...
There are legitimate use cases of dynamic properties, such as value objects derived from a dynamic JSON response, or configuration objects that allow arbitrary values.
Ideally, classes should declare the dynamic properties in the class to avoid the deprecation notice. It is not required to declare the property with a property type.
Exempted Dynamic Property Patterns
There are three exceptions to this deprecation. Utilizing one of the approaches below avoids the deprecation notice.
- Classes with
#[AllowDynamicProperties]
attribute. stdClass
and its sub-classes- Classes with
__get
and__set
magic methods
Classes with #[AllowDynamicProperties]
attribute
PHP 8.2 introduces a new attribute in the global namespace named #[AllowDynamicProperties]
. Classes declared with this attribute signals PHP to not emit any deprecation notices when setting dynamic properties on objects of that class.
Child classes automatically inherit the
#[AllowDynamicProperties]
attribute from parent class.
The snippet below declares a User
class with #[AllowDynamicProperties]
attribute, and does not emit any deprecation notices even though it sets properties dynamically.
+ #[AllowDynamicProperties]
class User {
private int $uid;
}
$user = new User();
$user->name = 'Foo';
stdClass
and its sub-classes
PHP uses stdClass
as the base class when it coerces data to objects, when decoding JSON objects, etc. In PHP 8.2, the #[AllowDynamicProperties]
attribute is internally added to the stdClass
class. This means setting dynamic properties on stdClass
objects or any classes that extend stdClass
does not emit dynamic property deprecation notices.
$object = new stdClass();
$object->foo = 'bar';
class User extends stdClass {}
$object = new User();
$object->foo = 'bar';
Neither of the snippets above emit dynamic property deprecation notices because the stdClass
internally has the #[AllowDynamicProperties]
attribute.
Classes with __get
and __set
magic methods
Classes that declare a __set
magic method are excluded from the dynamic property declaration. It might be necessary to couple it with a __get
method to create a practically useful class.
class User {
public function __set(string $name, mixed $value): void {
}
}
$user = new User();
$user->name = 'test';
The snippet above does not emit a deprecation notice because the User
class implements the __set
magic method.
Note that setting a dynamic property from within the __set
method is still deprecated:
class User {
public function __set(string $name, mixed $value): void {
$this->{$name} = $value;
}
}
$user = new User();
$user->name = 'test';
Deprecated: Creation of dynamic property User::$name is deprecated in ... on line ...
Associating object data with WeakMaps
PHP 8.0 introduced WeakMaps to PHP. Applications that set dynamic properties to associate auxiliary data may consider using WeakMaps. This might improve the clarity and maintainability of the code, while avoiding the dynamic property deprecation notice.
For example, the following snippet stores a dynamic property called processed
. This property is not declared on the Event
class, and does not belong to the Event
class itself.
$event = new Event();
$event->processed = true; // <-- dynamic property
$is_processed = !empty($event->processed);
With WeakMaps, it is possible to associate data in a WeakMap. When the Event
object is dropped from the scope, the associated data will be removed automatically.
$event = new Event();
+ $processed_events = new WeakMap();
- $event->processed = true; // <-- dynamic property
+ $processed_events[$event] = true;
- $is_processed = !empty($event->processed);
+ $is_processed = !empty($processed_events[$event]);
Backwards Compatibility Impact
The deprecation notice is emitted on PHP 8.2 and later. In PHP 9.0, dynamic properties will result in a fatal error.
It is possible to programmatically emit a deprecation in older versions in the interest of consistency across multiple PHP versions.
Related Changes
- PHP 8.0: Class magic method signatures are strictly enforced
- PHP 8.0: New mixed pseudo type
- PHP 8.0: WeakMaps