Skip to content

Commit e63a44d

Browse files
committed
Merge branch 'PHP-7.4'
* PHP-7.4: Fix bug #78226: Don't call __set() on uninitialized typed properties
2 parents 7d056fc + f1848a4 commit e63a44d

File tree

7 files changed

+86
-8
lines changed

7 files changed

+86
-8
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
--TEST--
2+
__set() should not be invoked when setting an uninitialized typed property
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public int $foo;
8+
public function __set($name, $value) {
9+
echo "__set ", $name, " = ", $value, "\n";
10+
}
11+
}
12+
13+
$test = new Test;
14+
$test->foo = 42;
15+
var_dump($test->foo);
16+
17+
// __set will be called after unset()
18+
unset($test->foo);
19+
$test->foo = 42;
20+
21+
// __set will be called after unset() without prior initialization
22+
$test = new Test;
23+
unset($test->foo);
24+
$test->foo = 42;
25+
26+
class Test2 extends Test {
27+
}
28+
29+
// Check that inherited properties work correctly
30+
$test = new Test;
31+
$test->foo = 42;
32+
var_dump($test->foo);
33+
unset($test->foo);
34+
$test->foo = 42;
35+
36+
// Test that cloning works correctly
37+
$test = clone $test;
38+
$test->foo = 42;
39+
$test = clone new Test;
40+
$test->foo = 42;
41+
var_dump($test->foo);
42+
unset($test->foo);
43+
$test->foo = 42;
44+
45+
?>
46+
--EXPECT--
47+
int(42)
48+
__set foo = 42
49+
__set foo = 42
50+
int(42)
51+
__set foo = 42
52+
__set foo = 42
53+
int(42)
54+
__set foo = 42

Zend/zend_API.c

+6-3
Original file line numberDiff line numberDiff line change
@@ -1119,13 +1119,13 @@ static zend_always_inline void _object_properties_init(zend_object *object, zend
11191119

11201120
if (UNEXPECTED(class_type->type == ZEND_INTERNAL_CLASS)) {
11211121
do {
1122-
ZVAL_COPY_OR_DUP(dst, src);
1122+
ZVAL_COPY_OR_DUP_PROP(dst, src);
11231123
src++;
11241124
dst++;
11251125
} while (src != end);
11261126
} else {
11271127
do {
1128-
ZVAL_COPY(dst, src);
1128+
ZVAL_COPY_PROP(dst, src);
11291129
src++;
11301130
dst++;
11311131
} while (src != end);
@@ -3539,6 +3539,7 @@ ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name
35393539
}
35403540
}
35413541
} else {
3542+
zval *property_default_ptr;
35423543
if ((property_info_ptr = zend_hash_find_ptr(&ce->properties_info, name)) != NULL &&
35433544
(property_info_ptr->flags & ZEND_ACC_STATIC) == 0) {
35443545
property_info->offset = property_info_ptr->offset;
@@ -3559,7 +3560,9 @@ ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name
35593560
ce->properties_info_table[ce->default_properties_count - 1] = property_info;
35603561
}
35613562
}
3562-
ZVAL_COPY_VALUE(&ce->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)], property);
3563+
property_default_ptr = &ce->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)];
3564+
ZVAL_COPY_VALUE(property_default_ptr, property);
3565+
Z_PROP_FLAG_P(property_default_ptr) = Z_ISUNDEF_P(property) ? IS_PROP_UNINIT : 0;
35633566
}
35643567
if (ce->type & ZEND_INTERNAL_CLASS) {
35653568
switch(Z_TYPE_P(property)) {

Zend/zend_inheritance.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
11131113
do {
11141114
dst--;
11151115
src--;
1116-
ZVAL_COPY_VALUE(dst, src);
1116+
ZVAL_COPY_VALUE_PROP(dst, src);
11171117
} while (dst != end);
11181118
pefree(src, ce->type == ZEND_INTERNAL_CLASS);
11191119
end = ce->default_properties_table;
@@ -1128,7 +1128,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
11281128
do {
11291129
dst--;
11301130
src--;
1131-
ZVAL_COPY_OR_DUP(dst, src);
1131+
ZVAL_COPY_OR_DUP_PROP(dst, src);
11321132
if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) {
11331133
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
11341134
}
@@ -1138,7 +1138,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
11381138
do {
11391139
dst--;
11401140
src--;
1141-
ZVAL_COPY(dst, src);
1141+
ZVAL_COPY_PROP(dst, src);
11421142
if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) {
11431143
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
11441144
}

Zend/zend_object_handlers.c

+7
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,11 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
817817
zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR, EG(current_execute_data) && ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)));
818818
goto exit;
819819
}
820+
if (Z_PROP_FLAG_P(variable_ptr) == IS_PROP_UNINIT) {
821+
/* Writes to uninitializde typed properties bypass __set(). */
822+
Z_PROP_FLAG_P(variable_ptr) = 0;
823+
goto write_std_property;
824+
}
820825
} else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
821826
if (EXPECTED(zobj->properties != NULL)) {
822827
if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
@@ -1072,6 +1077,8 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void
10721077
}
10731078
return;
10741079
}
1080+
/* Reset the IS_PROP_UNINIT flag, if it exists. */
1081+
Z_PROP_FLAG_P(slot) = 0;
10751082
} else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))
10761083
&& EXPECTED(zobj->properties != NULL)) {
10771084
if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {

Zend/zend_objects.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
209209

210210
do {
211211
i_zval_ptr_dtor(dst);
212-
ZVAL_COPY_VALUE(dst, src);
212+
ZVAL_COPY_VALUE_PROP(dst, src);
213213
zval_add_ref(dst);
214214
if (UNEXPECTED(Z_ISREF_P(dst)) &&
215215
(ZEND_DEBUG || ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(dst)))) {

Zend/zend_types.h

+14
Original file line numberDiff line numberDiff line change
@@ -1277,4 +1277,18 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) {
12771277
} \
12781278
} while (0)
12791279

1280+
/* Properties store a flag distinguishing unset and unintialized properties
1281+
* (both use IS_UNDEF type) in the Z_EXTRA space. As such we also need to copy
1282+
* the Z_EXTRA space when copying property default values etc. We define separate
1283+
* macros for this purpose, so this workaround is easier to remove in the future. */
1284+
#define IS_PROP_UNINIT 1
1285+
#define Z_PROP_FLAG_P(z) Z_EXTRA_P(z)
1286+
#define ZVAL_COPY_VALUE_PROP(z, v) \
1287+
do { *(z) = *(v); } while (0)
1288+
#define ZVAL_COPY_PROP(z, v) \
1289+
do { ZVAL_COPY(z, v); Z_PROP_FLAG_P(z) = Z_PROP_FLAG_P(v); } while (0)
1290+
#define ZVAL_COPY_OR_DUP_PROP(z, v) \
1291+
do { ZVAL_COPY_OR_DUP(z, v); Z_PROP_FLAG_P(z) = Z_PROP_FLAG_P(v); } while (0)
1292+
1293+
12801294
#endif /* ZEND_TYPES_H */

ext/opcache/zend_accelerator_util_funcs.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ static void zend_class_copy_ctor(zend_class_entry **pce)
270270
end = src + ce->default_properties_count;
271271
ce->default_properties_table = dst;
272272
for (; src != end; src++, dst++) {
273-
ZVAL_COPY_VALUE(dst, src);
273+
ZVAL_COPY_VALUE_PROP(dst, src);
274274
}
275275
}
276276

0 commit comments

Comments
 (0)