Take the State of PHP 2026 Survey →

Deprecate dynamic properties View as Markdown

Implemented (PHP 8.2)
Authors
Nikita Popov nikic@php.net
Date
2021-08-23
Target
PHP 8.2
Implementation
https://github.com/php/php-src/pull/7571
Source
wiki.php.net/rfc/deprecate_dynamic_properties

Vote

Deprecate dynamic property creation with #[AllowDynamicProperties] opt-in? closed
Yes
52
No
25
77 voters
alcaeus Yes 2021-11-12 18:56
alec No 2021-11-13 07:25
andi No 2021-11-15 15:34
asgrim Yes 2021-11-12 14:55
ashnazg Yes 2021-11-17 17:58
beberlei Yes 2021-11-13 19:26
bishop Yes 2021-11-18 14:50
bmajdak Yes 2021-11-12 16:18
brandon No 2021-11-13 17:11
brzuchal Yes 2021-11-12 15:50
bukka No 2021-11-14 12:34
bwoebi Yes 2021-11-12 12:58
carusogabriel Yes 2021-11-22 21:38
chx No 2021-11-23 20:15
cmb Yes 2021-11-12 12:20
colinodell Yes 2021-11-12 15:05
crell Yes 2021-11-12 15:14
cschneid No 2021-11-24 15:26
danack Yes 2021-11-15 12:18
daverandom Yes 2021-11-12 16:48
derick No 2021-11-12 15:11
dharman Yes 2021-11-12 13:26
didou Yes 2021-11-12 16:16
ekin Yes 2021-11-24 09:16
evvc Yes 2021-11-18 07:30
galvao Yes 2021-11-12 16:10
gasolwu Yes 2021-11-13 09:26
geekcom No 2021-11-26 00:47
girgias Yes 2021-11-12 15:38
heiglandreas No 2021-11-15 12:33
ilutov Yes 2021-11-12 14:26
irker Yes 2021-11-13 17:02
jbnahan Yes 2021-11-14 20:06
jhdxr Yes 2021-11-26 04:43
kalle Yes 2021-11-13 06:04
kelunik Yes 2021-11-14 15:13
kguest Yes 2021-11-15 20:09
kocsismate Yes 2021-11-12 13:10
krakjoe Yes 2021-11-15 19:19
laruence No 2021-11-17 11:08
lbarnaud Yes 2021-11-13 09:39
lcobucci Yes 2021-11-13 09:27
levim Yes 2021-11-16 02:24
lufei Yes 2021-11-20 11:17
mansion No 2021-11-12 16:37
marandall Yes 2021-11-12 14:02
mauricio No 2021-11-12 23:51
mbeccati No 2021-11-22 08:09
mcmic Yes 2021-11-15 08:55
nicolasgrekas Yes 2021-11-12 16:57
nikic Yes 2021-11-12 12:04
ocramius Yes 2021-11-12 14:27
pajoye No 2021-11-14 08:59
patrickallaert Yes 2021-11-13 11:40
petk Yes 2021-11-12 13:23
pollita Yes 2021-11-12 14:58
ralphschindler No 2021-11-16 18:13
ramsey No 2021-11-12 16:20
rasmus No 2021-11-14 12:45
reywob No 2021-11-12 20:43
salathe No 2021-11-15 10:15
sebastian No 2021-11-23 09:50
seld Yes 2021-11-17 08:07
sergey Yes 2021-11-12 21:44
stas No 2021-11-14 07:01
svpernova09 Yes 2021-11-16 02:44
tandre Yes 2021-11-21 15:14
thekid Yes 2021-11-12 18:22
theodorejb Yes 2021-11-12 15:51
thorstenr No 2021-11-15 05:32
trowski Yes 2021-11-12 17:12
tstarling No 2021-11-12 23:46
twosee Yes 2021-11-15 05:57
villfa Yes 2021-11-13 12:13
weierophinney No 2021-11-12 18:41
wyrihaximus Yes 2021-11-25 22:48
zeev No 2021-11-14 10:50

Discussions on the mailing list

[RFC] Deprecate dynamic properties
by Nikita Popov · 82 messages
[VOTE] Deprecate dynamic properties
by Nikita Popov · 39 messages
[RFC] [Discussion] Readonly Classes
· 9 messages
Strict properties and unserialization
by Pierrick Charron · 2 messages

RFC text

Copied from wiki.php.net, last synchronized Oct 13, 2026 11:50 UTC

Introduction

When writing to a property that has not been declared, PHP will silently create a dynamic property instead. In modern code, this is rarely done intentionally. This RFC proposes to deprecate and later remove the creation of dynamic properties, unless the class explicitly allows dynamic properties. stdClass and __get/__set are not affected by this change.

class User {
    public $name;
}
 
$user = new User;
 
// Assigns declared property User::$name.
$user->name = "foo";
 
// Oops, a typo:
$user->nane = "foo";
// PHP <= 8.1: Silently creates dynamic $user->nane property.
// PHP    8.2: Raises deprecation warning, still creates dynamic property.
// PHP    9.0: Throws Error exception.

“Dynamic property” here refers to a property that has not been declared in the class. It has no relation to the access style (e.g. $user->{'na' . 'me'} is still an access to a declared property).

Motivation

The motivation for this change is twofold: To prevent mistakes (due to typos or renames) in the common case, and to make intentional uses explicit. The core problem is that reading from a non-existing property issues a diagnostic that makes the issue immediately apparent, while writing to a non-existing property is entirely silent. PHP gives no indication whatsoever that the programmer has made a mistake.

A common counter-argument is that even if PHP itself does not detect the mistake, static analysis still can. While this is true to a degree, there are a number of problems:

  1. Static analysis in IDEs (probably the most widespread type of static analysis used in PHP) has to be conservative about diagnostics relating to dynamic properties. For example, PhpStorm treats creation of dynamic properties as only a weak warning (non-intrusive grey underline), because it cannot distinguish whether this dynamic property assignment is indeed a bug, or an intentional use. Treating dynamic property creation as a more severe error would result in false positives in cases where dynamic properties are used intentionally. The #[AllowDynamicProperties] attribute proposed in this RFC makes the cases where dynamic properties are used intentionally explicit. This means that static analysis tooling can both a) suppress any diagnostic in explicitly allowed cases and b) report a hard error in all other cases, without producing any false positives.
  2. Static analysis can only analyze assignments with a known object type and property name. This does not include any kind of dynamic assignments, such as those that may be performed by a serializer, hydrator or any other mechanism that directly populates objects without going through a constructor. Static analysis also requires a fully-typed codebase, as the actually assigned property may otherwise not be known.
  3. On a more philosophical note, I believe that a programming language should be usable without external tooling. While IDE use is widespread among PHP programmers, it should still be possible to write PHP code in a text editor like vim without exposing you to silent bug classes.

Finally, if classes using dynamic properties are explicitly declared, then we no longer need to reserve space for them on each object. This would reduce the size of all objects (that don't opt-in to dynamic properties) by 8 bytes. However, this is a fairly long-term benefit that will require additional technical work to realize.

Proposal

The creation of dynamic properties on classes that aren't marked with the #[AllowDynamicProperties] attribute is deprecated in PHP 8.2 and becomes an Error exception in PHP 9.0. All used properties should be declared in the class declaration.

class Foo {}
$foo = new Foo;
 
// Deprecated: Creation of dynamic property Foo::$bar is deprecated
$foo->bar = 1;
 
// No deprecation warning: Dynamic property already exists.
$foo->bar = 2;

Classes marked with #[AllowDynamicProperties] as well as their children can continue using dynamic properties without deprecation or removal. The only bundled class marked as #[AllowDynamicProperties] is stdClass.

$obj = (object) []; // = new stdClass;
 
// No deprecation warning
$obj->foo = 1;
 
#[AllowDynamicProperties]
class Test {}
class Test2 extends Test {}
 
// No deprecation warning
$obj = new Test;
$obj->bar = 1;
 
// No deprecation warning
$obj = new Test2;
$obj->bar = 1;

It should be noted that properties accessed through __get()/__set() are not considered as “dynamic properties”. The following example does not generate any deprecation warnings:

class ArrayLikeObject {
    private array $data = [];
    public function &__get($name) { return $this->data[$name]; }
    public function __isset($name, $value) { return isset($this->data[$name]; }
    public function __set($name, $value) { $this->data[$name] = $value; }
    public function __unset($name) { unset($this->data[$name]; }
}
 
$obj = new ArrayLikeObject;
 
// Calls ArrayLikeObject::__set(), no deprecation warning.
$obj->foo = 1;

Backward Incompatible Changes

Removing support for dynamic properties constitutes a significant backwards compatibility break. While modern PHP code consistently declares used properties, this is not necessarily the case for legacy code. When encountered with a dynamic property deprecation warning, there are a number of things that can be done to avoid it.

The simplest and most common would be to simply add the property declaration:

class Test {
    public $value; // <-- Add property declaration.
 
    public function __construct($value) {
        $this->value = $value;
    }
}

For classes that intentionally don't have a fixed set of properties, it's possible to either implement magic __get()/__set() or mark the class using the #[AllowDynamicProperties] attribute. Marking a class with #[AllowDynamicProperties] is fully backwards-compatible with earlier PHP versions, because prior to PHP 8.0 this would be interpreted as a comment, and the use non-existent classes as attributes is not an error.

In some cases it is desirable to associate information with objects that you do not own. Previously, it was possible to add a dynamic property for this purpose. Instead, a WeakMap should be used to store the information in a non-intrusive way:

class Test {
    private WeakMap $extraInfo;
 
    public function addExtraInfo(object $obj) {
        // Instead of:
        $obj->extraInfo = ...;
        // Use:
        $this->extraInfo[$obj] = ...;
    }
}

In rare cases, dynamic properties are used for lazy initialization. For example Symfony's Constraint::$groups property is not declared and then dynamically created inside __get(). This use-case can be accommodated by declaring the property, but then unsetting it in the constructor:

abstract class Constraint {
    public $groups;
 
    public function __construct() {
        unset($this->groups);
    }
 
    public function __get($name) {
        // Will get called on first access, but once initialized.
        $this->groups = ...;
    }
}

A declared property that has been unset remains a declared property, and will not result in a dynamic property when it is reinitialized.

Discussion

Alternative opt-in to dynamic properties

This RFC offers #[AllowDynamicProperties] as a way to opt-in to the use of dynamic properties. A previous version of this proposal instead suggested to extend from stdClass, and make stdClass the only class with first-class dynamic property support.

The difference between these approaches is in the end goal: #[AllowDynamicProperties] requires making classes that rely on dynamic properties explicit and prevents accidental use of dynamic properties. This is a big win for the ecosystem, but it does not have much effect on the overall complexity of the language or implementation, as dynamic properties still need to be supported on arbitrary classes. Requiring an extension of stdClass would allow us to actually remove the “dynamic properties” concept from the language in the future: stdClass would effectively just provide very optimized implementations of __get() and __set().

While completely removing dynamic properties is a worthwhile end goal, we also need to acknowledge that dynamic properties have played an important historical role in PHP, and legacy codebases in particular may be making heavy use of them. While adding an attribute provides a straightforward upgrade path, extending stdClass may not always be easily possible due to lack of multiple inheritance support. For this reason, this RFC pursues the more conservative attribute-based approach.

We may still wish to remove dynamic properties entirely at some later point. Having the #[AllowDynamicProperties] attribute will make it much easier to evaluate such a move, as it will be easier to analyze how much and in what way dynamic properties are used in the ecosystem.

Opt-out of dynamic properties instead

The Locked classes RFC took an alternative approach to this problem space: Rather than deprecating/removing dynamic properties and providing an opt-in for specific classes, it instead allowed marking specific classes as locked in order to forbid creation of dynamic properties on them.

I don't believe that this is the right strategy, because in contemporary code, classes being “locked” is the default state, while classes that require dynamic properties are a rare exception. Additionally, this requires that class owners (which may be 3rd party packages) consistently add the “locked” keyword to be effective.

An alternative that has been discussed in the context of the language evolution proposal is to instead disallow the use of dynamic properties in a file through a declare directive (whether that be a fine-grained option or an “edition” mechanism). This does not require the cooperation of 3rd-party libraries.

However, based on the discussion on the language evolution proposal, this would only delay the time where disallowed dynamic properties become the default and only behavior, as there was a strong consensus that diverging language behavior should not be maintained indefinitely. Dynamic properties would ultimately still get deprecated and removed.

Vote

Voting started 2021-11-12 and ended 2021-11-26.

Vote: Deprecate dynamic property creation with #[AllowDynamicProperties] opt-in? — Yes: 52 · No: 25 (closed)