Skip to content

Commit 9bb980a

Browse files
committed
Autovivify via a handler
1 parent 5bd9a06 commit 9bb980a

7 files changed

+71
-6
lines changed

Zend/tests/offsets/auto-vivification-objects.phpt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Auto-vivification for objects
33
--FILE--
44
<?php
55

6-
class Vector implements DimensionFetchable, DimensionWritable, FetchAppendable {
6+
class Vector implements DimensionFetchable, DimensionWritable, FetchAppendable, Autovivificapable {
77
private array $a = [];
88

99
private function dumpCall($method): void {
@@ -35,6 +35,10 @@ class Vector implements DimensionFetchable, DimensionWritable, FetchAppendable {
3535
$ref =& $this->a[];
3636
return $ref;
3737
}
38+
public function autovivify(null &$ref): void {
39+
$this->dumpCall(__METHOD__);
40+
$ref = new self();
41+
}
3842
}
3943

4044
$obj = new Vector();
@@ -54,8 +58,10 @@ var_dump($obj[2]);
5458
Object ID #1 called Vector::offsetFetch
5559
NULL
5660
Object ID #1 called Vector::offsetFetch
61+
Object ID #1 called Vector::autovivify
5762
Object ID #2 called Vector::append
5863
Object ID #1 called Vector::offsetFetch
64+
Object ID #1 called Vector::autovivify
5965
Object ID #3 called Vector::offsetSet
6066
Check results
6167
Object ID #1 called Vector::offsetGet

Zend/zend_dimension_handlers.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ typedef struct _zend_internal_class_dimensions_functions {
3434
void (*/* const */ append)(zend_object *object, zval *value);
3535
zval *(*/* const */ fetch_append)(zend_object *object, zval *rv);
3636
void (*/* const */ unset_dimension)(zend_object *object, zval *offset);
37+
void (*/* const */ autovivify)(zend_object *object, zval *reference);
3738
} zend_internal_class_dimensions_functions;
3839

3940
typedef struct _zend_user_class_dimensions_functions {
@@ -44,6 +45,7 @@ typedef struct _zend_user_class_dimensions_functions {
4445
zend_function *append;
4546
zend_function *fetch_append;
4647
zend_function *unset_dimension;
48+
zend_function *autovivify;
4749
} zend_user_class_dimensions_functions;
4850

4951
ZEND_API zval* zend_class_read_dimension(zend_object *object, zval *offset, zval *rv);
@@ -53,6 +55,7 @@ ZEND_API void zend_class_write_dimension(zend_object *object, zval *offset, zva
5355
ZEND_API void zend_class_append(zend_object *object, zval *value);
5456
ZEND_API zval *zend_class_fetch_append(zend_object *object, zval *rv);
5557
ZEND_API void zend_class_unset_dimension(zend_object *object, zval *offset);
58+
ZEND_API void zend_class_autovivify(zend_object *object, zval *reference);
5659

5760
/* VM and JIT Helper */
5861
ZEND_API bool zend_class_isset_empty_dimension(zend_object *object, zval *offset, bool is_empty);

Zend/zend_execute.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,11 +3065,11 @@ static zend_never_inline void zend_fetch_object_dimension_address(zval *result,
30653065
/* Check if we need to auto-vivify a possible null return */
30663066
if (UNEXPECTED(
30673067
opline->extended_value == ZEND_FETCH_DIM_DIM
3068-
&& Z_ISREF_P(result)
3069-
&& Z_ISNULL_P(Z_REFVAL_P(result))
3068+
&& obj->ce->dimension_handlers->autovivify
3069+
&& Z_ISREF_P(retval)
3070+
&& Z_ISNULL_P(Z_REFVAL_P(retval))
30703071
)) {
3071-
// TODO THIS IS A VERY CRUDE PROTOTYPE
3072-
object_init_ex(Z_REFVAL_P(result), obj->ce);
3072+
zend_class_autovivify(obj, retval);
30733073
}
30743074
if (result != retval) {
30753075
ZVAL_INDIRECT(result, retval);

Zend/zend_interfaces_dimension.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ ZEND_API zend_class_entry *zend_ce_dimension_write;
3333
ZEND_API zend_class_entry *zend_ce_dimension_unset;
3434
ZEND_API zend_class_entry *zend_ce_appendable;
3535
ZEND_API zend_class_entry *zend_ce_dimension_fetch_append;
36+
ZEND_API zend_class_entry *zend_ce_autovivificapable;
3637

3738
ZEND_API zval* zend_class_read_dimension(zend_object *object, zval *offset, zval *rv) {
3839
if (object->ce->type == ZEND_USER_CLASS) {
@@ -257,6 +258,20 @@ ZEND_API void zend_class_unset_dimension(zend_object *object, zval *offset)
257258
}
258259
}
259260

261+
ZEND_API void zend_class_autovivify(zend_object *object, zval *reference)
262+
{
263+
if (object->ce->type == ZEND_USER_CLASS) {
264+
zend_function *zf = object->ce->dimension_functions->autovivify;
265+
ZEND_ASSERT(zf);
266+
GC_ADDREF(object);
267+
zend_call_known_instance_method_with_1_params(zf, object, /* retval */ NULL, reference);
268+
OBJ_RELEASE(object);
269+
} else {
270+
ZEND_ASSERT(object->ce->type == ZEND_INTERNAL_CLASS);
271+
object->ce->dimension_handlers->autovivify(object, reference);
272+
}
273+
}
274+
260275
/* rv is a slot provided by the callee that is returned */
261276
static zval *zend_user_class_read_dimension(zend_object *object, zval *offset, zval *rv)
262277
{
@@ -547,6 +562,19 @@ static int zend_implement_dimension_fetch_append(zend_class_entry *interface, ze
547562
return SUCCESS;
548563
}
549564

565+
static int zend_implement_autovivificapable(zend_class_entry *interface, zend_class_entry *class_type)
566+
{
567+
if (class_type->type == ZEND_INTERNAL_CLASS) {
568+
return SUCCESS;
569+
}
570+
571+
zend_user_class_dimensions_functions *funcs = NULL;
572+
ALLOC_HANDLERS_IF_MISSING(funcs, class_type);
573+
574+
funcs->autovivify = zend_hash_str_find_ptr(&class_type->function_table, "autovivify", strlen("autovivify"));
575+
return SUCCESS;
576+
}
577+
550578
ZEND_API void zend_register_dimension_interfaces(void)
551579
{
552580
zend_ce_dimension_read = register_class_DimensionReadable();
@@ -569,4 +597,7 @@ ZEND_API void zend_register_dimension_interfaces(void)
569597

570598
zend_ce_arrayaccess = register_class_ArrayAccess(zend_ce_dimension_read, zend_ce_dimension_write, zend_ce_dimension_unset);
571599
zend_ce_arrayaccess->interface_gets_implemented = zend_implement_arrayaccess;
600+
601+
zend_ce_autovivificapable = register_class_Autovivificapable();
602+
zend_ce_autovivificapable->interface_gets_implemented = zend_implement_autovivificapable;
572603
}

Zend/zend_interfaces_dimension.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ extern ZEND_API zend_class_entry *zend_ce_dimension_write;
3232
extern ZEND_API zend_class_entry *zend_ce_dimension_unset;
3333
extern ZEND_API zend_class_entry *zend_ce_appendable;
3434
extern ZEND_API zend_class_entry *zend_ce_dimension_fetch_append;
35+
extern ZEND_API zend_class_entry *zend_ce_autovivificapable;
3536

3637
ZEND_API void zend_register_dimension_interfaces(void);
3738

Zend/zend_interfaces_dimensions.stub.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ interface FetchAppendable extends Appendable
3434
public function &fetchAppend(): mixed;
3535
}
3636

37+
interface Autovivificapable
38+
{
39+
public function autovivify(null &$ref): void;
40+
}
41+
3742
interface ArrayAccess extends DimensionReadable, DimensionWritable, DimensionUnsetable
3843
{
3944
/** @tentative-return-type */

Zend/zend_interfaces_dimensions_arginfo.h

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)