Skip to main content

How to Fix "Creation of dynamic property is deprecated" in PHP 8.2+

 If you are upgrading a legacy codebase to PHP 8.2, your logs are likely flooding with this message:

Deprecated: Creation of dynamic property ClassName::$propertyName is deprecated

This is not a bug in the PHP interpreter. It is a deliberate language change to enforce stricter object definitions. In PHP 9.0, this deprecation notice will become a fatal ErrorException, crashing your application.

Here is the technical breakdown of why this is happening and the three specific strategies to resolve it.

The Root Cause: Object Shapes and Optimization

Historically, PHP allowed developers to assign values to undeclared properties on any object. While flexible, this behavior incurs significant technical debt:

  1. Performance: PHP engines (Zend) rely on "object shapes" (hidden classes) to optimize property access. When you dynamically add properties, you mutate the object's shape at runtime, forcing the engine to de-optimize and fall back to slower hash-table lookups.
  2. Maintainability: Dynamic properties defeat static analysis tools (PHPStan, Psalm) and IDE autocompletion, making refactoring risky.
  3. Correctness: It allows silent failure on typos (e.g., assigning to $user->nmae instead of $user->name).

PHP 8.2 restricts this behavior to push developers toward explicit schema definitions.


Solution 1: Explicit Property Declaration (Recommended)

The most robust fix is to strictly define your class schema. This is the "Principal Engineer" approach: it fixes the deprecation, documents the code, improves performance, and enables type safety.

The Legacy Code (Fails in 8.2)

<?php

class UserEntity {
    public function __construct(int $id, string $name) {
        // Triggers Deprecation in 8.2
        $this->id = $id;
        $this->name = $name;
    }
}

$user = new UserEntity(1, 'Alice');
$user->isAdmin = true; // Triggers Deprecation in 8.2

The Modern Fix

Refactor the class to declare properties. Use typed properties (available since PHP 7.4) and readonly (PHP 8.1+) where immutable state is required.

<?php

declare(strict_types=1);

class UserEntity {
    public int $id;
    public string $name;
    public bool $isAdmin = false; // Default value

    public function __construct(int $id, string $name) {
        $this->id = $id;
        $this->name = $name;
    }
}

$user = new UserEntity(1, 'Alice');
$user->isAdmin = true; // Valid

Why this works: The properties exist in the class definition (the VTable). The engine knows the memory offset for these properties at compile time.


Solution 2: The #[AllowDynamicProperties] Attribute

If you are maintaining a massive legacy codebase (e.g., Magento 1, old WordPress plugins) or a library that acts as a generic data container, refactoring every class may be impossible.

PHP 8.2 introduces the #[AllowDynamicProperties] attribute. This signals to the engine that strict shape enforcement should be disabled for this specific class.

The Fix

Simply add the attribute just above the class definition.

<?php

declare(strict_types=1);

// This attribute opts-out of the deprecation notice
#[AllowDynamicProperties]
class LegacyConfigContainer {
    public function __construct(array $config) {
        foreach ($config as $key => $value) {
            // This is now allowed in 8.2 without warnings
            $this->{$key} = $value;
        }
    }
}

$config = new LegacyConfigContainer(['db_host' => 'localhost', 'retries' => 3]);
echo $config->db_host; // Output: localhost

Note on Inheritance: Child classes automatically inherit this behavior. You do not need to re-declare the attribute on classes extending LegacyConfigContainer.


Solution 3: Using stdClass or __get/__set

There are specific use cases—such as parsing arbitrary JSON responses or building fluent builders—where dynamic properties are a feature, not a bug.

Option A: Use stdClass

The built-in stdClass is explicitly exempt from this deprecation. If your object has no methods and is purely a data transfer object (DTO) for unstructured data, cast it or use stdClass.

<?php

$payload = json_decode('{"status": "ok", "meta": {"count": 5}}');

// json_decode returns stdClass by default, which allows dynamic properties
$payload->timestamp = time(); // Valid in 8.2+

Option B: Magic Methods

If you need logic attached to dynamic data, implement __get and __set. Classes implementing these magic methods are exempt from the deprecation because the property assignment is intercepted by logic rather than the object's internal structure map.

<?php

declare(strict_types=1);

class DynamicRegistry {
    private array $storage = [];

    public function __set(string $name, mixed $value): void {
        $this->storage[$name] = $value;
    }

    public function __get(string $name): mixed {
        return $this->storage[$name] ?? null;
    }
}

$registry = new DynamicRegistry();
// Calls __set(), does not trigger dynamic property deprecation
$registry->apiKey = '123-456'; 

Summary

  1. Audit: Run your test suite with E_DEPRECATED enabled to find offenders.
  2. Refactor (Best): Add typed property declarations to your classes.
  3. Patch (Legacy): Apply #[AllowDynamicProperties] to classes that cannot be refactored.
  4. Architect: Use stdClass or Magic Methods for truly dynamic data structures.

Fixing this now is critical. Ignoring these notices effectively puts an expiration date on your application matching the release of PHP 9.0.