Next revision | Previous revision |
rfc:remove_object_auto_vivification [2019/02/25 10:20] – created nikic | rfc:remove_object_auto_vivification [2019/02/25 10:31] (current) – nikic |
---|
* Status: Draft | * Status: Draft |
* Target Version: PHP 8.0 | * Target Version: PHP 8.0 |
| * Implementation: https://github.com/php/php-src/pull/3865 |
| |
===== Introduction ===== | ===== Introduction ===== |
When assigning a property to a falsy value, PHP will currently convert the falsy value into an ''stdClass'' object and emit a warning. This RFC proposes to remove this "auto-vivification" behavior, and make changes to object write semantics made possible by the removal of this functionality. | When assigning a property to a falsy value, PHP will currently convert the falsy value into an ''stdClass'' object and emit a warning. This RFC proposes to remove this "auto-vivification" behavior, and make changes to object write semantics made possible by the removal of this functionality. |
| |
An example of the current behavior is shown in the following example: | An example of the current behavior is shown in the following: |
| |
<code php> | <code php> |
In the first two cases, the value is falsy and a property assignment results in the creation of an object. In the last case, the value is truthy and no implicit conversion into an object occurs. | In the first two cases, the value is falsy and a property assignment results in the creation of an object. In the last case, the value is truthy and no implicit conversion into an object occurs. |
| |
The auto-vivification behavior places a somewhat subtle requirement on the interpretation of property assignments: Normally, when performing an ''$obj->prop = $val'' operation, this only modifies the object stored in ''$obj'', but importantly, does not modify the storage location of ''$obj'' itself. After the operation, ''$obj'' will always point to the same object, even if that object has undergone internal state changes. | The auto-vivification behavior places a somewhat subtle requirement on the interpretation of property assignments: Normally, when performing an ''%%$obj->prop = $val%%'' operation, this only modifies the object stored in ''$obj'', but does not modify the storage location of ''$obj'' itself. After the operation, ''$obj'' will always point to the same object, even if that object has undergone internal state changes. |
| |
However, the existence of auto-vivification makes this not strictly true: If ''$obj'' is a falsy value, then it will be converted into an object, and that requires changing the storage location of ''$obj'' itself. This distinction doesn't matter in most cases, but can be observed when magic ''%%__get()%%'' (or ArrayAccess ''offsetGet'') is used: | However, the existence of auto-vivification makes this not strictly true: If ''$obj'' is a falsy value, then it will be converted into an object, and that requires changing the storage location of ''$obj'' itself. This distinction doesn't matter in most cases, but can be observed when magic ''%%__get()%%'' (or ArrayAccess ''offsetGet'') is used: |
</code> | </code> |
| |
In this example, the assignment to ''$magic->arrayOfObj[0]->prop'' generates an "indirect modification has no effect" notice, while the object it still successfully modified. The reason for this behavior is that the assignment needs to fetch ''$magic->arrayOfObj[0]'' for-write on the off-chance that a falsy value will have to be converted into an object. | In this example, the assignment to ''%%$magic->arrayOfObj[0]->prop%%'' generates an "indirect modification has no effect" notice, while the object it still successfully modified. The reason for this behavior is that the assignment needs to fetch ''%%$magic->arrayOfObj[0]%%'' for-write on the off-chance that a falsy value will have to be converted into an object. |
| |
===== Proposal ===== | ===== Proposal ===== |
==== Fetch LHS of property assignments for-read ==== | ==== Fetch LHS of property assignments for-read ==== |
| |
When performing ''$a->b = $c'', fetch ''$a'' for-read rather than for-write. This means that the magic ''%%__get()%%'' example above will no longer generate a notice. | When performing ''%%$a->b = $c%%'', fetch ''$a'' for-read rather than for-write. This means that the magic ''%%__get()%%'' example above will no longer generate a notice. |
| |
The other side-effect of this change is that invalid accesses on the left hand side of the assignment will now be reported. Consider this example where ''$abc'' is an undefined variable: | The other side-effect of this change is that invalid accesses on the left hand side of the assignment will now be reported. Consider this example, where ''$abc'' is assumed to be an undefined variable: |
| |
<code php> | <code php> |
</code> | </code> |
| |
Previously this only generated the "creating default object" warning, because for-write fetches suppress most other types of diagnostics. With this RFC the fetch is performed for-read and the use of an undefinde variable is properly reported. | Previously this only generated the "creating default object" warning, because for-write fetches suppress most other types of diagnostics. With this RFC the fetch is performed for-read and the use of an undefined variable is reported properly. |
| |
=== SimpleXML === | === SimpleXML === |
| |
<code> | <code> |
<?xml version="1.0"?> <collection><movie><characters><character><name>Tom Hanks</name></character></characters></movie></collection> | <?xml version="1.0"?> |
| <collection><movie><characters><character><name>Tom Hanks</name></character></characters></movie></collection> |
</code> | </code> |
| |
This functionality is currently implemented under the assumption that everything is fetched for-write, while this RFC proposes to fetch the ''$xml->movie[]->characters->character[0]'' portion of the access for-read. This will result in a "Cannot use [] for reading" compile-time error. | This functionality is currently implemented under the assumption that everything is fetched for-write, while this RFC proposes to fetch the ''%%$xml->movie[]->characters->character[0]%%'' portion of the access for-read. This will result in a "Cannot use [] for reading" compile-time error. |
| |
However, even if the code is adjusted to use ''$xml->movie[0]'' rather than ''$xml->movie[]'' (which is at least legal as far as the compiler is concerned), restoring the previous behavior here will still be something of a challenge. | However, even if the code is adjusted to use ''%%$xml->movie[0]%%'' rather than ''%%$xml->movie[]%%'' (which is at least legal as far as the compiler is concerned), restoring the previous behavior here will still be something of a challenge. |
| |
I'm unsure what to do about this as yet. | I'm unsure what to do about this as yet. |