From b39984c9911f10c9f325cfa82baf1427595ef005 Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 18 Oct 2026 00:37:04 +0200 Subject: [PATCH 1/3] Rework how attributed constants are stored and handled The original PR GH-16952 introduced special handling and a special flag to store the attribute via an OP_DATA opcode. It required special handling in various places. This commit reworks that by introducing a type specialization of the zend_constant structure and storing the attribute data in the main op array. This reduces complexity and removes special handling in various places. --- UPGRADING.INTERNALS | 8 ++++ Zend/zend_attributes.c | 5 +++ Zend/zend_attributes.h | 6 ++- Zend/zend_compile.c | 37 ++++++----------- Zend/zend_compile.h | 5 --- Zend/zend_constants.c | 12 ++++-- Zend/zend_constants.h | 17 ++++++++ Zend/zend_execute.c | 12 +++--- Zend/zend_opcode.c | 12 ------ Zend/zend_vm_def.h | 35 +++++++--------- Zend/zend_vm_execute.h | 70 +++++++++++++------------------- Zend/zend_vm_opcodes.c | 2 +- ext/opcache/jit/zend_jit_ir.c | 1 - ext/opcache/jit/zend_jit_trace.c | 1 - ext/opcache/zend_file_cache.c | 27 ------------ ext/opcache/zend_persist.c | 9 ---- ext/opcache/zend_persist_calc.c | 15 ------- ext/reflection/php_reflection.c | 2 +- 18 files changed, 107 insertions(+), 169 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 4c5d74b49e9c..eed8e17c5bfe 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -47,6 +47,11 @@ PHP 8.7 INTERNALS UPGRADE NOTES - The INIT_CLASS_ENTRY_INIT_METHODS() macro, which was previously used as part of handling disabled classes, was removed following the removal of the `disable_classes` INI setting in PHP 8.5. +- Removed ZEND_ACC_PTR_OPS. Attributes of user constants are now stored in + the attributes table of the declaring file's main op_array. + zend_constant.attributes can therefore be shared. + Use zend_constant_attributes_offset() to get the offset of a constant's own attributes. + Added zend_get_attribute_str_ex() to look up an attribute at a raw offset. ======================== 2. Build system changes @@ -82,6 +87,9 @@ PHP 8.7 INTERNALS UPGRADE NOTES 4. OpCode changes ======================== +- ZEND_DECLARE_ATTRIBUTED_CONST no longer has an OP_DATA opline. + The offset of the constant's attributes in the op_array's attributes table is stored in extended_value. + ======================== 5. SAPI changes ======================== diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 71f0d788e921..601e318a8368 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -304,6 +304,11 @@ ZEND_API zend_attribute *zend_get_attribute_str(const HashTable *attributes, con return get_attribute_str(attributes, str, len, 0); } +ZEND_API zend_attribute *zend_get_attribute_str_ex(const HashTable *attributes, const char *str, size_t len, uint32_t offset) +{ + return get_attribute_str(attributes, str, len, offset); +} + ZEND_API zend_attribute *zend_get_parameter_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset) { return get_attribute(attributes, lcname, offset + 1); diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index c044ef073cc8..71a035a13776 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -64,7 +64,9 @@ typedef struct _zend_attribute { zend_string *validation_error; uint32_t flags; uint32_t lineno; - /* Parameter offsets start at 1, everything else uses 0. */ + /* Parameter offsets start at 1. + * User constants use their own offset in the shared table of the main op_array (see zend_attributed_constant). + * Everything else uses 0. */ uint32_t offset; uint32_t argc; zend_attribute_arg args[1]; @@ -78,6 +80,8 @@ typedef struct _zend_internal_attribute { ZEND_API zend_attribute *zend_get_attribute(const HashTable *attributes, const zend_string *lcname); ZEND_API zend_attribute *zend_get_attribute_str(const HashTable *attributes, const char *str, size_t len); +/* Raw offset, i.e. not adjusted for parameters. */ +ZEND_API zend_attribute *zend_get_attribute_str_ex(const HashTable *attributes, const char *str, size_t len, uint32_t offset); ZEND_API zend_attribute *zend_get_parameter_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset); ZEND_API zend_attribute *zend_get_parameter_attribute_str(const HashTable *attributes, const char *str, size_t len, uint32_t offset); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ffd40f06014d..c6ee512679ce 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8226,23 +8226,12 @@ static void zend_compile_attributes( if (*attributes != NULL) { /* Allow delaying target validation for forward compatibility. */ - const zend_attribute *delayed_target_validation = NULL; - if (target == ZEND_ATTRIBUTE_TARGET_PARAMETER) { - ZEND_ASSERT(offset >= 1); - /* zend_get_parameter_attribute_str will add 1 too */ - delayed_target_validation = zend_get_parameter_attribute_str( - *attributes, - "delayedtargetvalidation", - strlen("delayedtargetvalidation"), - offset - 1 - ); - } else { - delayed_target_validation = zend_get_attribute_str( - *attributes, - "delayedtargetvalidation", - strlen("delayedtargetvalidation") - ); - } + const zend_attribute *delayed_target_validation = zend_get_attribute_str_ex( + *attributes, + "delayedtargetvalidation", + strlen("delayedtargetvalidation"), + offset + ); /* Validate attributes in a secondary loop (needed to detect repeated attributes). */ ZEND_HASH_PACKED_FOREACH_PTR(*attributes, attr) { if (attr->offset != offset || NULL == (config = zend_internal_attribute_get(attr->lcname))) { @@ -10447,16 +10436,16 @@ static void zend_compile_const_decl(zend_ast *ast) /* {{{ */ ); } - HashTable *attributes = NULL; - zend_compile_attributes(&attributes, list->child[1], 0, ZEND_ATTRIBUTE_TARGET_CONST, 0); + /* Constants can only be declared at the top level, so the attributes of + * all constants of a file are stored in the attributes table of the main op_array. */ + HashTable **attributes = &CG(active_op_array)->attributes; + ZEND_ASSERT(!CG(active_op_array)->function_name); + uint32_t offset = *attributes ? zend_hash_num_elements(*attributes) : 0; + zend_compile_attributes(attributes, list->child[1], offset, ZEND_ATTRIBUTE_TARGET_CONST, 0); ZEND_ASSERT(last_op != NULL); last_op->opcode = ZEND_DECLARE_ATTRIBUTED_CONST; - znode attribs_node; - attribs_node.op_type = IS_CONST; - ZVAL_PTR(&attribs_node.u.constant, attributes); - zend_emit_op_data(&attribs_node); - CG(active_op_array)->fn_flags |= ZEND_ACC_PTR_OPS; + last_op->extended_value = offset; } /* }}}*/ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index ec6d8b09bbe7..e82d4212116f 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -402,11 +402,6 @@ typedef struct _zend_oparray_context { /* supports opcache compile-time evaluation (funcs) | | | */ #define ZEND_ACC_COMPILE_TIME_EVAL (1 << 27) /* | X | | */ /* | | | */ -/* Has IS_PTR operands that needs special cleaning; same | | | */ -/* value as ZEND_ACC_OVERRIDE but override is for class | | | */ -/* methods and this is for the top level op array | | | */ -#define ZEND_ACC_PTR_OPS (1 << 28) /* | X | | */ -/* | | | */ /* has #[\NoDiscard] attribute | | | */ #define ZEND_ACC_NODISCARD (1 << 29) /* | X | | */ /* | | | */ diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 6f50c59641c2..4b695419fb38 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -75,6 +75,7 @@ static void copy_zend_constant(zval *zv) zend_constant *c = Z_PTR_P(zv); ZEND_ASSERT(ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT); + ZEND_ASSERT(!(ZEND_CONSTANT_FLAGS(c) & CONST_ATTRIBUTED)); Z_PTR_P(zv) = pemalloc(sizeof(zend_constant), 1); memcpy(Z_PTR_P(zv), c, sizeof(zend_constant)); @@ -497,9 +498,10 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, const zend_class_entry * static void* zend_hash_add_constant(HashTable *ht, zend_string *key, const zend_constant *c) { void *ret; - zend_constant *copy = pemalloc(sizeof(zend_constant), ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT); + size_t size = (ZEND_CONSTANT_FLAGS(c) & CONST_ATTRIBUTED) ? sizeof(zend_attributed_constant) : sizeof(zend_constant); + zend_constant *copy = pemalloc(size, ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT); - memcpy(copy, c, sizeof(zend_constant)); + memcpy(copy, c, size); ret = zend_hash_add_ptr(ht, key, copy); if (!ret) { pefree(copy, ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT); @@ -568,13 +570,15 @@ ZEND_API zend_constant *zend_register_constant(zend_constant *c) } void zend_constant_add_attributes(zend_constant *c, HashTable *attributes) { + ZEND_ASSERT(attributes != NULL); GC_TRY_ADDREF(attributes); c->attributes = attributes; - zend_attribute *deprecated_attribute = zend_get_attribute_str( + zend_attribute *deprecated_attribute = zend_get_attribute_str_ex( c->attributes, "deprecated", - strlen("deprecated") + strlen("deprecated"), + zend_constant_attributes_offset(c) ); if (deprecated_attribute) { diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 9461ac764c90..410ca504f6d8 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -27,6 +27,7 @@ #define CONST_DEPRECATED (1<<2) /* Deprecated */ #define CONST_OWNED (1<<3) /* constant should be destroyed together with class */ #define CONST_RECURSIVE (1<<4) /* Recursion protection for constant evaluation */ +#define CONST_ATTRIBUTED (1<<5) /* Allocated as zend_attributed_constant */ #define CONST_IS_RECURSIVE(c) (Z_CONSTANT_FLAGS((c)->value) & CONST_RECURSIVE) #define CONST_PROTECT_RECURSION(c) \ @@ -44,6 +45,7 @@ typedef struct _zend_constant { zval value; zend_string *name; zend_string *filename; + /* May be shared with other constants, see zend_attributed_constant. */ HashTable *attributes; } zend_constant; @@ -58,6 +60,21 @@ typedef struct _zend_constant { ((_flags) & 0xff) | ((_module_number) << 8); \ } while (0) +/* User constants declared with attributes. + * This is a type specialization of the zend_constant type. */ +typedef struct zend_attributed_constant { + zend_constant constant; + /* Offset into the attributes able of the main op_array of the declaring file. */ + uint32_t attributes_offset; +} zend_attributed_constant; + +static zend_always_inline uint32_t zend_constant_attributes_offset(const zend_constant *c) +{ + return (ZEND_CONSTANT_FLAGS(c) & CONST_ATTRIBUTED) + ? ((const zend_attributed_constant *) c)->attributes_offset + : 0; +} + #define REGISTER_NULL_CONSTANT(name, flags) zend_register_null_constant((name), sizeof(name)-1, (flags), module_number) #define REGISTER_BOOL_CONSTANT(name, bval, flags) zend_register_bool_constant((name), sizeof(name)-1, (bval), (flags), module_number) #define REGISTER_LONG_CONSTANT(name, lval, flags) zend_register_long_constant((name), sizeof(name)-1, (lval), (flags), module_number) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e7923d76996c..e88f3f71cb4c 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1831,7 +1831,7 @@ ZEND_API zend_never_inline ZEND_COLD void zend_wrong_string_offset_error(void) zend_throw_error(NULL, "%s", msg); } -ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix) +ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, uint32_t offset, zend_class_entry* scope, zend_string **message_suffix) { *message_suffix = ZSTR_EMPTY_ALLOC(); @@ -1839,7 +1839,7 @@ ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute return SUCCESS; } - zend_attribute *deprecated = zend_get_attribute_str(attributes, "deprecated", sizeof("deprecated")-1); + zend_attribute *deprecated = zend_get_attribute_str_ex(attributes, "deprecated", sizeof("deprecated")-1, offset); if (!deprecated) { return SUCCESS; @@ -1900,7 +1900,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_functi { zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); - if (get_deprecation_suffix_from_attribute(fbc->common.attributes, fbc->common.scope, &message_suffix) == FAILURE) { + if (get_deprecation_suffix_from_attribute(fbc->common.attributes, 0, fbc->common.scope, &message_suffix) == FAILURE) { return; } @@ -2007,7 +2007,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_ { zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); - if (get_deprecation_suffix_from_attribute(c->attributes, c->ce, &message_suffix) == FAILURE) { + if (get_deprecation_suffix_from_attribute(c->attributes, 0, c->ce, &message_suffix) == FAILURE) { return; } @@ -2028,7 +2028,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_constant(const zend_consta { zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); - if (get_deprecation_suffix_from_attribute(c->attributes, NULL, &message_suffix) == FAILURE) { + if (get_deprecation_suffix_from_attribute(c->attributes, zend_constant_attributes_offset(c), NULL, &message_suffix) == FAILURE) { return; } @@ -2048,7 +2048,7 @@ ZEND_API ZEND_COLD void zend_use_of_deprecated_trait( ) { zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); - if (get_deprecation_suffix_from_attribute(trait->attributes, trait, &message_suffix) == FAILURE) { + if (get_deprecation_suffix_from_attribute(trait->attributes, 0, trait, &message_suffix) == FAILURE) { return; } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index a25f236c3c20..9a4e89f28d0f 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -595,18 +595,6 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) efree(op_array->vars); } - /* ZEND_ACC_PTR_OPS and ZEND_ACC_OVERRIDE use the same value */ - if ((op_array->fn_flags & ZEND_ACC_PTR_OPS) && !op_array->function_name) { - zend_op *op = op_array->opcodes; - zend_op *end = op + op_array->last; - while (op < end) { - if (op->opcode == ZEND_DECLARE_ATTRIBUTED_CONST) { - HashTable *attributes = Z_PTR_P(RT_CONSTANT(op+1, (op+1)->op1)); - zend_hash_release(attributes); - } - op++; - } - } if (op_array->literals) { zval *literal = op_array->literals; zval *end = literal + op_array->last_literal; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index cf8d626bbdbc..3df50f50927f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8373,46 +8373,41 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST) ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST) +ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST, NUM) { USE_OPLINE zval *name; zval *val; - zend_constant c, *registered; + zend_attributed_constant c; + zend_constant *registered; SAVE_OPLINE(); name = GET_OP1_ZVAL_PTR(BP_VAR_R); val = GET_OP2_ZVAL_PTR(BP_VAR_R); - ZVAL_COPY(&c.value, val); - if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { - zval_ptr_dtor_nogc(&c.value); + ZVAL_COPY(&c.constant.value, val); + if (Z_OPT_TYPE(c.constant.value) == IS_CONSTANT_AST) { + if (UNEXPECTED(zval_update_constant_ex(&c.constant.value, EX(func)->op_array.scope) != SUCCESS)) { + zval_ptr_dtor_nogc(&c.constant.value); FREE_OP1(); FREE_OP2(); HANDLE_EXCEPTION(); } } /* non persistent, case sensitive */ - ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT); - c.name = zend_string_copy(Z_STR_P(name)); + ZEND_CONSTANT_SET_FLAGS(&c.constant, CONST_ATTRIBUTED, PHP_USER_CONSTANT); + c.constant.name = zend_string_copy(Z_STR_P(name)); + c.attributes_offset = opline->extended_value; - registered = zend_register_constant(&c); - if (registered == NULL) { - FREE_OP1(); - FREE_OP2(); - /* two opcodes used, second one is the data with attributes */ - ZEND_VM_NEXT_OPCODE_EX(1, 2); + registered = zend_register_constant(&c.constant); + if (registered != NULL) { + /* The attributes are stored in the main op_array, see zend_compile_const_decl() */ + zend_constant_add_attributes(registered, EX(func)->op_array.attributes); } - HashTable *attributes = Z_PTR_P(GET_OP_DATA_ZVAL_PTR(BP_VAR_R)); - ZEND_ASSERT(attributes != NULL); - zend_constant_add_attributes(registered, attributes); - FREE_OP1(); FREE_OP2(); - /* two opcodes used, second one is the data with attributes */ - ZEND_VM_NEXT_OPCODE_EX(1, 2); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, UNUSED, NUM, NUM|CACHE_SLOT) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 7fee6dbde454..18a2763c1276 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -8301,16 +8301,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_ATTRI USE_OPLINE zval *name; zval *val; - zend_constant c, *registered; + zend_attributed_constant c; + zend_constant *registered; SAVE_OPLINE(); name = RT_CONSTANT(opline, opline->op1); val = RT_CONSTANT(opline, opline->op2); - ZVAL_COPY(&c.value, val); - if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { - zval_ptr_dtor_nogc(&c.value); + ZVAL_COPY(&c.constant.value, val); + if (Z_OPT_TYPE(c.constant.value) == IS_CONSTANT_AST) { + if (UNEXPECTED(zval_update_constant_ex(&c.constant.value, EX(func)->op_array.scope) != SUCCESS)) { + zval_ptr_dtor_nogc(&c.constant.value); @@ -8319,28 +8320,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_ATTRI } } /* non persistent, case sensitive */ - ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT); - c.name = zend_string_copy(Z_STR_P(name)); - - registered = zend_register_constant(&c); - if (registered == NULL) { + ZEND_CONSTANT_SET_FLAGS(&c.constant, CONST_ATTRIBUTED, PHP_USER_CONSTANT); + c.constant.name = zend_string_copy(Z_STR_P(name)); + c.attributes_offset = opline->extended_value; - - - - /* two opcodes used, second one is the data with attributes */ - ZEND_VM_NEXT_OPCODE_EX(1, 2); + registered = zend_register_constant(&c.constant); + if (registered != NULL) { + /* The attributes are stored in the main op_array, see zend_compile_const_decl() */ + zend_constant_add_attributes(registered, EX(func)->op_array.attributes); } - HashTable *attributes = Z_PTR_P(get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1)); - ZEND_ASSERT(attributes != NULL); - zend_constant_add_attributes(registered, attributes); - - /* two opcodes used, second one is the data with attributes */ - ZEND_VM_NEXT_OPCODE_EX(1, 2); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -61164,16 +61157,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_ATTRIBUTED USE_OPLINE zval *name; zval *val; - zend_constant c, *registered; + zend_attributed_constant c; + zend_constant *registered; SAVE_OPLINE(); name = RT_CONSTANT(opline, opline->op1); val = RT_CONSTANT(opline, opline->op2); - ZVAL_COPY(&c.value, val); - if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { - zval_ptr_dtor_nogc(&c.value); + ZVAL_COPY(&c.constant.value, val); + if (Z_OPT_TYPE(c.constant.value) == IS_CONSTANT_AST) { + if (UNEXPECTED(zval_update_constant_ex(&c.constant.value, EX(func)->op_array.scope) != SUCCESS)) { + zval_ptr_dtor_nogc(&c.constant.value); @@ -61182,28 +61176,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_ATTRIBUTED } } /* non persistent, case sensitive */ - ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT); - c.name = zend_string_copy(Z_STR_P(name)); - - registered = zend_register_constant(&c); - if (registered == NULL) { + ZEND_CONSTANT_SET_FLAGS(&c.constant, CONST_ATTRIBUTED, PHP_USER_CONSTANT); + c.constant.name = zend_string_copy(Z_STR_P(name)); + c.attributes_offset = opline->extended_value; - - - - /* two opcodes used, second one is the data with attributes */ - ZEND_VM_NEXT_OPCODE_EX(1, 2); + registered = zend_register_constant(&c.constant); + if (registered != NULL) { + /* The attributes are stored in the main op_array, see zend_compile_const_decl() */ + zend_constant_add_attributes(registered, EX(func)->op_array.attributes); } - HashTable *attributes = Z_PTR_P(get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1)); - ZEND_ASSERT(attributes != NULL); - zend_constant_add_attributes(registered, attributes); - - /* two opcodes used, second one is the data with attributes */ - ZEND_VM_NEXT_OPCODE_EX(1, 2); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_CONST_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index cc14862ebedd..f6eb53477da9 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -449,7 +449,7 @@ static uint32_t zend_vm_opcodes_flags[214] = { 0x00000000, 0x01042003, 0x01001103, - 0x00000303, + 0x01000303, 0x01000003, 0x01000303, 0x00001301, diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index bfa5297aa6db..7680b7959a2b 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -4125,7 +4125,6 @@ static int zend_jit_handler(zend_jit_ctx *jit, const zend_op *opline, int may_th case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_ASSIGN_OBJ_REF: case ZEND_FRAMELESS_ICALL_3: - case ZEND_DECLARE_ATTRIBUTED_CONST: zend_jit_set_last_valid_opline(jit, opline + 2); break; default: diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index e1836b826107..f81bb27faf1e 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -627,7 +627,6 @@ static zend_always_inline int zend_jit_trace_op_len(const zend_op *opline) case ZEND_ASSIGN_OBJ_REF: case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_FRAMELESS_ICALL_3: - case ZEND_DECLARE_ATTRIBUTED_CONST: return 2; /* OP_DATA */ case ZEND_RECV_INIT: len = 1; diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 0b6cabe42015..d93b3d6a0e4d 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -247,13 +247,6 @@ static void zend_file_cache_unserialize_func(zval *zv, zend_persistent_script *script, void *buf); -static void zend_file_cache_serialize_attribute(zval *zv, - zend_persistent_script *script, - zend_file_cache_metainfo *info, - void *buf); - -static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_script *script, void *buf); - static void *zend_file_cache_serialize_interned(zend_string *str, zend_file_cache_metainfo *info) { @@ -444,9 +437,6 @@ static void zend_file_cache_serialize_zval(zval *zv, /* Used by static properties. */ SERIALIZE_PTR(Z_INDIRECT_P(zv)); break; - case IS_PTR: - /* Used by attributes on constants, will be handled separately */ - break; default: ZEND_ASSERT(Z_TYPE_P(zv) < IS_STRING); break; @@ -566,13 +556,6 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra UNSERIALIZE_PTR(opline); end = opline + op_array->last; while (opline < end) { - if (opline->opcode == ZEND_OP_DATA - && (opline-1)->opcode == ZEND_DECLARE_ATTRIBUTED_CONST - ) { - zval *literal = RT_CONSTANT(opline, opline->op1); - SERIALIZE_ATTRIBUTES(Z_PTR_P(literal)); - } - #if ZEND_USE_ABS_CONST_ADDR if (opline->op1_type == IS_CONST) { SERIALIZE_PTR(opline->op1.zv); @@ -1376,9 +1359,6 @@ static void zend_file_cache_unserialize_zval(zval *zv, /* Used by static properties. */ UNSERIALIZE_PTR(Z_INDIRECT_P(zv)); break; - case IS_PTR: - /* Used by attributes on constants, will be handled separately */ - break; default: ZEND_ASSERT(Z_TYPE_P(zv) < IS_STRING); break; @@ -1550,13 +1530,6 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr } #endif - if (opline->opcode == ZEND_OP_DATA - && (opline-1)->opcode == ZEND_DECLARE_ATTRIBUTED_CONST - ) { - zval *literal = RT_CONSTANT(opline, opline->op1); - UNSERIALIZE_ATTRIBUTES(Z_PTR_P(literal)); - } - if (opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { if (script->corrupted) { opline->extended_value &= ~ZEND_PARTIAL_CACHEABLE_IN_SHM; diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 134551781ba2..42db6d0673f2 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -290,8 +290,6 @@ static void zend_persist_zval(zval *z) efree(old_ref); } break; - case IS_PTR: - break; default: ZEND_ASSERT(Z_TYPE_P(z) < IS_STRING); break; @@ -626,13 +624,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc } } #endif - if (opline->opcode == ZEND_OP_DATA && (opline-1)->opcode == ZEND_DECLARE_ATTRIBUTED_CONST) { - zval *literal = RT_CONSTANT(opline, opline->op1); - HashTable *attributes = Z_PTR_P(literal); - attributes = zend_persist_attributes(attributes); - ZVAL_PTR(literal, attributes); - } - if (opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { if (!ZCG(current_persistent_script)->corrupted) { opline->extended_value |= ZEND_PARTIAL_CACHEABLE_IN_SHM; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 71398024e7c6..8e828746964f 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -163,8 +163,6 @@ static void zend_persist_zval_calc(zval *z) } } break; - case IS_PTR: - break; default: ZEND_ASSERT(Z_TYPE_P(z) < IS_STRING); break; @@ -275,19 +273,6 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array->opcodes); ADD_SIZE(sizeof(zend_op) * op_array->last); - /* ZEND_ACC_PTR_OPS and ZEND_ACC_OVERRIDE use the same value */ - if ((op_array->fn_flags & ZEND_ACC_PTR_OPS) && !op_array->function_name) { - zend_op *op = op_array->opcodes; - const zend_op *end = op + op_array->last; - while (op < end) { - if (op->opcode == ZEND_DECLARE_ATTRIBUTED_CONST) { - HashTable *attributes = Z_PTR_P(RT_CONSTANT(op+1, (op+1)->op1)); - zend_persist_attributes_calc(attributes); - } - op++; - } - } - if (op_array->filename) { ADD_STRING(op_array->filename); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0c1a6ba721ce..8057bbcb3c14 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -8148,7 +8148,7 @@ ZEND_METHOD(ReflectionConstant, getAttributes) GET_REFLECTION_OBJECT_PTR(const_); reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, - const_->attributes, 0, NULL, ZEND_ATTRIBUTE_TARGET_CONST, + const_->attributes, zend_constant_attributes_offset(const_), NULL, ZEND_ATTRIBUTE_TARGET_CONST, const_->filename); } From a3a302454b067cc5672273dc4d4574849e861b85 Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 18 Oct 2026 10:55:18 +0200 Subject: [PATCH 2/3] tweak --- Zend/zend_constants.c | 1 - Zend/zend_constants.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 4b695419fb38..3382033ad572 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -570,7 +570,6 @@ ZEND_API zend_constant *zend_register_constant(zend_constant *c) } void zend_constant_add_attributes(zend_constant *c, HashTable *attributes) { - ZEND_ASSERT(attributes != NULL); GC_TRY_ADDREF(attributes); c->attributes = attributes; diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 410ca504f6d8..8f03a51be54f 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -114,7 +114,7 @@ ZEND_API zend_constant *zend_register_double_constant(const char *name, size_t n ZEND_API zend_constant *zend_register_string_constant(const char *name, size_t name_len, const char *strval, int flags, int module_number); ZEND_API zend_constant *zend_register_stringl_constant(const char *name, size_t name_len, const char *strval, size_t strlen, int flags, int module_number); ZEND_API zend_constant *zend_register_constant(zend_constant *c); -void zend_constant_add_attributes(zend_constant *c, HashTable *attributes); +void ZEND_ATTRIBUTE_NONNULL_ARGS(1, 2) zend_constant_add_attributes(zend_constant *c, HashTable *attributes); #ifdef ZTS void zend_copy_constants(HashTable *target, HashTable *source); #endif From 3e75da98e7436ebe8ca78aeaa76bdd7ed17fbf6f Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 18 Oct 2026 14:51:02 +0200 Subject: [PATCH 3/3] Use ZEND_ATTRIBUTE_NONNULL --- Zend/zend_constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 8f03a51be54f..dc3acfeb3517 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -114,7 +114,7 @@ ZEND_API zend_constant *zend_register_double_constant(const char *name, size_t n ZEND_API zend_constant *zend_register_string_constant(const char *name, size_t name_len, const char *strval, int flags, int module_number); ZEND_API zend_constant *zend_register_stringl_constant(const char *name, size_t name_len, const char *strval, size_t strlen, int flags, int module_number); ZEND_API zend_constant *zend_register_constant(zend_constant *c); -void ZEND_ATTRIBUTE_NONNULL_ARGS(1, 2) zend_constant_add_attributes(zend_constant *c, HashTable *attributes); +void ZEND_ATTRIBUTE_NONNULL zend_constant_add_attributes(zend_constant *c, HashTable *attributes); #ifdef ZTS void zend_copy_constants(HashTable *target, HashTable *source); #endif