diff --git a/Zend/tests/data_class_001.phpt b/Zend/tests/data_class_001.phpt new file mode 100644 index 0000000000000..92a9801a1ec99 --- /dev/null +++ b/Zend/tests/data_class_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +Data classes can be defined and compared. +--FILE-- + +--EXPECT-- +bool(true) +bool(false) +bool(false) +bool(true) +data object(Point)#1 (2) { + ["x"]=> + int(1) + ["y"]=> + int(2) +} diff --git a/Zend/tests/data_class_002.phpt b/Zend/tests/data_class_002.phpt new file mode 100644 index 0000000000000..848f49519349d --- /dev/null +++ b/Zend/tests/data_class_002.phpt @@ -0,0 +1,29 @@ +--TEST-- +Data classes can be combined with inheritance and other features. +--FILE-- + +--EXPECT-- +bool(true) +bool(false) +bool(false) +bool(true) diff --git a/Zend/tests/data_class_003.phpt b/Zend/tests/data_class_003.phpt new file mode 100644 index 0000000000000..676e01976fddd --- /dev/null +++ b/Zend/tests/data_class_003.phpt @@ -0,0 +1,17 @@ +--TEST-- +Data class inheritance rules are enforced. +--FILE-- + +--EXPECTF-- +Fatal error: Data class Point3D cannot extend non-data class Point in %s on line %d diff --git a/Zend/tests/data_class_004.phpt b/Zend/tests/data_class_004.phpt new file mode 100644 index 0000000000000..dcfc2ce1717e6 --- /dev/null +++ b/Zend/tests/data_class_004.phpt @@ -0,0 +1,21 @@ +--TEST-- +Reflection should work +--FILE-- +isDataClass()); +var_dump($bar->isDataClass()); +?> +--EXPECT-- +bool(false) +bool(true) diff --git a/Zend/tests/data_class_005.phpt b/Zend/tests/data_class_005.phpt new file mode 100644 index 0000000000000..14d27fec3606a --- /dev/null +++ b/Zend/tests/data_class_005.phpt @@ -0,0 +1,52 @@ +--TEST-- +Copy on write +--FILE-- +x += $other->x; + $this->y += $other->y; + return $this; + } + public function __toString(): string { + return "({$this->x},{$this->y})"; + } +} + +$p1 = new Point(1, 2); +$p2 = $p1; + +// p2 is copy on write (3,2) +$p2->x = 3; +echo "p1: $p1\n"; +echo "p2 (x+3): $p2\n"; + +echo "p1->add(p2): " . $p1->add($p2) . "\n"; +echo "p1: $p1\n"; +$po = new Point(1,1); +$p2 = $po; +$p2->x++; +echo "p2: $p2\n"; +echo "po: $po\n"; + +function modify(Point $p): Point { + $p->y++; + return $p; +} + +$p1 = new Point(1,1); +$p2 = modify($p1); +echo "p1: $p1\n"; +echo "p2: $p2\n"; + +--EXPECT-- +p1: (1,2) +p2 (x+3): (3,2) +p1->add(p2): (4,4) +p1: (1,2) +p2: (2,1) +po: (1,1) +p1: (1,1) +p2: (1,2) diff --git a/Zend/tests/data_class_006.phpt b/Zend/tests/data_class_006.phpt new file mode 100644 index 0000000000000..cd9b5386f3b64 --- /dev/null +++ b/Zend/tests/data_class_006.phpt @@ -0,0 +1,42 @@ +--TEST-- +A convoluted path +--FILE-- +x = $x; + $this->l = new Length($x + $y); + echo "$previous === $this\n"; + } + + public function addNoisily(Point $other): Point { + echo "Adding $this and $other\n"; + $previous = $this; + $this->x += $other->x; + $this->y += $other->y; + $this->l = new Length($this->x + $this->y); + echo "$previous !== $this\n"; + return $this; + } + + public function __toString(): string { + return "({$this->x},{$this->y}|{$this->l->length})"; + } +} + +$p1 = new Point(1, 2); +$p2 = $p1->addNoisily($p1); + +?> +--EXPECT-- +(1,2|3) === (1,2|3) +Adding (1,2|3) and (1,2|3) +(1,2|3) !== (2,4|6) diff --git a/Zend/tests/data_class_007.phpt b/Zend/tests/data_class_007.phpt new file mode 100644 index 0000000000000..e725f93ffc202 --- /dev/null +++ b/Zend/tests/data_class_007.phpt @@ -0,0 +1,18 @@ +--TEST-- +Private properties are included in equality checks +--FILE-- + +--EXPECT-- +bool(true) +bool(true) diff --git a/Zend/tests/data_class_008.phpt b/Zend/tests/data_class_008.phpt new file mode 100644 index 0000000000000..011c13ea08c37 --- /dev/null +++ b/Zend/tests/data_class_008.phpt @@ -0,0 +1,16 @@ +--TEST-- +Data classes can be anonymous +--FILE-- + +--EXPECT-- +data object(class@anonymous)#1 (1) { + ["value":"class@anonymous":private]=> + int(1) +} diff --git a/Zend/tests/data_class_009.phpt b/Zend/tests/data_class_009.phpt new file mode 100644 index 0000000000000..9975da8027adf --- /dev/null +++ b/Zend/tests/data_class_009.phpt @@ -0,0 +1,20 @@ +--TEST-- +unset triggers copy-on-write +--FILE-- +value); +var_dump($data !== $copy); +var_dump($data); +?> +--EXPECT-- +bool(true) +data object(class@anonymous)#1 (1) { + ["value"]=> + int(1) +} diff --git a/Zend/tests/data_class_010.phpt b/Zend/tests/data_class_010.phpt new file mode 100644 index 0000000000000..6ca2f4cb139e6 --- /dev/null +++ b/Zend/tests/data_class_010.phpt @@ -0,0 +1,18 @@ +--TEST-- +Serialization works +--FILE-- + +--EXPECT-- +string(40) "O:5:"Point":2:{s:1:"x";i:1;s:1:"y";i:2;}" +bool(true) diff --git a/Zend/tests/data_class_011.phpt b/Zend/tests/data_class_011.phpt new file mode 100644 index 0000000000000..a7e11062d0383 --- /dev/null +++ b/Zend/tests/data_class_011.phpt @@ -0,0 +1,37 @@ +--TEST-- +Other features work with data classes +--FILE-- +length = sqrt($this->x ** 2 + $this->y ** 2); + } +} + +final readonly data class Point2D implements Point { + use PythagoreanTheorem; // contains implementation of $length + + public function __construct(public int $x, public int $y) { + $this->memoizeLength(); // from the trait + } + + public function add(Point $point): Point { + return new static($this->x + $point->x, $this->y + $point->y); + } +} + +$p1 = new Point2D(1, 1); +$p2 = new Point2D(2, 2); +$p3 = $p1->add($p2); +echo $p3->length, "\n"; +?> +--EXPECT-- +6 diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index d33747412ef0a..de2c67e7ad7da 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1898,6 +1898,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio if (decl->flags & ZEND_ACC_READONLY_CLASS) { smart_str_appends(str, "readonly "); } + if (decl->flags & ZEND_ACC_DATA_CLASS) { + smart_str_appends(str, "data "); + } smart_str_appends(str, "class "); } smart_str_appendl(str, ZSTR_VAL(decl->name), ZSTR_LEN(decl->name)); diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index d7bcb1f54e889..7009049780529 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -86,6 +86,11 @@ static void validate_allow_dynamic_properties( ZSTR_VAL(scope->name) ); } + if (scope->ce_flags & ZEND_ACC_DATA_CLASS) { + zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to data class %s", + ZSTR_VAL(scope->name) + ); + } if (scope->ce_flags & ZEND_ACC_ENUM) { zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to enum %s", ZSTR_VAL(scope->name) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7e5351ddfafdb..130659fe2cb1c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -862,6 +862,8 @@ static const char *zend_modifier_token_to_string(uint32_t token) return "final"; case T_READONLY: return "readonly"; + case T_DATA: + return "data"; case T_ABSTRACT: return "abstract"; case T_PUBLIC_SET: @@ -995,6 +997,10 @@ uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */ zend_throw_exception(zend_ce_compile_error, "Multiple readonly modifiers are not allowed", 0); return 0; } + if ((flags & ZEND_ACC_DATA_CLASS) && (new_flag & ZEND_ACC_DATA_CLASS)) { + zend_throw_exception(zend_ce_compile_error, "Multiple data modifiers are not allowed", 0); + return 0; + } if ((new_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flags & ZEND_ACC_FINAL)) { zend_throw_exception(zend_ce_compile_error, "Cannot use the final modifier on an abstract class", 0); @@ -1020,6 +1026,10 @@ uint32_t zend_add_anonymous_class_modifier(uint32_t flags, uint32_t new_flag) zend_throw_exception(zend_ce_compile_error, "Multiple readonly modifiers are not allowed", 0); return 0; } + if ((flags & ZEND_ACC_DATA_CLASS) && (new_flag & ZEND_ACC_DATA_CLASS)) { + zend_throw_exception(zend_ce_compile_error, "Multiple data modifiers are not allowed", 0); + return 0; + } return new_flags; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 1eaf3ef686e79..ee46f742a1168 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -267,7 +267,7 @@ typedef struct _zend_oparray_context { #define ZEND_ACC_PROTECTED_SET (1 << 11) /* | | X | */ #define ZEND_ACC_PRIVATE_SET (1 << 12) /* | | X | */ /* | | | */ -/* Class Flags (unused: 30,31) | | | */ +/* Class Flags (unused: 31) | | | */ /* =========== | | | */ /* | | | */ /* Special class types | | | */ @@ -333,6 +333,9 @@ typedef struct _zend_oparray_context { /* Class cannot be serialized or unserialized | | | */ #define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */ /* | | | */ +/* Class is compared by value | | | */ +#define ZEND_ACC_DATA_CLASS (1 << 30) /* X | | | */ +/* | | | */ /* Function Flags (unused: 29-30) | | | */ /* ============== | | | */ /* | | | */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index d85bfeb6ae2ab..91102364d8e35 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1783,6 +1783,13 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par ); } + if (UNEXPECTED((ce->ce_flags & ZEND_ACC_DATA_CLASS) != (parent_ce->ce_flags & ZEND_ACC_DATA_CLASS))) { + zend_error_noreturn(E_COMPILE_ERROR, "%s class %s cannot extend %s class %s", + ce->ce_flags & ZEND_ACC_DATA_CLASS ? "Data" : "Non-data", ZSTR_VAL(ce->name), + parent_ce->ce_flags & ZEND_ACC_DATA_CLASS ? "data" : "non-data", ZSTR_VAL(parent_ce->name) + ); + } + if (ce->parent_name) { zend_string_release_ex(ce->parent_name, 0); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index d2a29e670d8bf..9c1adc4186e7f 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -158,6 +158,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_PROTECTED_SET "'protected(set)'" %token T_PUBLIC_SET "'public(set)'" %token T_READONLY "'readonly'" +%token T_DATA "'data'" %token T_VAR "'var'" %token T_UNSET "'unset'" %token T_ISSET "'isset'" @@ -314,7 +315,7 @@ reserved_non_modifiers: semi_reserved: reserved_non_modifiers - | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY + | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY | T_DATA ; ampersand: @@ -624,6 +625,7 @@ class_modifier: T_ABSTRACT { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } | T_FINAL { $$ = ZEND_ACC_FINAL; } | T_READONLY { $$ = ZEND_ACC_READONLY_CLASS|ZEND_ACC_NO_DYNAMIC_PROPERTIES; } + | T_DATA { $$ = ZEND_ACC_DATA_CLASS; } ; trait_declaration_statement: diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 7ae73875926eb..6bb8ea887a189 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1745,6 +1745,10 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_IDENT(T_READONLY); } +"data" { + RETURN_TOKEN_WITH_IDENT(T_DATA); +} + "unset" { RETURN_TOKEN_WITH_IDENT(T_UNSET); } diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index c2fbc0ee110c9..d20bb803240ac 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2409,6 +2409,10 @@ ZEND_API bool ZEND_FASTCALL zend_is_identical(const zval *op1, const zval *op2) return (Z_ARRVAL_P(op1) == Z_ARRVAL_P(op2) || zend_hash_compare(Z_ARRVAL_P(op1), Z_ARRVAL_P(op2), (compare_func_t) hash_zval_identical_function, 1) == 0); case IS_OBJECT: + // check the class entry for a ACC_DATA_CLASS flag + if (Z_OBJ_P(op1)->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + return !Z_OBJ_HANDLER_P(op1, compare)((zval *)op1, (zval *)op2); + } return (Z_OBJ_P(op1) == Z_OBJ_P(op2)); default: return 0; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7e471b5acd8b6..67db052e6493e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1041,6 +1041,20 @@ ZEND_VM_HANDLER(28, ZEND_ASSIGN_OBJ_OP, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, OP) ZEND_VM_C_LABEL(assign_op_object): /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (OP2_TYPE == IS_CONST) { name = Z_STR_P(property); } else { @@ -1310,6 +1324,20 @@ ZEND_VM_HANDLER(132, ZEND_PRE_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACH ZEND_VM_C_LABEL(pre_incdec_object): /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (OP2_TYPE == IS_CONST) { name = Z_STR_P(property); } else { @@ -1380,6 +1408,20 @@ ZEND_VM_HANDLER(134, ZEND_POST_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CAC ZEND_VM_C_LABEL(post_incdec_object): /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (OP2_TYPE == IS_CONST) { name = Z_STR_P(property); } else { @@ -2560,6 +2602,19 @@ ZEND_VM_C_LABEL(fast_assign_obj): ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (OP2_TYPE != IS_CONST) { @@ -6808,7 +6863,20 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_S break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (OP2_TYPE != IS_CONST) { zend_tmp_string_release(tmp_name); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9209399a5cdbf..7848323925781 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -23548,6 +23548,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CONST_H assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -23768,6 +23782,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CONST_HAN pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -23832,6 +23860,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CONST_HA post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -24151,6 +24193,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -24305,6 +24360,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -24459,6 +24527,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -24613,6 +24694,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -26170,7 +26264,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CONST_HANDL break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CONST != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -26534,6 +26641,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_TMPVAR_ assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -26756,6 +26877,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HA pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -26821,6 +26956,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_H post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -27142,6 +27291,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -27296,6 +27458,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -27450,6 +27625,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -27604,6 +27792,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -28686,7 +28887,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HAND break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -30892,6 +31106,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CV_HAND assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -31112,6 +31340,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CV_HANDLE pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -31176,6 +31418,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CV_HANDL post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -31495,6 +31751,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -31649,6 +31918,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -31803,6 +32085,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -31957,6 +32252,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -33134,7 +33442,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CV_HANDLER( break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CV != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -33561,6 +33882,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CONS assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -33651,6 +33986,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CONST_ pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -33715,6 +34064,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CONST post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -34242,6 +34605,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -34396,6 +34772,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -34550,6 +34939,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -34704,6 +35106,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -35391,7 +35806,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HA break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CONST != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -35732,6 +36160,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_TMPV assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -35822,6 +36264,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -35887,6 +36343,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVA post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -36410,6 +36880,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -36564,6 +37047,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -36718,6 +37214,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -36872,6 +37381,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -37387,7 +37909,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_H break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -38380,6 +38915,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV_H assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -38470,6 +39019,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CV_HAN pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -38534,6 +39097,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CV_HA post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -39056,6 +39633,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -39210,6 +39800,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -39364,6 +39967,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -39518,6 +40134,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -40032,7 +40661,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDL break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CV != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -42533,6 +43175,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CONST_HA assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -42753,6 +43409,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CONST_HAND pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -42817,6 +43487,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CONST_HAN post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -43456,6 +44140,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -43610,6 +44307,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -43764,6 +44474,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -43918,6 +44641,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -45343,7 +46079,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CONST_HANDLE break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CONST != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -46486,6 +47235,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_TMPVAR_H assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -46708,6 +47471,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HAN pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -46773,6 +47550,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HA post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -47408,6 +48199,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -47562,6 +48366,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -47716,6 +48533,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -47870,6 +48700,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -49097,7 +49940,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDL break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -51989,6 +52845,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CV_HANDL assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -52209,6 +53079,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CV_HANDLER pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -52273,6 +53157,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CV_HANDLE post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -52907,6 +53805,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -53061,6 +53972,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -53215,6 +54139,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -53369,6 +54306,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -54691,7 +55641,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER(Z break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CV != IS_CONST) { zend_tmp_string_release(tmp_name); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ecfa20fe0c0c8..f8514f3d4ab42 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -354,6 +354,9 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const if (ce->ce_flags & ZEND_ACC_READONLY_CLASS) { smart_str_append_printf(str, "readonly "); } + if (ce->ce_flags & ZEND_ACC_DATA_CLASS) { + smart_str_append_printf(str, "data "); + } smart_str_append_printf(str, "class "); } smart_str_append_printf(str, "%s", ZSTR_VAL(ce->name)); @@ -1622,6 +1625,10 @@ ZEND_METHOD(Reflection, getModifierNames) if (modifiers & (ZEND_ACC_READONLY | ZEND_ACC_READONLY_CLASS)) { add_next_index_stringl(return_value, "readonly", sizeof("readonly")-1); } + + if (modifiers & ZEND_ACC_DATA_CLASS) { + add_next_index_stringl(return_value, "data", sizeof("data")-1); + } } /* }}} */ @@ -4877,6 +4884,11 @@ ZEND_METHOD(ReflectionClass, isReadOnly) _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_READONLY_CLASS); } +ZEND_METHOD(ReflectionClass, isDataClass) +{ + _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_DATA_CLASS); +} + /* {{{ Returns whether this class is abstract */ ZEND_METHOD(ReflectionClass, isAbstract) { @@ -4889,7 +4901,7 @@ ZEND_METHOD(ReflectionClass, getModifiers) { reflection_object *intern; zend_class_entry *ce; - uint32_t keep_flags = ZEND_ACC_FINAL | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_READONLY_CLASS; + uint32_t keep_flags = ZEND_ACC_FINAL | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_READONLY_CLASS | ZEND_ACC_DATA_CLASS; ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ce); diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index be511d7ee14cd..6397d76d58428 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -355,6 +355,8 @@ public function isFinal(): bool {} public function isReadOnly(): bool {} + public function isDataClass(): bool {} + /** @tentative-return-type */ public function getModifiers(): int {} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index d78a685dde9c9..f1d09f285cddb 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3c6be99bb36965139464925a618cb0bf03affa62 */ + * Stub hash: 4877c4510cf80bbfe4d39fd671b84e0663b65773 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -272,6 +272,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClass_isReadOnly arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionClass_isDataClass arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + #define arginfo_class_ReflectionClass_getModifiers arginfo_class_ReflectionFunctionAbstract_getNumberOfParameters ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionClass_isInstance, 0, 1, _IS_BOOL, 0) @@ -820,6 +822,7 @@ ZEND_METHOD(ReflectionClass, isEnum); ZEND_METHOD(ReflectionClass, isAbstract); ZEND_METHOD(ReflectionClass, isFinal); ZEND_METHOD(ReflectionClass, isReadOnly); +ZEND_METHOD(ReflectionClass, isDataClass); ZEND_METHOD(ReflectionClass, getModifiers); ZEND_METHOD(ReflectionClass, isInstance); ZEND_METHOD(ReflectionClass, newInstance); @@ -1111,6 +1114,7 @@ static const zend_function_entry class_ReflectionClass_methods[] = { ZEND_ME(ReflectionClass, isAbstract, arginfo_class_ReflectionClass_isAbstract, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, isFinal, arginfo_class_ReflectionClass_isFinal, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, isReadOnly, arginfo_class_ReflectionClass_isReadOnly, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, isDataClass, arginfo_class_ReflectionClass_isDataClass, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getModifiers, arginfo_class_ReflectionClass_getModifiers, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, isInstance, arginfo_class_ReflectionClass_isInstance, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, newInstance, arginfo_class_ReflectionClass_newInstance, ZEND_ACC_PUBLIC) diff --git a/ext/standard/var.c b/ext/standard/var.c index 1c2b0eb164a1c..14b8c405326a6 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -89,6 +89,10 @@ static void php_object_property_dump(zend_property_info *prop_info, zval *zv, ze /* }}} */ static const char *php_var_dump_object_prefix(zend_object *obj) { + if (obj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + return "data "; + } + if (EXPECTED(!zend_object_is_lazy(obj))) { return ""; } diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index a046ab50e1498..7aa58ee1d4ada 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -96,6 +96,7 @@ char *get_token_type_name(int token_type) case T_PROTECTED_SET: return "T_PROTECTED_SET"; case T_PUBLIC_SET: return "T_PUBLIC_SET"; case T_READONLY: return "T_READONLY"; + case T_DATA: return "T_DATA"; case T_VAR: return "T_VAR"; case T_UNSET: return "T_UNSET"; case T_ISSET: return "T_ISSET"; diff --git a/ext/tokenizer/tokenizer_data.stub.php b/ext/tokenizer/tokenizer_data.stub.php index 45f3c89f2de3a..fa9adfbc69ded 100644 --- a/ext/tokenizer/tokenizer_data.stub.php +++ b/ext/tokenizer/tokenizer_data.stub.php @@ -357,6 +357,11 @@ * @cvalue T_READONLY */ const T_READONLY = UNKNOWN; +/** + * @var int + * @cvalue T_DATA + */ +const T_DATA = UNKNOWN; /** * @var int * @cvalue T_VAR diff --git a/ext/tokenizer/tokenizer_data_arginfo.h b/ext/tokenizer/tokenizer_data_arginfo.h index 61f6ac1ec3659..8f5298f4493f9 100644 --- a/ext/tokenizer/tokenizer_data_arginfo.h +++ b/ext/tokenizer/tokenizer_data_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d917cab61a2b436a16d2227cdb438add45e42d69 */ + * Stub hash: dc57c500646c3ce4426e8a5821297d24381e11cf */ static void register_tokenizer_data_symbols(int module_number) { @@ -74,6 +74,7 @@ static void register_tokenizer_data_symbols(int module_number) REGISTER_LONG_CONSTANT("T_PROTECTED_SET", T_PROTECTED_SET, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_PUBLIC_SET", T_PUBLIC_SET, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_READONLY", T_READONLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_DATA", T_DATA, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_VAR", T_VAR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_UNSET", T_UNSET, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_ISSET", T_ISSET, CONST_PERSISTENT);