Skip to content

Commit f4eea7c

Browse files
committed
Prototype for auto-vivification
1 parent dd04654 commit f4eea7c

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
--TEST--
2+
Auto-vivification for objects
3+
--FILE--
4+
<?php
5+
6+
class Vector implements DimensionFetchable, DimensionWritable, FetchAppendable {
7+
private array $a = [];
8+
9+
private function dumpCall($method): void {
10+
echo 'Object ID #', spl_object_id($this), ' called ', $method, PHP_EOL;
11+
}
12+
13+
public function offsetExists($offset): bool {
14+
$this->dumpCall(__METHOD__);
15+
return array_key_exists($offset, $this->a);
16+
}
17+
public function offsetGet($offset): mixed {
18+
$this->dumpCall(__METHOD__);
19+
return $this->a[$offset];
20+
}
21+
public function offsetSet($offset, $value): void {
22+
$this->dumpCall(__METHOD__);
23+
$this->a[$offset] = $value;
24+
}
25+
public function &offsetFetch($offset): mixed {
26+
$this->dumpCall(__METHOD__);
27+
return $this->a[$offset];
28+
}
29+
public function append($value): void {
30+
$this->dumpCall(__METHOD__);
31+
$this->a[] = $value;
32+
}
33+
public function &fetchAppend(): mixed {
34+
$this->dumpCall(__METHOD__);
35+
$ref =& $this->a[];
36+
return $ref;
37+
}
38+
}
39+
40+
$obj = new Vector();
41+
42+
$ref = &$obj[0];
43+
var_dump($ref);
44+
45+
$obj[1][] = 'append';
46+
$obj[2][50] = 'set-to-offset';
47+
48+
echo "Check results\n";
49+
var_dump($obj[1]);
50+
var_dump($obj[2]);
51+
52+
?>
53+
--EXPECT--
54+
Object ID #1 called Vector::offsetFetch
55+
NULL
56+
Object ID #1 called Vector::offsetFetch
57+
Object ID #2 called Vector::append
58+
Object ID #1 called Vector::offsetFetch
59+
Object ID #3 called Vector::offsetSet
60+
Check results
61+
Object ID #1 called Vector::offsetGet
62+
object(Vector)#2 (1) {
63+
["a":"Vector":private]=>
64+
array(1) {
65+
[0]=>
66+
string(6) "append"
67+
}
68+
}
69+
Object ID #1 called Vector::offsetGet
70+
object(Vector)#3 (1) {
71+
["a":"Vector":private]=>
72+
array(1) {
73+
[50]=>
74+
string(13) "set-to-offset"
75+
}
76+
}

Zend/zend_execute.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,6 +3044,7 @@ static zend_never_inline void zend_fetch_object_dimension_address(zval *result,
30443044
* $ao[] = &$var;
30453045
* cases */
30463046
&& Z_TYPE_P(retval) != IS_INDIRECT
3047+
/* IS_OBJECT is to support ArrayAccess without by-ref returns */
30473048
&& Z_TYPE_P(retval) != IS_OBJECT
30483049
) {
30493050
zend_class_entry *ce = obj->ce;
@@ -3057,6 +3058,19 @@ static zend_never_inline void zend_fetch_object_dimension_address(zval *result,
30573058
ZVAL_UNDEF(result);
30583059
goto clean_up;
30593060
}
3061+
/* Check if we need to auto-vivify a possible null return */
3062+
if (Z_ISREF_P(result) && Z_ISNULL_P(Z_REFVAL_P(result))) {
3063+
const zend_op *next_opline = execute_data->opline + 1;
3064+
if (UNEXPECTED(
3065+
next_opline->opcode == ZEND_ASSIGN_DIM
3066+
|| next_opline->opcode == ZEND_ASSIGN_DIM_OP
3067+
|| next_opline->opcode == ZEND_FETCH_DIM_W
3068+
|| next_opline->opcode == ZEND_FETCH_DIM_RW
3069+
)) {
3070+
// TODO THIS IS A VERY CRUDE PROTOTYPE
3071+
object_init_ex(Z_REFVAL_P(result), obj->ce);
3072+
}
3073+
}
30603074
if (result != retval) {
30613075
ZVAL_INDIRECT(result, retval);
30623076
}

0 commit comments

Comments
 (0)