diff --git a/Zend/tests/type_declarations/typed_properties_magic_set.phpt b/Zend/tests/type_declarations/typed_properties_magic_set.phpt new file mode 100644 index 0000000000000..a55448441784a --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_magic_set.phpt @@ -0,0 +1,31 @@ +--TEST-- +__set() should not be invoked when setting an uninitialized typed property +--FILE-- +foo = 42; +var_dump($test->foo); + +// __set will be called after unset() +unset($test->foo); +$test->foo = 42; + +// __set will be called after unset() without prior initialization +$test = new Test; +unset($test->foo); +$test->foo = 42; + +?> +--EXPECT-- +int(42) +__set foo = 42 +__set foo = 42 diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ce37710dd09e9..2dfe812dbbeb5 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6135,7 +6135,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) / } else if (!ZEND_TYPE_IS_SET(type)) { ZVAL_NULL(&value_zv); } else { - ZVAL_UNDEF(&value_zv); + Z_TYPE_INFO_P(&value_zv) = IS_UNINIT_PROP_EX; } zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type); diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 09984390c9024..7761781fe36d5 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -836,6 +836,10 @@ ZEND_API zval *zend_std_write_property(zval *object, zval *member, zval *value, zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR, EG(current_execute_data) && ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))); goto exit; } + if (Z_TYPE_INFO_P(variable_ptr) == IS_UNINIT_PROP_EX) { + /* Writes to uninitialized typed properties bypass __set() */ + goto write_std_property; + } } else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) { if (EXPECTED(zobj->properties != NULL)) { if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { @@ -1113,6 +1117,10 @@ ZEND_API void zend_std_unset_property(zval *object, zval *member, void **cache_s } goto exit; } + if (Z_TYPE_INFO_P(slot) == IS_UNINIT_PROP_EX) { + /* Mark property as fully unset, so that __set() will be called from this point on. */ + ZVAL_UNDEF(slot); + } } else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset)) && EXPECTED(zobj->properties != NULL)) { if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 83877e0d5d4a9..1c9cecffc8953 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -556,8 +556,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { /* zval.u1.v.type_flags */ #define IS_TYPE_REFCOUNTED (1<<0) #define IS_TYPE_COLLECTABLE (1<<1) +#define IS_TYPE_UNINIT_PROP (1<<2) -#if 1 +#if 0 /* This optimized version assumes that we have a single "type_flag" */ /* IS_TYPE_COLLECTABLE may be used only with IS_TYPE_REFCOUNTED */ # define Z_TYPE_INFO_REFCOUNTED(t) (((t) & Z_TYPE_FLAGS_MASK) != 0) @@ -576,6 +577,8 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define IS_CONSTANT_AST_EX (IS_CONSTANT_AST | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) +#define IS_UNINIT_PROP_EX (IS_UNDEF | (IS_TYPE_UNINIT_PROP << Z_TYPE_FLAGS_SHIFT)) + /* string flags (zval.value->gc.u.flags) */ #define IS_STR_INTERNED GC_IMMUTABLE /* interned string */ #define IS_STR_PERSISTENT GC_PERSISTENT /* allocated using malloc */ @@ -624,7 +627,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_CONSTANT(zval) (Z_TYPE(zval) == IS_CONSTANT_AST) #define Z_CONSTANT_P(zval_p) Z_CONSTANT(*(zval_p)) -#if 1 +#if 0 /* This optimized version assumes that we have a single "type_flag" */ /* IS_TYPE_COLLECTABLE may be used only with IS_TYPE_REFCOUNTED */ #define Z_REFCOUNTED(zval) (Z_TYPE_FLAGS(zval) != 0) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 287466df4ba64..09108a92ad2df 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6448,11 +6448,11 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit): } value = &p->val; value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { if (UNEXPECTED(value_type == IS_INDIRECT)) { value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { break; } } else { @@ -6488,11 +6488,11 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit): value = &p->val; value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { if (UNEXPECTED(value_type == IS_INDIRECT)) { value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF) + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF) && EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key, 0) == SUCCESS)) { break; } @@ -6601,11 +6601,11 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) } value = &p->val; value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { if (UNEXPECTED(value_type == IS_INDIRECT)) { value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { break; } } else { @@ -6640,11 +6640,11 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) value = &p->val; value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { if (UNEXPECTED(value_type == IS_INDIRECT)) { value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF) + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF) && EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key, 0) == SUCCESS)) { if ((value_type & Z_TYPE_MASK) != IS_REFERENCE) { zend_property_info *prop_info = diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index bb01bb1ee3339..b9d2574445e98 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -21393,11 +21393,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE } value = &p->val; value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { if (UNEXPECTED(value_type == IS_INDIRECT)) { value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { break; } } else { @@ -21433,11 +21433,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE value = &p->val; value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { if (UNEXPECTED(value_type == IS_INDIRECT)) { value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF) + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF) && EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key, 0) == SUCCESS)) { break; } @@ -21546,11 +21546,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z } value = &p->val; value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { if (UNEXPECTED(value_type == IS_INDIRECT)) { value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { break; } } else { @@ -21585,11 +21585,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z value = &p->val; value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF)) { if (UNEXPECTED(value_type == IS_INDIRECT)) { value = Z_INDIRECT_P(value); value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF) + if (EXPECTED((value_type & Z_TYPE_MASK) != IS_UNDEF) && EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key, 0) == SUCCESS)) { if ((value_type & Z_TYPE_MASK) != IS_REFERENCE) { zend_property_info *prop_info =