Replacing PHP Dynamic Class Properties

Content:

Starting with PHP 8.2, released in November 2022, using dynamic class properties will generate a warning.

Dynamic properties have never been a proper ‘feature’ in PHP, and support has now been deprecated

While providing flexibility when it comes to setting values in an object, dynamic properties open the door to potential bugs in your application. A typo in a property name could result in hours of debugging, as there’s was no mechanism for PHP to flag this.

Such typos will now be highlighted with a warning. It’s possible to silence these warnings, but PHP 9.0 will remove support for dynamic properties entirely. Overriding the deprecation warnings is not a long-term solution.

This article will show you a few options for replacing the use of dynamic class properties in your application.

Changes Explained

Attempting to set an undefined property now triggers a warning, indicating the deprecation of this action. You will see a message similar to the following:

Deprecated: Creation of dynamic property Class::$property is deprecated in {file} on line {line}

Class properties now need to be defined in the class to avoid this warning, and will be a requirement in PHP 9.0. Class properties can be defined without specifying a data type or visibility, making fixing this issue as simple as adding the property name to your class definition.

class MyClass {
    $myProperty
}

This is generally a simple, if time consuming, change to make. However, there are a few cases where additional changes are required. Strategies to deal with these cases are outlined below.

Working With Databases

If your program interacts with a database, you might be familiar with the use of dynamic class properties when fetching results. In the past, you might have utilised code like:

$db_result->fetch_object('\MyNamespace\MyClass');

With this code, the specified class (\MyNamespace\MyObject) is instantiated, and values in the query result are assigned dynamically. This will no longer be possible in PHP 9.0, so an alternative method is required.

To work around this, you can create a new function to set the properties passed to it.

public function setValue(string $key, mixed $value)
{
    $this->$key = $value;
}

This method allows you to dynamically set properties in your class based on the results fetched from the database.

Rather than creating an object of your specified class, the database result can be looped through, calling the setValue() function on each key/value pair in the returned object.

$obj = new \MyNamespace\MyClass;
$result = $db_result->fetch_object();

foreach ($result as $key => $value) {
    $obj->setValue($key, $value);
}

This maintains the functionality of the previous method, while eliminating dynamic class properties.

Implementing __get() and __set()

For those who wish to maintain some level of flexibility in property access, PHP provides the ‘magic methods’ __get() and __set(). These methods allow you to capture attempts to access or modify undefined or private properties, providing an opportunity to handle such cases dynamically.

public function __get($name) {
    return $this->properties[$name] ?? null;
}

public function __set($name, $value) {
    $this->properties[$name] = $value;
}

Where a specified property is inaccessible (either due to visibility, or not existing), the __get() or __set() function will be called automatically depending on the action being performed. Defining your own code inside these magic methods allow custom actions to be performed.

In this example, a class property name ‘properties’ needs to be defined inside the class as an empty array. The __get() function uses this array to dynamically set new values, without creating a new class property.

Silencing the Warnings

We mentioned earlier on that it’s possible to silence these warnings.

While you should avoid this if possible, it can be useful to do this temporarily until permanent changes are made. This is particularly true with a large code base, or if dynamic properties were heavily used.

To do this, add #[AllowDynamicProperties] above your class definition. In this example, no warning will be emitted.

#[AllowDynamicProperties]
class Car {}

$car = new Car();
$car->colour = 'red';

This is not recommended, but useful to temporarily silence these errors.