Skip to content

Commit 3eb97a4

Browse files
committed
Always use separate static_members_table
When running without opcache, static_members_table is shared with default_static_members_table. This is visible in reflection output, because ReflectionProperty::getDefaultValue() will return the current value, rather than the default value. Address this by never sharing the table, which matches the behavior we already see under opcache. Fixes bug #80821. Closes GH-7299.
1 parent 87c181a commit 3eb97a4

File tree

9 files changed

+82
-134
lines changed

9 files changed

+82
-134
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ PHP NEWS
77
. Fixed Bug #80959 (infinite loop in building cfg during JIT compilation)
88
(Nikita, Dmitry)
99

10+
- Reflection:
11+
. Fixed bug #80821 (ReflectionProperty::getDefaultValue() returns current
12+
value for statics). (Nikita)
13+
1014
22 Jul 2021, PHP 8.1.0beta1
1115

1216
- Core:

Zend/zend_API.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,10 +1422,8 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
14221422
if (class_type->default_static_members_count) {
14231423
static_members_table = CE_STATIC_MEMBERS(class_type);
14241424
if (!static_members_table) {
1425-
if (class_type->type == ZEND_INTERNAL_CLASS || (ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED))) {
1426-
zend_class_init_statics(class_type);
1427-
static_members_table = CE_STATIC_MEMBERS(class_type);
1428-
}
1425+
zend_class_init_statics(class_type);
1426+
static_members_table = CE_STATIC_MEMBERS(class_type);
14291427
}
14301428
}
14311429

@@ -4087,12 +4085,13 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z
40874085
}
40884086
ZVAL_COPY_VALUE(&ce->default_static_members_table[property_info->offset], property);
40894087
if (!ZEND_MAP_PTR(ce->static_members_table)) {
4090-
ZEND_ASSERT(ce->type == ZEND_INTERNAL_CLASS);
4091-
if (!EG(current_execute_data)) {
4088+
if (ce->type == ZEND_INTERNAL_CLASS &&
4089+
ce->info.internal.module->type == MODULE_PERSISTENT) {
40924090
ZEND_MAP_PTR_NEW(ce->static_members_table);
40934091
} else {
4094-
/* internal class loaded by dl() */
4095-
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
4092+
ZEND_MAP_PTR_INIT(ce->static_members_table,
4093+
zend_arena_alloc(&CG(arena), sizeof(zval **)));
4094+
ZEND_MAP_PTR_SET(ce->static_members_table, NULL);
40964095
}
40974096
}
40984097
} else {

Zend/zend_compile.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1873,12 +1873,10 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_hand
18731873
zend_hash_init(&ce->constants_table, 8, NULL, NULL, persistent_hashes);
18741874
zend_hash_init(&ce->function_table, 8, NULL, ZEND_FUNCTION_DTOR, persistent_hashes);
18751875

1876-
if (ce->type == ZEND_INTERNAL_CLASS) {
1877-
ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
1878-
} else {
1879-
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
1876+
if (ce->type == ZEND_USER_CLASS) {
18801877
ce->info.user.doc_comment = NULL;
18811878
}
1879+
ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
18821880
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
18831881

18841882
ce->default_properties_count = 0;

Zend/zend_inheritance.c

Lines changed: 19 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,63 +1517,29 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
15171517
dst = end + parent_ce->default_static_members_count;
15181518
ce->default_static_members_table = end;
15191519
}
1520-
if (UNEXPECTED(parent_ce->type != ce->type)) {
1521-
/* User class extends internal */
1522-
if (CE_STATIC_MEMBERS(parent_ce) == NULL) {
1523-
zend_class_init_statics(parent_ce);
1524-
}
1525-
if (UNEXPECTED(zend_update_class_constants(parent_ce) != SUCCESS)) {
1526-
ZEND_UNREACHABLE();
1520+
src = parent_ce->default_static_members_table + parent_ce->default_static_members_count;
1521+
do {
1522+
dst--;
1523+
src--;
1524+
if (Z_TYPE_P(src) == IS_INDIRECT) {
1525+
ZVAL_INDIRECT(dst, Z_INDIRECT_P(src));
1526+
} else {
1527+
ZVAL_INDIRECT(dst, src);
15271528
}
1528-
src = CE_STATIC_MEMBERS(parent_ce) + parent_ce->default_static_members_count;
1529-
do {
1530-
dst--;
1531-
src--;
1532-
if (Z_TYPE_P(src) == IS_INDIRECT) {
1533-
ZVAL_INDIRECT(dst, Z_INDIRECT_P(src));
1534-
} else {
1535-
ZVAL_INDIRECT(dst, src);
1536-
}
1537-
} while (dst != end);
1538-
} else if (ce->type == ZEND_USER_CLASS) {
1539-
if (CE_STATIC_MEMBERS(parent_ce) == NULL) {
1540-
ZEND_ASSERT(parent_ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED));
1541-
zend_class_init_statics(parent_ce);
1529+
if (Z_TYPE_P(Z_INDIRECT_P(dst)) == IS_CONSTANT_AST) {
1530+
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
1531+
ce->ce_flags |= ZEND_ACC_HAS_AST_STATICS;
15421532
}
1543-
src = CE_STATIC_MEMBERS(parent_ce) + parent_ce->default_static_members_count;
1544-
do {
1545-
dst--;
1546-
src--;
1547-
if (Z_TYPE_P(src) == IS_INDIRECT) {
1548-
ZVAL_INDIRECT(dst, Z_INDIRECT_P(src));
1549-
} else {
1550-
ZVAL_INDIRECT(dst, src);
1551-
}
1552-
if (Z_TYPE_P(Z_INDIRECT_P(dst)) == IS_CONSTANT_AST) {
1553-
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
1554-
ce->ce_flags |= ZEND_ACC_HAS_AST_STATICS;
1555-
}
1556-
} while (dst != end);
1557-
} else {
1558-
src = parent_ce->default_static_members_table + parent_ce->default_static_members_count;
1559-
do {
1560-
dst--;
1561-
src--;
1562-
if (Z_TYPE_P(src) == IS_INDIRECT) {
1563-
ZVAL_INDIRECT(dst, Z_INDIRECT_P(src));
1564-
} else {
1565-
ZVAL_INDIRECT(dst, src);
1566-
}
1567-
} while (dst != end);
1568-
}
1533+
} while (dst != end);
15691534
ce->default_static_members_count += parent_ce->default_static_members_count;
15701535
if (!ZEND_MAP_PTR(ce->static_members_table)) {
1571-
ZEND_ASSERT(ce->type == ZEND_INTERNAL_CLASS);
1572-
if (!EG(current_execute_data)) {
1536+
if (ce->type == ZEND_INTERNAL_CLASS &&
1537+
ce->info.internal.module->type == MODULE_PERSISTENT) {
15731538
ZEND_MAP_PTR_NEW(ce->static_members_table);
15741539
} else {
1575-
/* internal class loaded by dl() */
1576-
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
1540+
ZEND_MAP_PTR_INIT(ce->static_members_table,
1541+
zend_arena_alloc(&CG(arena), sizeof(zval *)));
1542+
ZEND_MAP_PTR_SET(ce->static_members_table, NULL);
15771543
}
15781544
}
15791545
}
@@ -2715,7 +2681,8 @@ static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
27152681
ZVAL_COPY_VALUE(dst, src);
27162682
}
27172683
}
2718-
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
2684+
ZEND_MAP_PTR_INIT(ce->static_members_table, zend_arena_alloc(&CG(arena), sizeof(zval *)));
2685+
ZEND_MAP_PTR_SET(ce->static_members_table, NULL);
27192686

27202687
/* properties_info */
27212688
if (!(HT_FLAGS(&ce->properties_info) & HASH_FLAG_UNINITIALIZED)) {

Zend/zend_object_handlers.c

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,7 +1480,11 @@ ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend
14801480
}
14811481

14821482
if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0)) {
1483-
goto undeclared_property;
1483+
undeclared_property:
1484+
if (type != BP_VAR_IS) {
1485+
zend_throw_error(NULL, "Access to undeclared static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
1486+
}
1487+
return NULL;
14841488
}
14851489

14861490
if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) {
@@ -1489,17 +1493,9 @@ ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend
14891493
}
14901494
}
14911495

1492-
/* check if static properties were destroyed */
1496+
/* Ensure static properties are initialized. */
14931497
if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
1494-
if (ce->type == ZEND_INTERNAL_CLASS || (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED))) {
1495-
zend_class_init_statics(ce);
1496-
} else {
1497-
undeclared_property:
1498-
if (type != BP_VAR_IS) {
1499-
zend_throw_error(NULL, "Access to undeclared static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
1500-
}
1501-
return NULL;
1502-
}
1498+
zend_class_init_statics(ce);
15031499
}
15041500

15051501
ret = CE_STATIC_MEMBERS(ce) + property_info->offset;

Zend/zend_opcode.c

Lines changed: 18 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -170,42 +170,21 @@ ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce)
170170
zval *static_members = CE_STATIC_MEMBERS(ce);
171171
zval *p = static_members;
172172
zval *end = p + ce->default_static_members_count;
173-
if (UNEXPECTED(ZEND_MAP_PTR(ce->static_members_table) == &ce->default_static_members_table)) {
174-
/* Special case: If this is a static property on a dl'ed internal class, then the
175-
* static property table and the default property table are the same. In this case we
176-
* destroy the values here, but leave behind valid UNDEF zvals and don't free the
177-
* table itself. */
178-
while (p != end) {
179-
if (UNEXPECTED(Z_ISREF_P(p))) {
180-
zend_property_info *prop_info;
181-
ZEND_REF_FOREACH_TYPE_SOURCES(Z_REF_P(p), prop_info) {
182-
if (prop_info->ce == ce && p - static_members == prop_info->offset) {
183-
ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
184-
break; /* stop iteration here, the array might be realloc()'ed */
185-
}
186-
} ZEND_REF_FOREACH_TYPE_SOURCES_END();
187-
}
188-
i_zval_ptr_dtor(p);
189-
ZVAL_UNDEF(p);
190-
p++;
191-
}
192-
} else {
193-
ZEND_MAP_PTR_SET(ce->static_members_table, NULL);
194-
while (p != end) {
195-
if (UNEXPECTED(Z_ISREF_P(p))) {
196-
zend_property_info *prop_info;
197-
ZEND_REF_FOREACH_TYPE_SOURCES(Z_REF_P(p), prop_info) {
198-
if (prop_info->ce == ce && p - static_members == prop_info->offset) {
199-
ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
200-
break; /* stop iteration here, the array might be realloc()'ed */
201-
}
202-
} ZEND_REF_FOREACH_TYPE_SOURCES_END();
203-
}
204-
i_zval_ptr_dtor(p);
205-
p++;
173+
ZEND_MAP_PTR_SET(ce->static_members_table, NULL);
174+
while (p != end) {
175+
if (UNEXPECTED(Z_ISREF_P(p))) {
176+
zend_property_info *prop_info;
177+
ZEND_REF_FOREACH_TYPE_SOURCES(Z_REF_P(p), prop_info) {
178+
if (prop_info->ce == ce && p - static_members == prop_info->offset) {
179+
ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
180+
break; /* stop iteration here, the array might be realloc()'ed */
181+
}
182+
} ZEND_REF_FOREACH_TYPE_SOURCES_END();
206183
}
207-
efree(static_members);
184+
i_zval_ptr_dtor(p);
185+
p++;
208186
}
187+
efree(static_members);
209188
}
210189
}
211190

@@ -298,13 +277,13 @@ ZEND_API void destroy_zend_class(zval *zv)
298277
zend_class_entry *ce = Z_PTR_P(zv);
299278
zend_function *fn;
300279

280+
if (ce->default_static_members_count) {
281+
zend_cleanup_internal_class_data(ce);
282+
}
283+
301284
if (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED|ZEND_ACC_FILE_CACHED)) {
302285
zend_op_array *op_array;
303286

304-
if (ce->default_static_members_count) {
305-
zend_cleanup_internal_class_data(ce);
306-
}
307-
308287
if (!(ce->ce_flags & ZEND_ACC_FILE_CACHED)) {
309288
if (ZEND_MAP_PTR(ce->mutable_data) && ZEND_MAP_PTR_GET_IMM(ce->mutable_data)) {
310289
zend_cleanup_mutable_class_data(ce);
@@ -390,15 +369,7 @@ ZEND_API void destroy_zend_class(zval *zv)
390369
zval *end = p + ce->default_static_members_count;
391370

392371
while (p != end) {
393-
if (UNEXPECTED(Z_ISREF_P(p))) {
394-
zend_property_info *prop_info;
395-
ZEND_REF_FOREACH_TYPE_SOURCES(Z_REF_P(p), prop_info) {
396-
if (prop_info->ce == ce && p - ce->default_static_members_table == prop_info->offset) {
397-
ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
398-
break; /* stop iteration here, the array might be realloc()'ed */
399-
}
400-
} ZEND_REF_FOREACH_TYPE_SOURCES_END();
401-
}
372+
ZEND_ASSERT(!Z_ISREF_P(p));
402373
i_zval_ptr_dtor(p);
403374
p++;
404375
}
@@ -458,9 +429,6 @@ ZEND_API void destroy_zend_class(zval *zv)
458429
p++;
459430
}
460431
free(ce->default_static_members_table);
461-
if (ZEND_MAP_PTR(ce->static_members_table) != &ce->default_static_members_table) {
462-
zend_cleanup_internal_class_data(ce);
463-
}
464432
}
465433

466434
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {

ext/opcache/zend_file_cache.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,17 +1653,19 @@ static void zend_file_cache_unserialize_class(zval *zv,
16531653
if (!(script->corrupted)) {
16541654
ce->ce_flags |= ZEND_ACC_IMMUTABLE;
16551655
ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
1656-
if (ce->ce_flags & ZEND_ACC_IMMUTABLE && ce->default_static_members_table) {
1656+
ZEND_MAP_PTR_NEW(ce->mutable_data);
1657+
if (ce->default_static_members_count) {
16571658
ZEND_MAP_PTR_NEW(ce->static_members_table);
1658-
} else {
1659-
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
16601659
}
1661-
ZEND_MAP_PTR_NEW(ce->mutable_data);
16621660
} else {
16631661
ce->ce_flags &= ~ZEND_ACC_IMMUTABLE;
16641662
ce->ce_flags |= ZEND_ACC_FILE_CACHED;
1665-
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
16661663
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
1664+
if (ce->default_static_members_count) {
1665+
ZEND_MAP_PTR_INIT(ce->static_members_table,
1666+
zend_arena_alloc(&CG(arena), sizeof(zval *)));
1667+
ZEND_MAP_PTR_SET(ce->static_members_table, NULL);
1668+
}
16671669
}
16681670
}
16691671

ext/opcache/zend_persist.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -926,11 +926,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
926926
} else {
927927
ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
928928
}
929-
} else {
930-
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
931929
}
932-
} else {
933-
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
934930
}
935931

936932
zend_hash_persist(&ce->constants_table);

ext/reflection/tests/bug80821.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Bug #80821: ReflectionProperty::getDefaultValue() returns current value for statics
3+
--FILE--
4+
<?php
5+
6+
class Statics {
7+
public static $staticVar = 1;
8+
}
9+
10+
Statics::$staticVar = 2;
11+
12+
$reflect = new \ReflectionClass(Statics::class);
13+
$prop = $reflect->getProperty("staticVar");
14+
var_dump($prop->getDefaultValue());
15+
16+
?>
17+
--EXPECT--
18+
int(1)

0 commit comments

Comments
 (0)