mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Fix inheritance of class constants if mutable data used
Class constants from parents should always be directly reused, rather than re-evaluated as a separate copy. Previously this used to happen automatically, as we'd just inherit the class constant entry from the parent class. With mutable data there may now be a separate copy of the constant, so we need to use that copy when updating constants. Otherwise we may evaluate the same constant multiple times. Closes GH-7658.
This commit is contained in:
parent
61b432ca24
commit
44e5d25300
3 changed files with 61 additions and 7 deletions
35
Zend/tests/class_constant_inheritance_mutable_data.phpt
Normal file
35
Zend/tests/class_constant_inheritance_mutable_data.phpt
Normal file
|
@ -0,0 +1,35 @@
|
|||
--TEST--
|
||||
Class constant inheritance with mutable data
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
// This would previously leak under opcache.
|
||||
class A {
|
||||
const X = 'X' . self::Y;
|
||||
const Y = 'Y';
|
||||
}
|
||||
interface I {
|
||||
const X2 = 'X2' . self::Y2;
|
||||
const Y2 = 'Y2';
|
||||
}
|
||||
eval('class B extends A implements I {}');
|
||||
var_dump(new B);
|
||||
var_dump(B::X, B::X2);
|
||||
|
||||
// This should only produce one warning, not two.
|
||||
class X {
|
||||
const C = 1 % 1.5;
|
||||
}
|
||||
class Y extends X {
|
||||
}
|
||||
var_dump(X::C, Y::C);
|
||||
?>
|
||||
--EXPECTF--
|
||||
object(B)#1 (0) {
|
||||
}
|
||||
string(2) "XY"
|
||||
string(4) "X2Y2"
|
||||
|
||||
Deprecated: Implicit conversion from float 1.5 to int loses precision in %s on line %d
|
||||
int(0)
|
||||
int(0)
|
|
@ -1322,12 +1322,19 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_
|
|||
zend_hash_extend(constants_table, zend_hash_num_elements(&class_type->constants_table), 0);
|
||||
|
||||
ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) {
|
||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
|
||||
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
|
||||
memcpy(new_c, c, sizeof(zend_class_constant));
|
||||
c = new_c;
|
||||
if (c->ce == class_type) {
|
||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
|
||||
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
|
||||
memcpy(new_c, c, sizeof(zend_class_constant));
|
||||
c = new_c;
|
||||
}
|
||||
Z_TRY_ADDREF(c->value);
|
||||
} else {
|
||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
|
||||
c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(c->ce), key);
|
||||
ZEND_ASSERT(c);
|
||||
}
|
||||
}
|
||||
Z_TRY_ADDREF(c->value);
|
||||
_zend_hash_append_ptr(constants_table, key, c);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
|
@ -1409,8 +1416,18 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
|
|||
} else {
|
||||
constants_table = &class_type->constants_table;
|
||||
}
|
||||
ZEND_HASH_FOREACH_PTR(constants_table, c) {
|
||||
|
||||
zend_string *name;
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(constants_table, name, val) {
|
||||
c = Z_PTR_P(val);
|
||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
|
||||
if (c->ce != class_type) {
|
||||
Z_PTR_P(val) = c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(c->ce), name);
|
||||
if (Z_TYPE(c->value) != IS_CONSTANT_AST) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
val = &c->value;
|
||||
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
|
||||
return FAILURE;
|
||||
|
|
|
@ -250,7 +250,9 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce)
|
|||
zend_class_constant *c;
|
||||
|
||||
ZEND_HASH_FOREACH_PTR(constants_table, c) {
|
||||
zval_ptr_dtor_nogc(&c->value);
|
||||
if (c->ce == ce) {
|
||||
zval_ptr_dtor_nogc(&c->value);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
zend_hash_destroy(constants_table);
|
||||
mutable_data->constants_table = NULL;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue