Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision |
rfc:deprecate_dynamic_properties [2021/08/25 10:59] – nikic | rfc:deprecate_dynamic_properties [2021/11/12 12:04] – nikic |
---|
* Date: 2021-08-23 | * Date: 2021-08-23 |
* Author: Nikita Popov <nikic@php.net> | * Author: Nikita Popov <nikic@php.net> |
* Status: Under Discussion | * Status: Voting |
* Target Version: PHP 8.2 | * Target Version: PHP 8.2 |
* Implementation: https://github.com/php/php-src/pull/7390 | * Implementation: https://github.com/php/php-src/pull/7571 |
| |
===== Introduction ===== | ===== 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. stdClass and %%__get%%/%%__set%% are not affected by this change. | 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. |
| |
<PHP> | <PHP> |
// Oops, a typo: | // Oops, a typo: |
$user->nane = "foo"; | $user->nane = "foo"; |
// PHP < 8.1: Silently creates dynamic $user->nane property. | // PHP <= 8.1: Silently creates dynamic $user->nane property. |
// PHP 8.2: Throws deprecation warning, still creates dynamic property. | // PHP 8.2: Raises deprecation warning, still creates dynamic property. |
// PHP 9.0: Throws Error exception. | // PHP 9.0: Throws Error exception. |
</PHP> | </PHP> |
| |
===== Proposal ===== | ===== Proposal ===== |
| |
The creation of dynamic properties on classes that don't inherit from ''stdClass'' 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. | 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. |
| |
<PHP> | <PHP> |
</PHP> | </PHP> |
| |
Objects of type ''stdClass'' and inheriting classes continue to support dynamic properties. | 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''. |
| |
<PHP> | <PHP> |
$obj->foo = 1; | $obj->foo = 1; |
| |
class myStdClass extends stdClass {} | #[AllowDynamicProperties] |
$obj2 = new myStdClass; | class Test {} |
| class Test2 extends Test {} |
| |
// No deprecation warning | // No deprecation warning |
$obj2->bar = 1; | $obj = new Test; |
</PHP> | $obj->bar = 1; |
| |
''stdClass'' objects are specifically intended to hold dynamic properties. ''extends stdClass'' is offered as a simple migration strategy for custom classes that are also specifically intended for use with dynamic properties. | // No deprecation warning |
| $obj = new Test2; |
| $obj->bar = 1; |
| </PHP> |
| |
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: | 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: |
</PHP> | </PHP> |
| |
For classes that intentionally don't have a fixed set of properties, it's possible to either implement magic ''%%__get()%%''/''%%__set()%%'', or to extend from the ''stdClass'' class, or from ''ArrayObject'' in ''ARRAY_AS_PROPS'' mode. | 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. |
| |
Using magic getters/setters provides the most control, but extending from ''stdClass'' will make dynamic property accesses more efficient by using optimized engine hooks. It will also match the current behavior most closely, for example with regard to the behavior of ''foreach'' or ''property_exists()''. | |
| |
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: | 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: |
==== Alternative opt-in to dynamic properties ==== | ==== Alternative opt-in to dynamic properties ==== |
| |
This RFC offers ''extends stdClass'' as a way to opt-in to the use of dynamic properties. Some people have suggested that we could either use a magic marker interface (''implements SupportsDynamicProperties'') or an attribute (''#[SupportsDynamicProperties]'') instead. | 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 reasoning behind the ''extends stdClass'' choice is that it is well-defined without introducing any special language support: Imagine that ''stdClass'' implements ''%%__get()%%''/''%%__set()%%'' to provide its "dynamic properties" support. A class extending from ''stdClass'' would naturally inherit this behavior according to normal inheritance semantics. Of course, ''stdClass'' currently doesn't implement magic get/set, but we probably do want to implement them when it comes to dropping engine support for dynamic properties. | 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()%%''. |
| |
Using an interface or attribute instead would require the engine to continue supporting dynamic properties on arbitrary classes long term, rather than simply inheriting the behavior from a single class that implements the functionality. | 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 ==== | ==== Opt-out of dynamic properties instead ==== |
===== Vote ===== | ===== Vote ===== |
| |
Yes/No. | Voting started 2021-11-12 and ended 2021-11-26. |
| |
| <doodle title="Deprecate dynamic property creation with #[AllowDynamicProperties] opt-in?" auth="nikic" voteType="single" closed="false"> |
| * Yes |
| * No |
| </doodle> |