From 87a22b2da5dde295d2635ac474a98e374760997f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 29 Jan 2023 21:01:05 +0100 Subject: [PATCH 01/11] Implement typed class constants RFC: https://wiki.php.net/rfc/typed_class_constants Co-Authored-By: Ben <7127204+moliata@users.noreply.github.com> Co-Authored-By: Bob Weinand <3154871+bwoebi@users.noreply.github.com> --- Zend/tests/enum/name-property.phpt | 4 +- .../typed_class_constants_diamond_error1.phpt | 16 +++++++ ...ed_class_constants_inheritance_error1.phpt | 14 ++++++ ...ed_class_constants_inheritance_error2.phpt | 14 ++++++ ..._class_constants_inheritance_success1.phpt | 29 +++++++++++ ..._class_constants_inheritance_success2.phpt | 48 +++++++++++++++++++ ..._class_constants_inheritance_success3.phpt | 16 +++++++ .../typed_class_constants_type_error1.phpt | 10 ++++ .../typed_class_constants_type_error2.phpt | 18 +++++++ .../typed_class_constants_type_error3.phpt | 18 +++++++ .../typed_class_constants_type_error4.phpt | 10 ++++ .../typed_class_constants_type_error5.phpt | 10 ++++ .../typed_class_constants_type_error6.phpt | 10 ++++ .../typed_class_constants_type_error7.phpt | 18 +++++++ .../typed_class_constants_type_error8.phpt | 10 ++++ .../typed_class_constants_type_success1.phpt | 43 +++++++++++++++++ .../typed_class_constants_type_success2.phpt | 37 ++++++++++++++ .../typed_class_constants_type_success3.phpt | 23 +++++++++ Zend/zend_API.c | 16 ++++++- Zend/zend_API.h | 1 + Zend/zend_ast.h | 2 +- Zend/zend_compile.c | 28 +++++++++-- Zend/zend_compile.h | 2 + Zend/zend_execute.c | 21 ++++++++ Zend/zend_execute.h | 4 ++ Zend/zend_inheritance.c | 24 +++++++++- Zend/zend_language_parser.y | 19 +++++--- Zend/zend_vm_def.h | 8 ++++ Zend/zend_vm_execute.h | 48 +++++++++++++++++++ ext/reflection/php_reflection.c | 33 +++++++++++++ ext/reflection/php_reflection.stub.php | 4 ++ ext/reflection/php_reflection_arginfo.h | 10 +++- .../tests/ReflectionClassConstant_basic1.phpt | 27 ++++++++++- 33 files changed, 577 insertions(+), 18 deletions(-) create mode 100644 Zend/tests/type_declarations/typed_class_constants_diamond_error1.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_error1.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_error2.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_success1.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_success2.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_success3.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error1.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error2.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error3.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error4.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error5.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error6.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error7.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error8.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_success1.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_success2.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_success3.phpt diff --git a/Zend/tests/enum/name-property.phpt b/Zend/tests/enum/name-property.phpt index 2197f235bbb2d..a493c752c95ff 100644 --- a/Zend/tests/enum/name-property.phpt +++ b/Zend/tests/enum/name-property.phpt @@ -33,14 +33,14 @@ array(1) { string(3) "Bar" array(2) { [0]=> - object(ReflectionProperty)#3 (2) { + object(ReflectionProperty)#4 (2) { ["name"]=> string(4) "name" ["class"]=> string(6) "IntFoo" } [1]=> - object(ReflectionProperty)#4 (2) { + object(ReflectionProperty)#5 (2) { ["name"]=> string(5) "value" ["class"]=> diff --git a/Zend/tests/type_declarations/typed_class_constants_diamond_error1.phpt b/Zend/tests/type_declarations/typed_class_constants_diamond_error1.phpt new file mode 100644 index 0000000000000..059c3cb2005b7 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_diamond_error1.phpt @@ -0,0 +1,16 @@ +--TEST-- +Typed class constants (diamond error with self) +--FILE-- +getMessage() . "\n"; +} +?> +--EXPECT-- +Undefined constant "C" diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_error1.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_error1.phpt new file mode 100644 index 0000000000000..698d9934e03c5 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_error1.phpt @@ -0,0 +1,14 @@ +--TEST-- +Typed class constants (incompatible inheritance; simple) +--FILE-- + +--EXPECTF-- +Fatal error: Declaration of B::CONST1 must be compatible with A::CONST1 in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_error2.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_error2.phpt new file mode 100644 index 0000000000000..a1d9d2ad9f848 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_error2.phpt @@ -0,0 +1,14 @@ +--TEST-- +Typed class constants (incompatible inheritance; missing type in child) +--FILE-- + +--EXPECTF-- +Fatal error: Declaration of B::CONST1 must be compatible with A::CONST1 in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_success1.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_success1.phpt new file mode 100644 index 0000000000000..0dbd3a3385bc8 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_success1.phpt @@ -0,0 +1,29 @@ +--TEST-- +Typed class constants (inheritance success) +--FILE-- + +--EXPECT-- +int(0) +int(0) +int(0) +array(0) { +} diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_success2.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_success2.phpt new file mode 100644 index 0000000000000..40e079c3596e7 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_success2.phpt @@ -0,0 +1,48 @@ +--TEST-- +Typed class constants (inheritance success - object types) +--FILE-- + +--EXPECTF-- +object(Z)#%d (%d) { +} +object(Z)#%d (%d) { +} +object(Z)#%d (%d) { +} +object(Z)#%d (%d) { +} +object(Z)#%d (%d) { +} \ No newline at end of file diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_success3.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_success3.phpt new file mode 100644 index 0000000000000..dfcc5742d2b43 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_success3.phpt @@ -0,0 +1,16 @@ +--TEST-- +Typed class constants (inheritance; private constants) +--FILE-- + +--EXPECT-- +string(1) "a" diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error1.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error1.phpt new file mode 100644 index 0000000000000..8f70043091fda --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error1.phpt @@ -0,0 +1,10 @@ +--TEST-- +Typed class constants (type mismatch; simple) +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use int as value for class constant A::CONST1 of type string in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error2.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error2.phpt new file mode 100644 index 0000000000000..c4633ec888f16 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error2.phpt @@ -0,0 +1,18 @@ +--TEST-- +Typed class constants (type mismatch; runtime simple) +--FILE-- +getMessage() . "\n"; +} +?> +--EXPECT-- +Cannot assign string to class constant A::CONST1 of type int diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error3.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error3.phpt new file mode 100644 index 0000000000000..661b854771a4c --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error3.phpt @@ -0,0 +1,18 @@ +--TEST-- +Typed class constants (type mismatch; runtime object) +--FILE-- +getMessage() . "\n"; +} +?> +--EXPECT-- +Cannot assign stdClass to class constant A::CONST1 of type string diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error4.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error4.phpt new file mode 100644 index 0000000000000..7e41b602cf8b3 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error4.phpt @@ -0,0 +1,10 @@ +--TEST-- +Typed class constants (type not allowed; callable) +--FILE-- + +--EXPECTF-- +Fatal error: Class constant A::CONST1 cannot have type callable in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error5.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error5.phpt new file mode 100644 index 0000000000000..c083c738989cc --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error5.phpt @@ -0,0 +1,10 @@ +--TEST-- +Typed class constants (type not allowed; void) +--FILE-- + +--EXPECTF-- +Fatal error: Class constant A::CONST1 cannot have type void in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error6.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error6.phpt new file mode 100644 index 0000000000000..cbb96588f5cc3 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error6.phpt @@ -0,0 +1,10 @@ +--TEST-- +Typed class constants (type not allowed; never) +--FILE-- + +--EXPECTF-- +Fatal error: Class constant A::CONST1 cannot have type never in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error7.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error7.phpt new file mode 100644 index 0000000000000..09021e0e27821 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error7.phpt @@ -0,0 +1,18 @@ +--TEST-- +Typed class constants (type mismatch; runtime) +--FILE-- +getMessage() . "\n"; +} +?> +--EXPECT-- +Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error8.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error8.phpt new file mode 100644 index 0000000000000..a6c32913f8196 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error8.phpt @@ -0,0 +1,10 @@ +--TEST-- +Typed class constants (type not allowed; static) +--FILE-- + +--EXPECTF-- +Fatal error: Class constant A::CONST1 cannot have type static in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_type_success1.phpt b/Zend/tests/type_declarations/typed_class_constants_type_success1.phpt new file mode 100644 index 0000000000000..8c1b54a3c9dd2 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_success1.phpt @@ -0,0 +1,43 @@ +--TEST-- +Typed class constants (declaration; compile-type simple) +--FILE-- + +--EXPECT-- +NULL +bool(false) +bool(true) +bool(true) +int(0) +float(3.14) +float(3) +string(0) "" +array(0) { +} +string(0) "" +NULL diff --git a/Zend/tests/type_declarations/typed_class_constants_type_success2.phpt b/Zend/tests/type_declarations/typed_class_constants_type_success2.phpt new file mode 100644 index 0000000000000..809ec90d7fb0b --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_success2.phpt @@ -0,0 +1,37 @@ +--TEST-- +Typed class constants (declaration; runtime) +--FILE-- + +--EXPECTF-- +object(B)#%d (%d) { +} +object(B)#%d (%d) { +} +object(B)#%d (%d) { +} +object(B)#%d (%d) { +} +object(B)#%d (%d) { +} diff --git a/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt new file mode 100644 index 0000000000000..b99866de310d7 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt @@ -0,0 +1,23 @@ +--TEST-- +Typed class constants (declaration; enums) +--FILE-- + +--EXPECTF-- +enum(E::Foo) +enum(E::Foo) +enum(E::Foo) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 1beb76dff4dc0..0af247fed7c57 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1473,6 +1473,10 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { return FAILURE; } + + if (ZEND_TYPE_IS_SET(c->type) && UNEXPECTED(!zend_verify_class_constant_type(c, val))) { + return FAILURE; + } } } ZEND_HASH_FOREACH_END(); } @@ -4532,7 +4536,7 @@ ZEND_API void zend_declare_property_stringl(zend_class_entry *ce, const char *na } /* }}} */ -ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int flags, zend_string *doc_comment) /* {{{ */ +ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry *ce, zend_string *name, zval *value, int flags, zend_string *doc_comment, zend_type type) /* {{{ */ { zend_class_constant *c; @@ -4556,11 +4560,15 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c } else { c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); } + ZVAL_COPY_VALUE(&c->value, value); ZEND_CLASS_CONST_FLAGS(c) = flags; + c->name = name; c->doc_comment = doc_comment; c->attributes = NULL; c->ce = ce; + c->type = type; + if (Z_TYPE_P(value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; @@ -4576,7 +4584,11 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c return c; } -/* }}} */ + +ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int flags, zend_string *doc_comment) +{ + return zend_declare_typed_class_constant(ce, name, value, flags, doc_comment, (zend_type) ZEND_TYPE_INIT_NONE(0)); +} ZEND_API void zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value) /* {{{ */ { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 92daf6570aea6..f34eadd233b0b 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -429,6 +429,7 @@ ZEND_API void zend_declare_property_double(zend_class_entry *ce, const char *nam ZEND_API void zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type); ZEND_API void zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type); +ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment, zend_type type); ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment); ZEND_API void zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value); ZEND_API void zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index ca12afd6b2149..73e4fed7a997a 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -161,7 +161,6 @@ enum _zend_ast_kind { ZEND_AST_CATCH, ZEND_AST_PROP_GROUP, ZEND_AST_PROP_ELEM, - ZEND_AST_CONST_ELEM, // Pseudo node for initializing enums ZEND_AST_CONST_ENUM_INIT, @@ -170,6 +169,7 @@ enum _zend_ast_kind { ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_FOREACH, ZEND_AST_ENUM_CASE, + ZEND_AST_CONST_ELEM, /* 5 child nodes */ ZEND_AST_PARAM = 5 << ZEND_AST_NUM_CHILDREN_SHIFT, diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 6aa5bd15a990b..226895027c553 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7693,7 +7693,7 @@ static void zend_check_trait_alias_modifiers(uint32_t attr) /* {{{ */ } /* }}} */ -static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr_ast) /* {{{ */ +static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr_ast) { zend_ast_list *list = zend_ast_get_list(ast); zend_class_entry *ce = CG(active_class_entry); @@ -7705,9 +7705,24 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as zend_ast *name_ast = const_ast->child[0]; zend_ast **value_ast_ptr = &const_ast->child[1]; zend_ast *doc_comment_ast = const_ast->child[2]; + zend_ast *type_ast = const_ast->child[3]; zend_string *name = zval_make_interned_string(zend_ast_get_zval(name_ast)); zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL; zval value_zv; + zend_type type = ZEND_TYPE_INIT_NONE(0); + + if (type_ast) { + type = zend_compile_typename(type_ast, /* force_allow_null */ 0); + + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + + if (type_mask != MAY_BE_ANY && (type_mask & (MAY_BE_CALLABLE|MAY_BE_VOID|MAY_BE_NEVER|MAY_BE_STATIC))) { + zend_string *type_str = zend_type_to_string(type); + + zend_error_noreturn(E_COMPILE_ERROR, "Class constant %s::%s cannot have type %s", + ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str)); + } + } if (UNEXPECTED((flags & ZEND_ACC_PRIVATE) && (flags & ZEND_ACC_FINAL))) { zend_error_noreturn( @@ -7717,14 +7732,21 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as } zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false); - c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment); + + if (!Z_CONSTANT(value_zv) && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) { + zend_string *type_str = zend_type_to_string(type); + + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as value for class constant %s::%s of type %s", + zend_zval_type_name(&value_zv), ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str)); + } + + c = zend_declare_typed_class_constant(ce, name, &value_zv, flags, doc_comment, type); if (attr_ast) { zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0); } } } -/* }}} */ static void zend_compile_class_const_group(zend_ast *ast) /* {{{ */ { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 9a2129d908d6c..c9075858ecfda 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -404,9 +404,11 @@ typedef struct _zend_property_info { typedef struct _zend_class_constant { zval value; /* flags are stored in u2 */ + zend_string *name; zend_string *doc_comment; HashTable *attributes; zend_class_entry *ce; + zend_type type; } zend_class_constant; #define ZEND_CLASS_CONST_FLAGS(c) Z_CONSTANT_FLAGS((c)->value) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 59ae2720f92cc..d4837edeb35bc 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -820,6 +820,16 @@ ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool s return zend_verify_weak_scalar_type_hint(type_mask, arg); } +ZEND_COLD zend_never_inline void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant) +{ + zend_string *type_str = zend_type_to_string(c->type); + + zend_type_error("Cannot assign %s to class constant %s::%s of type %s", + zend_zval_type_name(constant), ZSTR_VAL(c->ce->name), ZSTR_VAL(c->name), ZSTR_VAL(type_str)); + + zend_string_release(type_str); +} + ZEND_COLD zend_never_inline void zend_verify_property_type_error(const zend_property_info *info, const zval *property) { zend_string *type_str; @@ -1455,6 +1465,17 @@ static ZEND_COLD void zend_verify_missing_return_type(const zend_function *zf) zend_verify_return_error(zf, NULL); } +zend_bool zend_never_inline zend_verify_class_constant_type(const zend_class_constant *c, zval *constant) +{ + if (zend_check_type((zend_type *) &c->type, constant, NULL, NULL, /* is_return_type */ 1, /* is_internal_arg */ 0)) { + return 1; + } + + zend_verify_class_constant_type_error(c, constant); + + return 0; +} + static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_object_as_array(void) { zend_throw_error(NULL, "Cannot use object as array"); diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index caeaa2a3b9f5f..c343e5e724c76 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -484,6 +484,10 @@ ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call); #define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS) #define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS) + +zend_bool zend_verify_class_constant_type(const zend_class_constant *c, zval *constant); +ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant); + ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict); ZEND_COLD void zend_verify_property_type_error(const zend_property_info *info, const zval *property); ZEND_COLD void zend_magic_get_property_type_inconsistency_error(const zend_property_info *info, const zval *property); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 5a689408263ab..08595f126b989 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1359,6 +1359,15 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en } /* }}} */ +inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child) +{ + if (!ZEND_TYPE_IS_SET(child->type)) { + return INHERITANCE_ERROR; + } + + return zend_perform_covariant_type_check(child->ce, child->type, parent->ce, parent->type); +} + static void do_inherit_class_constant(zend_string *name, zend_class_constant *parent_const, zend_class_entry *ce) /* {{{ */ { zval *zv = zend_hash_find_known_hash(&ce->constants_table, name); @@ -1366,9 +1375,14 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa if (zv != NULL) { c = (zend_class_constant*)Z_PTR_P(zv); + if (UNEXPECTED((ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_PPP_MASK) > (ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PPP_MASK))) { zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s must be %s (as in class %s)%s", - ZSTR_VAL(ce->name), ZSTR_VAL(name), zend_visibility_string(ZEND_CLASS_CONST_FLAGS(parent_const)), ZSTR_VAL(parent_const->ce->name), (ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PUBLIC) ? "" : " or weaker"); + ZSTR_VAL(ce->name), ZSTR_VAL(name), + zend_visibility_string(ZEND_CLASS_CONST_FLAGS(parent_const)), + ZSTR_VAL(parent_const->ce->name), + (ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PUBLIC) ? "" : " or weaker" + ); } if (UNEXPECTED((ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_FINAL))) { @@ -1377,6 +1391,14 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(parent_const->ce->name), ZSTR_VAL(name) ); } + + if (!(ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE) && UNEXPECTED(ZEND_TYPE_IS_SET(parent_const->type))) { + if (class_constant_types_compatible(parent_const, c) == INHERITANCE_ERROR) { + zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s::%s must be compatible with %s::%s", + ZSTR_VAL(c->ce->name), ZSTR_VAL(name), ZSTR_VAL(parent_const->ce->name), ZSTR_VAL(name) + ); + } + } } else if (!(ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE)) { if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index d1d54ade4dfd0..9b663887264f0 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -267,7 +267,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list %type implements_list case_list if_stmt_without_else %type non_empty_parameter_list argument_list non_empty_argument_list property_list -%type class_const_list class_const_decl class_name_list trait_adaptations method_body non_empty_for_exprs +%type class_const_list first_class_const_decl class_const_decl class_name_list trait_adaptations method_body non_empty_for_exprs %type ctor_arguments alt_if_stmt_without_else trait_adaptation_list lexical_vars %type lexical_var_list encaps_list %type array_pair non_empty_array_pair_list array_pair_list possible_array_pair @@ -812,7 +812,6 @@ parameter: NULL, $6 ? zend_ast_create_zval_from_str($6) : NULL); } ; - optional_type_without_static: %empty { $$ = NULL; } | type_expr_without_static { $$ = $1; } @@ -1077,15 +1076,21 @@ property: class_const_list: class_const_list ',' class_const_decl { $$ = zend_ast_list_add($1, $3); } - | class_const_decl { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_CONST_DECL, $1); } + | first_class_const_decl { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_CONST_DECL, $1); } +; + +first_class_const_decl: + T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), NULL); } + | semi_reserved '=' expr backup_doc_comment { zval zv; if (zend_lex_tstring(&zv, $1) == FAILURE) { YYABORT; } $$ = zend_ast_create(ZEND_AST_CONST_ELEM, zend_ast_create_zval(&zv), $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), NULL); } + | type_expr identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $2, $4, ($5 ? zend_ast_create_zval_from_str($5) : NULL), $1); } ; class_const_decl: - identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); } + identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), NULL); } ; const_decl: - T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); } + T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), NULL); } ; echo_expr_list: @@ -1317,8 +1322,8 @@ function_call: { $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $4); } | variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name argument_list { $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $4); } - | callable_expr { $$ = CG(zend_lineno); } argument_list { - $$ = zend_ast_create(ZEND_AST_CALL, $1, $3); + | callable_expr { $$ = CG(zend_lineno); } argument_list { + $$ = zend_ast_create(ZEND_AST_CALL, $1, $3); $$->lineno = $2; } ; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index a3a8bde7bd8bb..3b293e62e6c5a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5981,6 +5981,13 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO HANDLE_EXCEPTION(); } + if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } + value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -5999,6 +6006,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } } if (OP2_TYPE == IS_CONST) { + value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index a48c72d81b79c..050e026c5fca0 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7255,6 +7255,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS HANDLE_EXCEPTION(); } + if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } + value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -7273,6 +7280,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if (IS_CONST == IS_CONST) { + value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -8409,6 +8417,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS HANDLE_EXCEPTION(); } + if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } + value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -8427,6 +8442,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if ((IS_TMP_VAR|IS_VAR|IS_CV) == IS_CONST) { + value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -25074,6 +25090,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ HANDLE_EXCEPTION(); } + if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } + value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -25092,6 +25115,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if (IS_CONST == IS_CONST) { + value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -25637,6 +25661,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ HANDLE_EXCEPTION(); } + if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } + value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -25655,6 +25686,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if ((IS_TMP_VAR|IS_VAR|IS_CV) == IS_CONST) { + value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -34222,6 +34254,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS HANDLE_EXCEPTION(); } + if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } + value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -34240,6 +34279,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if (IS_CONST == IS_CONST) { + value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -34575,6 +34615,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS HANDLE_EXCEPTION(); } + if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } + value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -34593,6 +34640,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if ((IS_TMP_VAR|IS_VAR|IS_CV) == IS_CONST) { + value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 467da74a9b976..01517e7801d8a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3820,6 +3820,39 @@ ZEND_METHOD(ReflectionClassConstant, getName) } /* }}} */ +/* Returns the type associated with the class constant */ +ZEND_METHOD(ReflectionClassConstant, getType) +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ref); + + if (!ZEND_TYPE_IS_SET(ref->type)) { + RETURN_NULL(); + } + + reflection_type_factory(ref->type, return_value, 1); +} + +/* Returns whether class constant has a type */ +ZEND_METHOD(ReflectionClassConstant, hasType) +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ref); + RETVAL_BOOL(ZEND_TYPE_IS_SET(ref->type)); +} + static void _class_constant_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) /* {{{ */ { reflection_object *intern; diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 3ecb5f7876500..6757f7231f529 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -609,6 +609,10 @@ public function getDocComment(): string|false {} public function getAttributes(?string $name = null, int $flags = 0): array {} public function isEnumCase(): bool {} + + public function hasType(): bool {} + + public function getType(): ?ReflectionType {} } /** @not-serializable */ diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 708dbbeeb5d63..d483795f5ad97 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f640c1b592a7e9e7a8e92195df579bfaaa3da6dc */ + * Stub hash: 613661b122b2f0845eb0d3e8bc220164834552f1 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -414,6 +414,10 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionClassConstant_hasType arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + +#define arginfo_class_ReflectionClassConstant_getType arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType + #define arginfo_class_ReflectionParameter___clone arginfo_class_ReflectionFunctionAbstract___clone ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionParameter___construct, 0, 0, 2) @@ -760,6 +764,8 @@ ZEND_METHOD(ReflectionClassConstant, getDeclaringClass); ZEND_METHOD(ReflectionClassConstant, getDocComment); ZEND_METHOD(ReflectionClassConstant, getAttributes); ZEND_METHOD(ReflectionClassConstant, isEnumCase); +ZEND_METHOD(ReflectionClassConstant, hasType); +ZEND_METHOD(ReflectionClassConstant, getType); ZEND_METHOD(ReflectionParameter, __construct); ZEND_METHOD(ReflectionParameter, __toString); ZEND_METHOD(ReflectionParameter, getName); @@ -1046,6 +1052,8 @@ static const zend_function_entry class_ReflectionClassConstant_methods[] = { ZEND_ME(ReflectionClassConstant, getDocComment, arginfo_class_ReflectionClassConstant_getDocComment, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, getAttributes, arginfo_class_ReflectionClassConstant_getAttributes, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, isEnumCase, arginfo_class_ReflectionClassConstant_isEnumCase, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClassConstant, hasType, arginfo_class_ReflectionClassConstant_hasType, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClassConstant, getType, arginfo_class_ReflectionClassConstant_getType, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt index 7d563bdf3bc16..468ea736ce6ef 100644 --- a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt +++ b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt @@ -28,11 +28,15 @@ function reflectClassConstant($base, $constant) { var_dump($constInfo->getDeclaringClass()); echo "getDocComment():\n"; var_dump($constInfo->getDocComment()); + echo "hasType():\n"; + var_dump($constInfo->hasType()); + echo "getType():\n"; + var_dump($constInfo->getType()); echo "\n**********************************\n"; } class TestClass { - public const /** My Doc comment */ PUB = true; + public const bool /** My Doc comment */ PUB = true; /** Another doc comment */ protected const PROT = 4; private const PRIV = "keepOut"; @@ -76,6 +80,11 @@ object(ReflectionClass)#3 (1) { } getDocComment(): string(21) "/** My Doc comment */" +hasType(): +bool(true) +getType(): +object(ReflectionNamedType)#3 (0) { +} ********************************** ********************************** @@ -105,6 +114,10 @@ object(ReflectionClass)#3 (1) { } getDocComment(): string(26) "/** Another doc comment */" +hasType(): +bool(false) +getType(): +NULL ********************************** ********************************** @@ -134,6 +147,10 @@ object(ReflectionClass)#3 (1) { } getDocComment(): bool(false) +hasType(): +bool(false) +getType(): +NULL ********************************** ********************************** @@ -163,6 +180,10 @@ object(ReflectionClass)#3 (1) { } getDocComment(): bool(false) +hasType(): +bool(false) +getType(): +NULL ********************************** ********************************** @@ -192,6 +213,10 @@ object(ReflectionClass)#3 (1) { } getDocComment(): bool(false) +hasType(): +bool(false) +getType(): +NULL ********************************** From 827d412a0f1f8b5f982f7c3c874b24f6d0dfc9f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 23 Feb 2023 15:37:47 +0100 Subject: [PATCH 02/11] Some fixes --- .../typed_class_constants_type_error8.phpt | 10 ---------- .../typed_class_constants_type_success3.phpt | 18 ++++++++++++------ Zend/zend_compile.c | 2 +- Zend/zend_execute.c | 2 +- Zend/zend_execute.h | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) delete mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error8.phpt diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error8.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error8.phpt deleted file mode 100644 index a6c32913f8196..0000000000000 --- a/Zend/tests/type_declarations/typed_class_constants_type_error8.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -Typed class constants (type not allowed; static) ---FILE-- - ---EXPECTF-- -Fatal error: Class constant A::CONST1 cannot have type static in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt index b99866de310d7..5b3a374947655 100644 --- a/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt @@ -1,23 +1,29 @@ --TEST-- -Typed class constants (declaration; enums) +Typed enum constants (self/static) --FILE-- ---EXPECTF-- +--EXPECT-- +enum(E::Foo) enum(E::Foo) enum(E::Foo) enum(E::Foo) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 226895027c553..5b8aa00d4df51 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7716,7 +7716,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); - if (type_mask != MAY_BE_ANY && (type_mask & (MAY_BE_CALLABLE|MAY_BE_VOID|MAY_BE_NEVER|MAY_BE_STATIC))) { + if (type_mask != MAY_BE_ANY && (type_mask & (MAY_BE_CALLABLE|MAY_BE_VOID|MAY_BE_NEVER))) { zend_string *type_str = zend_type_to_string(type); zend_error_noreturn(E_COMPILE_ERROR, "Class constant %s::%s cannot have type %s", diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index d4837edeb35bc..a4ff04357f10d 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1465,7 +1465,7 @@ static ZEND_COLD void zend_verify_missing_return_type(const zend_function *zf) zend_verify_return_error(zf, NULL); } -zend_bool zend_never_inline zend_verify_class_constant_type(const zend_class_constant *c, zval *constant) +bool zend_never_inline zend_verify_class_constant_type(const zend_class_constant *c, zval *constant) { if (zend_check_type((zend_type *) &c->type, constant, NULL, NULL, /* is_return_type */ 1, /* is_internal_arg */ 0)) { return 1; diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index c343e5e724c76..49079e75744e4 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -485,7 +485,7 @@ ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call); #define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS) -zend_bool zend_verify_class_constant_type(const zend_class_constant *c, zval *constant); +bool zend_verify_class_constant_type(const zend_class_constant *c, zval *constant); ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant); ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict); From 05ee202372be9f9cdeac5760a8ec10e739a1375b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 27 Feb 2023 09:53:09 +0100 Subject: [PATCH 03/11] Add support for trait constants --- ...ed_class_constants_inheritance_error3.phpt | 18 ++++++++ ...ed_class_constants_inheritance_error4.phpt | 18 ++++++++ ...ed_class_constants_inheritance_error5.phpt | 18 ++++++++ ..._class_constants_inheritance_success4.phpt | 39 ++++++++++++++++ ..._class_constants_inheritance_success5.phpt | 26 +++++++++++ Zend/zend_inheritance.c | 45 ++++++++++++++----- 6 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_error3.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_error4.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_error5.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_success4.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_inheritance_success5.phpt diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_error3.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_error3.phpt new file mode 100644 index 0000000000000..82c3154e9bfba --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_error3.phpt @@ -0,0 +1,18 @@ +--TEST-- +Typed class constants (incompatible composition; traits) +--FILE-- + +--EXPECTF-- +Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_error4.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_error4.phpt new file mode 100644 index 0000000000000..55929d8953119 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_error4.phpt @@ -0,0 +1,18 @@ +--TEST-- +Typed class constants (incompatible covariant composition; traits) +--FILE-- + +--EXPECTF-- +Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_error5.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_error5.phpt new file mode 100644 index 0000000000000..7068d9df7cbb7 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_error5.phpt @@ -0,0 +1,18 @@ +--TEST-- +Typed class constants (incompatible contravariant composition; traits) +--FILE-- + +--EXPECTF-- +Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_success4.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_success4.phpt new file mode 100644 index 0000000000000..faacb0d55e49c --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_success4.phpt @@ -0,0 +1,39 @@ +--TEST-- +Typed class constants (composition; traits) +--FILE-- + +--EXPECT-- +int(1) +array(0) { +} +enum(E::Case1) +object(stdClass)#1 (0) { +} diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_success5.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_success5.phpt new file mode 100644 index 0000000000000..7f59dc1be35aa --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_success5.phpt @@ -0,0 +1,26 @@ +--TEST-- +Typed class constants (redefinition; interfaces and traits) +--FILE-- + +--EXPECT-- +enum(E::Case1) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 08595f126b989..3f88dbb5f0147 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2253,8 +2253,24 @@ static zend_class_entry* find_first_constant_definition(zend_class_entry *ce, ze } /* }}} */ -static bool do_trait_constant_check(zend_class_entry *ce, zend_class_constant *trait_constant, zend_string *name, zend_class_entry **traits, size_t current_trait) /* {{{ */ -{ +static bool emit_incompatible_trait_constant_error( + zend_class_entry *ce, zend_class_constant *existing_constant, zend_class_constant *trait_constant, zend_string *name, + zend_class_entry **traits, size_t current_trait +) { + zend_error_noreturn(E_COMPILE_ERROR, + "%s and %s define the same constant (%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed", + ZSTR_VAL(find_first_constant_definition(ce, traits, current_trait, name, existing_constant->ce)->name), + ZSTR_VAL(trait_constant->ce->name), + ZSTR_VAL(name), + ZSTR_VAL(ce->name) + ); + + return false; +} + +static bool do_trait_constant_check( + zend_class_entry *ce, zend_class_constant *trait_constant, zend_string *name, zend_class_entry **traits, size_t current_trait +) { uint32_t flags_mask = ZEND_ACC_PPP_MASK | ZEND_ACC_FINAL; zval *zv = zend_hash_find_known_hash(&ce->constants_table, name); @@ -2265,21 +2281,28 @@ static bool do_trait_constant_check(zend_class_entry *ce, zend_class_constant *t zend_class_constant *existing_constant = Z_PTR_P(zv); - if ((ZEND_CLASS_CONST_FLAGS(trait_constant) & flags_mask) != (ZEND_CLASS_CONST_FLAGS(existing_constant) & flags_mask) || - !check_trait_property_or_constant_value_compatibility(ce, &trait_constant->value, &existing_constant->value)) { + if ((ZEND_CLASS_CONST_FLAGS(trait_constant) & flags_mask) != (ZEND_CLASS_CONST_FLAGS(existing_constant) & flags_mask)) { + return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + } + + if (ZEND_TYPE_IS_SET(trait_constant->type) != ZEND_TYPE_IS_SET(existing_constant->type)) { + return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + } else if (ZEND_TYPE_IS_SET(trait_constant->type)) { + inheritance_status status1 = zend_perform_covariant_type_check(ce, existing_constant->type, traits[current_trait], trait_constant->type); + inheritance_status status2 = zend_perform_covariant_type_check(traits[current_trait], trait_constant->type, ce, existing_constant->type); + if (status1 == INHERITANCE_ERROR || status2 == INHERITANCE_ERROR) { + return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + } + } + + if (!check_trait_property_or_constant_value_compatibility(ce, &trait_constant->value, &existing_constant->value)) { /* There is an existing constant of the same name, and it conflicts with the new one, so let's throw a fatal error */ - zend_error_noreturn(E_COMPILE_ERROR, - "%s and %s define the same constant (%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed", - ZSTR_VAL(find_first_constant_definition(ce, traits, current_trait, name, existing_constant->ce)->name), - ZSTR_VAL(trait_constant->ce->name), - ZSTR_VAL(name), - ZSTR_VAL(ce->name)); + return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); } /* There is an existing constant which is compatible with the new one, so no need to add it */ return false; } -/* }}} */ static void zend_do_traits_constant_binding(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */ { From 7b6f3ca4737204b07225edd090d060879446f2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 14 Mar 2023 09:26:45 +0100 Subject: [PATCH 04/11] Fixes --- .../typed_class_constants_type_error2.phpt | 8 +++++++ .../typed_class_constants_type_error3.phpt | 7 ++++++ .../typed_class_constants_type_error7.phpt | 7 ++++++ .../typed_class_constants_type_success1.phpt | 23 +++++++++++++++++++ .../typed_class_constants_type_success2.phpt | 15 ++++++++++++ .../typed_class_constants_type_success3.phpt | 8 +++++++ Zend/zend_execute.c | 5 ++++ Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 12 +++++----- 9 files changed, 80 insertions(+), 7 deletions(-) diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error2.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error2.phpt index c4633ec888f16..8acffaa38399f 100644 --- a/Zend/tests/type_declarations/typed_class_constants_type_error2.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_type_error2.phpt @@ -13,6 +13,14 @@ try { } catch (TypeError $exception) { echo $exception->getMessage() . "\n"; } + +try { + var_dump(A::CONST1); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + ?> --EXPECT-- Cannot assign string to class constant A::CONST1 of type int +Cannot assign string to class constant A::CONST1 of type int diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error3.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error3.phpt index 661b854771a4c..1e6ab277dec8b 100644 --- a/Zend/tests/type_declarations/typed_class_constants_type_error3.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_type_error3.phpt @@ -8,6 +8,12 @@ class A { define('C', new stdClass); +try { + var_dump(A::CONST1); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + try { var_dump(A::CONST1); } catch (TypeError $exception) { @@ -16,3 +22,4 @@ try { ?> --EXPECT-- Cannot assign stdClass to class constant A::CONST1 of type string +Cannot assign stdClass to class constant A::CONST1 of type string diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error7.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error7.phpt index 09021e0e27821..6fce750166305 100644 --- a/Zend/tests/type_declarations/typed_class_constants_type_error7.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_type_error7.phpt @@ -8,6 +8,12 @@ class A { define("C", new stdClass); +try { + var_dump(A::CONST1); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + try { var_dump(A::CONST1); } catch (TypeError $exception) { @@ -16,3 +22,4 @@ try { ?> --EXPECT-- Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable +Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable diff --git a/Zend/tests/type_declarations/typed_class_constants_type_success1.phpt b/Zend/tests/type_declarations/typed_class_constants_type_success1.phpt index 8c1b54a3c9dd2..61e82637d9ada 100644 --- a/Zend/tests/type_declarations/typed_class_constants_type_success1.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_type_success1.phpt @@ -17,27 +17,50 @@ class A { } var_dump(A::CONST1); +var_dump(A::CONST1); +var_dump(A::CONST2); var_dump(A::CONST2); var_dump(A::CONST3); +var_dump(A::CONST3); var_dump(A::CONST4); +var_dump(A::CONST4); +var_dump(A::CONST5); var_dump(A::CONST5); var_dump(A::CONST6); +var_dump(A::CONST6); var_dump(A::CONST7); +var_dump(A::CONST7); +var_dump(A::CONST8); var_dump(A::CONST8); var_dump(A::CONST9); +var_dump(A::CONST9); var_dump(A::CONST10); +var_dump(A::CONST10); +var_dump(A::CONST11); var_dump(A::CONST11); ?> --EXPECT-- NULL +NULL bool(false) +bool(false) +bool(true) +bool(true) bool(true) bool(true) int(0) +int(0) +float(3.14) float(3.14) float(3) +float(3) string(0) "" +string(0) "" +array(0) { +} array(0) { } string(0) "" +string(0) "" +NULL NULL diff --git a/Zend/tests/type_declarations/typed_class_constants_type_success2.phpt b/Zend/tests/type_declarations/typed_class_constants_type_success2.phpt index 809ec90d7fb0b..1359624452355 100644 --- a/Zend/tests/type_declarations/typed_class_constants_type_success2.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_type_success2.phpt @@ -19,10 +19,15 @@ class B implements Stringable { const C = new B(); var_dump(A::CONST1); +var_dump(A::CONST1); +var_dump(A::CONST2); var_dump(A::CONST2); var_dump(A::CONST3); +var_dump(A::CONST3); +var_dump(A::CONST4); var_dump(A::CONST4); var_dump(A::CONST5); +var_dump(A::CONST5); ?> --EXPECTF-- object(B)#%d (%d) { @@ -35,3 +40,13 @@ object(B)#%d (%d) { } object(B)#%d (%d) { } +object(B)#%d (%d) { +} +object(B)#%d (%d) { +} +object(B)#%d (%d) { +} +object(B)#%d (%d) { +} +object(B)#%d (%d) { +} diff --git a/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt index 5b3a374947655..76555d11a6d16 100644 --- a/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt @@ -18,8 +18,12 @@ class A { } var_dump(A::ENUM_CONST); +var_dump(A::ENUM_CONST); +var_dump(A::CONST1); var_dump(A::CONST1); var_dump(A::CONST2); +var_dump(A::CONST2); +var_dump(A::CONST3); var_dump(A::CONST3); ?> --EXPECT-- @@ -27,3 +31,7 @@ enum(E::Foo) enum(E::Foo) enum(E::Foo) enum(E::Foo) +enum(E::Foo) +enum(E::Foo) +enum(E::Foo) +enum(E::Foo) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index a4ff04357f10d..7d56c3ad4736c 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -822,6 +822,11 @@ ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool s ZEND_COLD zend_never_inline void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant) { + /* we _may_ land here in case reading already errored and runtime cache thus has not been updated (i.e. it contains a valid but unrelated info) */ + if (EG(exception)) { + return; + } + zend_string *type_str = zend_type_to_string(c->type); zend_type_error("Cannot assign %s to class constant %s::%s of type %s", diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 3b293e62e6c5a..86e3363f8f86e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5984,6 +5984,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { ZVAL_UNDEF(EX_VAR(opline->result.var)); + FREE_OP2(); HANDLE_EXCEPTION(); } } @@ -6006,7 +6007,6 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } } if (OP2_TYPE == IS_CONST) { - value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 050e026c5fca0..13601b378d5c9 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7258,6 +7258,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); } } @@ -7280,7 +7281,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if (IS_CONST == IS_CONST) { - value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -8420,6 +8420,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { ZVAL_UNDEF(EX_VAR(opline->result.var)); + FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); } } @@ -8442,7 +8443,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if ((IS_TMP_VAR|IS_VAR|IS_CV) == IS_CONST) { - value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -25093,6 +25093,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); } } @@ -25115,7 +25116,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if (IS_CONST == IS_CONST) { - value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -25664,6 +25664,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { ZVAL_UNDEF(EX_VAR(opline->result.var)); + FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); } } @@ -25686,7 +25687,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if ((IS_TMP_VAR|IS_VAR|IS_CV) == IS_CONST) { - value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -34257,6 +34257,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); } } @@ -34279,7 +34280,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if (IS_CONST == IS_CONST) { - value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { @@ -34618,6 +34618,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { ZVAL_UNDEF(EX_VAR(opline->result.var)); + FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); } } @@ -34640,7 +34641,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if ((IS_TMP_VAR|IS_VAR|IS_CV) == IS_CONST) { - value = &c->value; CACHE_POLYMORPHIC_PTR(opline->extended_value, ce, value); } } else { From 6d51940d447449659e62bd758803ba6f3ce7f4c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 21 Mar 2023 01:24:34 +0100 Subject: [PATCH 05/11] Newer fixes --- Zend/tests/enum/name-property.phpt | 4 +- .../typed_class_constants_type_error8.phpt | 40 +++++++++++ .../typed_class_constants_type_error9.phpt | 25 +++++++ .../typed_class_constants_type_success3.phpt | 6 ++ Zend/zend_API.c | 40 +++++++++-- Zend/zend_API.h | 1 + Zend/zend_constants.c | 2 +- Zend/zend_execute.c | 67 +++++++++++-------- Zend/zend_execute.h | 2 +- Zend/zend_inheritance.c | 10 ++- Zend/zend_vm_def.h | 11 +-- Zend/zend_vm_execute.h | 66 ++---------------- ext/opcache/ZendAccelerator.c | 5 +- ext/opcache/zend_file_cache.c | 9 ++- ext/opcache/zend_persist.c | 2 + ext/opcache/zend_persist_calc.c | 2 + ext/reflection/php_reflection.c | 15 +++-- ext/reflection/php_reflection.stub.php | 2 +- ext/reflection/php_reflection_arginfo.h | 2 +- ...eflectionClassConstant_getValue_typed.phpt | 39 +++++++++++ .../ReflectionClass_getConstant_typed.phpt | 29 ++++++++ .../tests/ReflectionClass_toString_006.phpt | 32 +++++++++ .../tests/ReflectionClass_toString_007.phpt | 20 ++++++ ...roperties_with_typed_class_constants1.phpt | 24 +++++++ .../constant_with_typed_class_constant.phpt | 25 +++++++ 25 files changed, 357 insertions(+), 123 deletions(-) create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error8.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error9.phpt create mode 100644 ext/reflection/tests/ReflectionClassConstant_getValue_typed.phpt create mode 100644 ext/reflection/tests/ReflectionClass_getConstant_typed.phpt create mode 100644 ext/reflection/tests/ReflectionClass_toString_006.phpt create mode 100644 ext/reflection/tests/ReflectionClass_toString_007.phpt create mode 100644 ext/reflection/tests/static_properties_with_typed_class_constants1.phpt create mode 100644 ext/standard/tests/constant_with_typed_class_constant.phpt diff --git a/Zend/tests/enum/name-property.phpt b/Zend/tests/enum/name-property.phpt index a493c752c95ff..2197f235bbb2d 100644 --- a/Zend/tests/enum/name-property.phpt +++ b/Zend/tests/enum/name-property.phpt @@ -33,14 +33,14 @@ array(1) { string(3) "Bar" array(2) { [0]=> - object(ReflectionProperty)#4 (2) { + object(ReflectionProperty)#3 (2) { ["name"]=> string(4) "name" ["class"]=> string(6) "IntFoo" } [1]=> - object(ReflectionProperty)#5 (2) { + object(ReflectionProperty)#4 (2) { ["name"]=> string(5) "value" ["class"]=> diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error8.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error8.phpt new file mode 100644 index 0000000000000..cbd3720a5ea4e --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error8.phpt @@ -0,0 +1,40 @@ +--TEST-- +Typed class constants (type mismatch; runtime) +--FILE-- +getMessage() . "\n"; +} + +try { + var_dump(A::CONST2); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +try { + var_dump(A::CONST1); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +try { + var_dump(A::CONST1); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} +?> +--EXPECT-- +Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable +Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable +Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable +Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error9.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error9.phpt new file mode 100644 index 0000000000000..b0f42b2078a5b --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error9.phpt @@ -0,0 +1,25 @@ +--TEST-- +Typed class constants (type coercion is unsupported) +--FILE-- +getMessage() . "\n"; +} +?> +--EXPECT-- +Cannot assign S to class constant A::S of type string diff --git a/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt index 76555d11a6d16..fba08013d2adf 100644 --- a/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt @@ -6,6 +6,7 @@ enum E { public const E CONST1 = E::Foo; public const self CONST2 = E::Foo; public const static CONST3 = E::Foo; + public const static|stdClass CONST4 = E::Foo; case Foo; } @@ -15,6 +16,7 @@ class A { public const E CONST1 = E::CONST1; public const E CONST2 = E::CONST2; public const E CONST3 = E::CONST3; + public const E CONST4 = E::CONST4; } var_dump(A::ENUM_CONST); @@ -25,6 +27,8 @@ var_dump(A::CONST2); var_dump(A::CONST2); var_dump(A::CONST3); var_dump(A::CONST3); +var_dump(A::CONST4); +var_dump(A::CONST4); ?> --EXPECT-- enum(E::Foo) @@ -35,3 +39,5 @@ enum(E::Foo) enum(E::Foo) enum(E::Foo) enum(E::Foo) +enum(E::Foo) +enum(E::Foo) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 0af247fed7c57..452d9ca0cd50f 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1412,6 +1412,33 @@ static zend_result update_property(zval *val, zend_property_info *prop_info) { return zval_update_constant_ex(val, prop_info->ce); } +zend_result zend_update_class_constant(zend_class_constant *c, zval *value, zend_class_entry *scope) +{ + if (EXPECTED(!ZEND_TYPE_IS_SET(c->type) || ZEND_TYPE_PURE_MASK(c->type) == MAY_BE_ANY)) { + return zval_update_constant_ex(value, scope); + } + + zval tmp; + + ZVAL_COPY_OR_DUP(&tmp, value); + zend_result result = zval_update_constant_ex(&tmp, scope); + if (result == FAILURE) { + zval_ptr_dtor(&tmp); + return FAILURE; + } + + if (UNEXPECTED(!zend_verify_class_constant_type(c, &tmp))) { + zval_ptr_dtor(&tmp); + return FAILURE; + } + + zval_ptr_dtor(value); + ZVAL_COPY_OR_DUP(value, &tmp); + zval_ptr_dtor(&tmp); + + return SUCCESS; +} + ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /* {{{ */ { zend_class_mutable_data *mutable_data = NULL; @@ -1470,11 +1497,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / } val = &c->value; - if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { - return FAILURE; - } - - if (ZEND_TYPE_IS_SET(c->type) && UNEXPECTED(!zend_verify_class_constant_type(c, val))) { + if (UNEXPECTED(zend_update_class_constant(c, val, c->ce) != SUCCESS)) { return FAILURE; } } @@ -4555,18 +4578,21 @@ ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry zval_make_interned_string(value); } + if (!ZSTR_IS_INTERNED(name)) { + name = zend_new_interned_string(zend_string_copy(name)); + } + if (ce->type == ZEND_INTERNAL_CLASS) { c = pemalloc(sizeof(zend_class_constant), 1); } else { c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); } - ZVAL_COPY_VALUE(&c->value, value); ZEND_CLASS_CONST_FLAGS(c) = flags; - c->name = name; c->doc_comment = doc_comment; c->attributes = NULL; c->ce = ce; + c->name = name; c->type = type; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index f34eadd233b0b..4b5f3680a4eca 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -439,6 +439,7 @@ ZEND_API void zend_declare_class_constant_double(zend_class_entry *ce, const cha ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length); ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value); +zend_result zend_update_class_constant(zend_class_constant *c, zval *value, zend_class_entry *scope); ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type); ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index f4ed521605968..dc5631d142383 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -361,7 +361,7 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * } MARK_CONSTANT_VISITED(ret_constant); - ret = zval_update_constant_ex(ret_constant, c->ce); + ret = zend_update_class_constant(c, ret_constant, c->ce); RESET_CONSTANT_VISITED(ret_constant); if (UNEXPECTED(ret != SUCCESS)) { diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 7d56c3ad4736c..ffbdb0e8b7ff8 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -822,11 +822,6 @@ ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool s ZEND_COLD zend_never_inline void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant) { - /* we _may_ land here in case reading already errored and runtime cache thus has not been updated (i.e. it contains a valid but unrelated info) */ - if (EG(exception)) { - return; - } - zend_string *type_str = zend_type_to_string(c->type); zend_type_error("Cannot assign %s to class constant %s::%s of type %s", @@ -923,7 +918,7 @@ static const zend_class_entry *resolve_single_class_type(zend_string *name, cons } static zend_always_inline const zend_class_entry *zend_ce_from_type( - const zend_property_info *info, const zend_type *type) { + const zend_class_entry *class_ce, const zend_type *type) { ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*type)); zend_string *name = ZEND_TYPE_NAME(*type); if (ZSTR_HAS_CE_CACHE(name)) { @@ -933,17 +928,17 @@ static zend_always_inline const zend_class_entry *zend_ce_from_type( } return ce; } - return resolve_single_class_type(name, info->ce); + return resolve_single_class_type(name, class_ce); } -static bool zend_check_intersection_for_property_class_type(zend_type_list *intersection_type_list, - const zend_property_info *info, const zend_class_entry *object_ce) +static bool zend_check_intersection_for_property_or_class_constant_class_type(zend_type_list *intersection_type_list, + const zend_class_entry *class_ce, const zend_class_entry *object_ce) { zend_type *list_type; ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - const zend_class_entry *ce = zend_ce_from_type(info, list_type); + const zend_class_entry *ce = zend_ce_from_type(class_ce, list_type); if (!ce || !instanceof_function(object_ce, ce)) { return false; } @@ -951,32 +946,34 @@ static bool zend_check_intersection_for_property_class_type(zend_type_list *inte return true; } -static bool zend_check_and_resolve_property_class_type( - const zend_property_info *info, const zend_class_entry *object_ce) { - if (ZEND_TYPE_HAS_LIST(info->type)) { +static bool zend_check_and_resolve_property_or_class_constant_class_type( + zend_type type, const zend_class_entry *class_ce, const zend_class_entry *object_ce) { + if (ZEND_TYPE_HAS_LIST(type)) { zend_type *list_type; - if (ZEND_TYPE_IS_INTERSECTION(info->type)) { - return zend_check_intersection_for_property_class_type( - ZEND_TYPE_LIST(info->type), info, object_ce); + if (ZEND_TYPE_IS_INTERSECTION(type)) { + return zend_check_intersection_for_property_or_class_constant_class_type( + ZEND_TYPE_LIST(type), class_ce, object_ce); } else { - ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) { + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { - if (zend_check_intersection_for_property_class_type( - ZEND_TYPE_LIST(*list_type), info, object_ce)) { + if (zend_check_intersection_for_property_or_class_constant_class_type( + ZEND_TYPE_LIST(*list_type), class_ce, object_ce)) { return true; } continue; } ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - const zend_class_entry *ce = zend_ce_from_type(info, list_type); + const zend_class_entry *ce = zend_ce_from_type(class_ce, list_type); if (ce && instanceof_function(object_ce, ce)) { return true; } } ZEND_TYPE_LIST_FOREACH_END(); return false; } + } else if ((ZEND_TYPE_PURE_MASK(type) & MAY_BE_STATIC)) { + return instanceof_function(object_ce, class_ce); } else { - const zend_class_entry *ce = zend_ce_from_type(info, &info->type); + const zend_class_entry *ce = zend_ce_from_type(class_ce, &type); return ce && instanceof_function(object_ce, ce); } } @@ -989,7 +986,7 @@ static zend_always_inline bool i_zend_check_property_type(const zend_property_in } if (ZEND_TYPE_IS_COMPLEX(info->type) && Z_TYPE_P(property) == IS_OBJECT - && zend_check_and_resolve_property_class_type(info, Z_OBJCE_P(property))) { + && zend_check_and_resolve_property_or_class_constant_class_type(info->type, info->ce, Z_OBJCE_P(property))) { return 1; } @@ -1470,15 +1467,31 @@ static ZEND_COLD void zend_verify_missing_return_type(const zend_function *zf) zend_verify_return_error(zf, NULL); } -bool zend_never_inline zend_verify_class_constant_type(const zend_class_constant *c, zval *constant) +static zend_always_inline bool zend_check_class_constant_type(zend_class_constant *c, zval *constant) { - if (zend_check_type((zend_type *) &c->type, constant, NULL, NULL, /* is_return_type */ 1, /* is_internal_arg */ 0)) { + ZEND_ASSERT(!Z_ISREF_P(constant)); + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(c->type, Z_TYPE_P(constant)))) { return 1; } - zend_verify_class_constant_type_error(c, constant); + if (((ZEND_TYPE_PURE_MASK(c->type) & MAY_BE_STATIC) || ZEND_TYPE_IS_COMPLEX(c->type)) && Z_TYPE_P(constant) == IS_OBJECT + && zend_check_and_resolve_property_or_class_constant_class_type(c->type, c->ce, Z_OBJCE_P(constant))) { + return 1; + } - return 0; + uint32_t type_mask = ZEND_TYPE_FULL_MASK(c->type); + ZEND_ASSERT(!(type_mask & (MAY_BE_CALLABLE|MAY_BE_NEVER|MAY_BE_VOID))); + return zend_verify_scalar_type_hint(type_mask, constant, 1, 0); +} + +ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_constant *c, zval *constant) +{ + if (!zend_check_class_constant_type(c, constant)) { + zend_verify_class_constant_type_error(c, constant); + return 0; + } + + return 1; } static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_object_as_array(void) @@ -3499,7 +3512,7 @@ static zend_always_inline int i_zend_verify_type_assignable_zval( } if (ZEND_TYPE_IS_COMPLEX(type) && zv_type == IS_OBJECT - && zend_check_and_resolve_property_class_type(info, Z_OBJCE_P(zv))) { + && zend_check_and_resolve_property_or_class_constant_class_type(info->type, info->ce, Z_OBJCE_P(zv))) { return 1; } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 49079e75744e4..316bc2d4c3b44 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -485,7 +485,7 @@ ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call); #define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS) -bool zend_verify_class_constant_type(const zend_class_constant *c, zval *constant); +ZEND_API bool zend_verify_class_constant_type(zend_class_constant *c, zval *constant); ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant); ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 3f88dbb5f0147..429723492acdf 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1359,7 +1359,7 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en } /* }}} */ -inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child) +static inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child) { if (!ZEND_TYPE_IS_SET(child->type)) { return INHERITANCE_ERROR; @@ -1663,12 +1663,16 @@ static zend_always_inline bool check_trait_property_or_constant_value_compatibil /* if any of the values is a constant, we try to resolve it */ if (UNEXPECTED(Z_TYPE_P(op1) == IS_CONSTANT_AST)) { ZVAL_COPY_OR_DUP(&op1_tmp, op1); - zval_update_constant_ex(&op1_tmp, ce); + if (UNEXPECTED(zval_update_constant_ex(&op1_tmp, ce) != SUCCESS)) { + return false; + } op1 = &op1_tmp; } if (UNEXPECTED(Z_TYPE_P(op2) == IS_CONSTANT_AST)) { ZVAL_COPY_OR_DUP(&op2_tmp, op2); - zval_update_constant_ex(&op2_tmp, ce); + if (UNEXPECTED(zval_update_constant_ex(&op2_tmp, ce) != SUCCESS)) { + return false; + } op2 = &op2_tmp; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 86e3363f8f86e..9f9313c8ea6ce 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5981,14 +5981,6 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO HANDLE_EXCEPTION(); } - if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - FREE_OP2(); - HANDLE_EXCEPTION(); - } - } - value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -5999,8 +5991,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); - if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP2(); HANDLE_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 13601b378d5c9..151f4f2fdc2cf 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7255,14 +7255,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS HANDLE_EXCEPTION(); } - if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - - HANDLE_EXCEPTION(); - } - } - value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -7273,8 +7265,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); - if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -8417,14 +8408,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS HANDLE_EXCEPTION(); } - if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - FREE_OP(opline->op2_type, opline->op2.var); - HANDLE_EXCEPTION(); - } - } - value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -8435,8 +8418,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); - if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); @@ -25090,14 +25072,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ HANDLE_EXCEPTION(); } - if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - - HANDLE_EXCEPTION(); - } - } - value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -25108,8 +25082,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); - if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -25661,14 +25634,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ HANDLE_EXCEPTION(); } - if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - FREE_OP(opline->op2_type, opline->op2.var); - HANDLE_EXCEPTION(); - } - } - value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -25679,8 +25644,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); - if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); @@ -34254,14 +34218,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS HANDLE_EXCEPTION(); } - if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - - HANDLE_EXCEPTION(); - } - } - value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -34272,8 +34228,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); - if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -34615,14 +34570,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS HANDLE_EXCEPTION(); } - if (UNEXPECTED(!(c->ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(c->ce)) != SUCCESS) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - FREE_OP(opline->op2_type, opline->op2.var); - HANDLE_EXCEPTION(); - } - } - value = &c->value; // Enums require loading of all class constants to build the backed enum table if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -34633,8 +34580,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); - if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 35aa00516b35c..3d3cade1f14df 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -720,6 +720,9 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int if (Z_TYPE(c->value) == IS_STRING) { ZVAL_STR(&c->value, new_interned_string(Z_STR(c->value))); } + if (c->name) { + c->name = new_interned_string(c->name); + } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); @@ -3757,7 +3760,7 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) ZEND_HASH_MAP_FOREACH_PTR(&ce->constants_table, c) { val = &c->value; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) { + if (EXPECTED(zend_update_class_constant(c, val, c->ce) == SUCCESS)) { was_changed = changed = true; } else { ok = false; diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index e1307c8ead04a..56e183d3830ee 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -687,17 +687,18 @@ static void zend_file_cache_serialize_class_constant(zval *z c = Z_PTR_P(zv); UNSERIALIZE_PTR(c); - ZEND_ASSERT(c->ce != NULL); + ZEND_ASSERT(c->ce != NULL && c->name != NULL); if (!IS_SERIALIZED(c->ce)) { SERIALIZE_PTR(c->ce); zend_file_cache_serialize_zval(&c->value, script, info, buf); - + SERIALIZE_STR(c->name); if (c->doc_comment) { SERIALIZE_STR(c->doc_comment); } SERIALIZE_ATTRIBUTES(c->attributes); + zend_file_cache_serialize_type(&c->type, script, info, buf); } } } @@ -1521,16 +1522,18 @@ static void zend_file_cache_unserialize_class_constant(zval * UNSERIALIZE_PTR(Z_PTR_P(zv)); c = Z_PTR_P(zv); - ZEND_ASSERT(c->ce != NULL); + ZEND_ASSERT(c->ce != NULL && c->name != NULL); if (!IS_UNSERIALIZED(c->ce)) { UNSERIALIZE_PTR(c->ce); zend_file_cache_unserialize_zval(&c->value, script, buf); + UNSERIALIZE_STR(c->name); if (c->doc_comment) { UNSERIALIZE_STR(c->doc_comment); } UNSERIALIZE_ATTRIBUTES(c->attributes); + zend_file_cache_unserialize_type(&c->type, c->ce, script, buf); } } } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index d7c2d63339107..5c1acd73ccc8b 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -816,6 +816,7 @@ static void zend_persist_class_constant(zval *zv) } c = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_class_constant)); zend_persist_zval(&c->value); + zend_accel_store_interned_string(c->name); ce = zend_shared_alloc_get_xlat_entry(c->ce); if (ce) { c->ce = ce; @@ -840,6 +841,7 @@ static void zend_persist_class_constant(zval *zv) if (c->attributes) { c->attributes = zend_persist_attributes(c->attributes); } + zend_persist_type(&c->type); } zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 4e3af0d68c653..392be02088911 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -393,12 +393,14 @@ static void zend_persist_class_constant_calc(zval *zv) zend_shared_alloc_register_xlat_entry(c, c); ADD_SIZE(sizeof(zend_class_constant)); zend_persist_zval_calc(&c->value); + ADD_INTERNED_STRING(c->name); if (ZCG(accel_directives).save_comments && c->doc_comment) { ADD_STRING(c->doc_comment); } if (c->attributes) { zend_persist_attributes_calc(c->attributes); } + zend_persist_type_calc(&c->type); } } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 01517e7801d8a..c70c9c24e33ed 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -558,13 +558,13 @@ static void _const_string(smart_str *str, char *name, zval *value, char *indent) /* {{{ _class_const_string */ static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char *indent) { - if (zval_update_constant_ex(&c->value, c->ce) == FAILURE) { + if (zend_update_class_constant(c, &c->value, c->ce) == FAILURE) { return; } const char *visibility = zend_visibility_string(ZEND_CLASS_CONST_FLAGS(c)); const char *final = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_FINAL ? "final " : ""; - const char *type = zend_zval_type_name(&c->value); + const char *type = ZEND_TYPE_IS_SET(c->type) ? ZSTR_VAL(zend_type_to_string(c->type)) : zend_zval_type_name(&c->value); smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", indent, final, visibility, type, name); if (Z_TYPE(c->value) == IS_ARRAY) { @@ -3921,7 +3921,10 @@ ZEND_METHOD(ReflectionClassConstant, getValue) GET_REFLECTION_OBJECT_PTR(ref); if (Z_TYPE(ref->value) == IS_CONSTANT_AST) { - zval_update_constant_ex(&ref->value, ref->ce); + zend_result result = zend_update_class_constant(ref, &ref->value, ref->ce); + if (result == FAILURE) { + RETURN_THROWS(); + } } ZVAL_COPY_OR_DUP(return_value, &ref->value); } @@ -4727,7 +4730,7 @@ ZEND_METHOD(ReflectionClass, getConstants) array_init(return_value); ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) { - if (UNEXPECTED(zval_update_constant_ex(&constant->value, constant->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(constant, &constant->value, constant->ce) != SUCCESS)) { RETURN_THROWS(); } @@ -4786,7 +4789,7 @@ ZEND_METHOD(ReflectionClass, getConstant) GET_REFLECTION_OBJECT_PTR(ce); constants_table = CE_CONSTANTS_TABLE(ce); ZEND_HASH_MAP_FOREACH_PTR(constants_table, c) { - if (UNEXPECTED(zval_update_constant_ex(&c->value, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, &c->value, c->ce) != SUCCESS)) { RETURN_THROWS(); } } ZEND_HASH_FOREACH_END(); @@ -6987,7 +6990,7 @@ ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue) if (Z_TYPE(ref->value) == IS_CONSTANT_AST) { zval_update_constant_ex(&ref->value, ref->ce); if (EG(exception)) { - return; + RETURN_THROWS(); } } diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 6757f7231f529..de0d83d244a85 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -346,7 +346,7 @@ public function getConstants(?int $filter = null): array {} public function getReflectionConstants(?int $filter = null): array {} /** @tentative-return-type */ - public function getConstant(string $name): mixed {} + public function getConstant(string $name): mixed {} // TODO throw exception when the constant doesn't exist /** @tentative-return-type */ public function getReflectionConstant(string $name): ReflectionClassConstant|false {} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index d483795f5ad97..ddc445f5206d3 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 613661b122b2f0845eb0d3e8bc220164834552f1 */ + * Stub hash: 75d10a475cce503d94bd8471764adf495f0ddd34 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) diff --git a/ext/reflection/tests/ReflectionClassConstant_getValue_typed.phpt b/ext/reflection/tests/ReflectionClassConstant_getValue_typed.phpt new file mode 100644 index 0000000000000..f964f4c146fbc --- /dev/null +++ b/ext/reflection/tests/ReflectionClassConstant_getValue_typed.phpt @@ -0,0 +1,39 @@ +--TEST-- +Test variations of getting typed class constant values +--FILE-- +getValue()); +echo $rc; + +$rc = new ReflectionClassConstant(B::class, 'CONST1'); +try { + $rc->getValue(); +} catch (TypeError $e) { + echo $e->getMessage() . "\n"; +} + +try { + echo $rc; +} catch (TypeError $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +object(stdClass)#1 (0) { +} +Constant [ public object CONST1 ] { Object } +Cannot assign stdClass to class constant B::CONST1 of type array +Cannot assign stdClass to class constant B::CONST1 of type array diff --git a/ext/reflection/tests/ReflectionClass_getConstant_typed.phpt b/ext/reflection/tests/ReflectionClass_getConstant_typed.phpt new file mode 100644 index 0000000000000..c63eaab5d9027 --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_getConstant_typed.phpt @@ -0,0 +1,29 @@ +--TEST-- +ReflectionClass::getConstant() with typed class constants +--FILE-- +getConstant("CONST1")); + +$rc = new ReflectionClass(D::class); +try { + $rc->getConstant("CONST1"); +} catch (TypeError $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +object(stdClass)#1 (0) { +} +Cannot assign stdClass to class constant D::CONST1 of type array diff --git a/ext/reflection/tests/ReflectionClass_toString_006.phpt b/ext/reflection/tests/ReflectionClass_toString_006.phpt new file mode 100644 index 0000000000000..b94467261f682 --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_toString_006.phpt @@ -0,0 +1,32 @@ +--TEST-- +Using ReflectionClass::__toString() with typed class constants +--FILE-- + +--EXPECTF-- +Class [ class Foo ] { + @@ %s 3-5 + + - Constants [1] { + Constant [ public ?int CONST1 ] { 1 } + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Methods [0] { + } +} diff --git a/ext/reflection/tests/ReflectionClass_toString_007.phpt b/ext/reflection/tests/ReflectionClass_toString_007.phpt new file mode 100644 index 0000000000000..fd3ed8f32e354 --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_toString_007.phpt @@ -0,0 +1,20 @@ +--TEST-- +Using ReflectionClass::__toString() with typed class constants when there is a type mismatch +--FILE-- +getMessage() . "\n"; +} + +?> +--EXPECT-- +Cannot assign stdClass to class constant Foo::CONST1 of type array diff --git a/ext/reflection/tests/static_properties_with_typed_class_constants1.phpt b/ext/reflection/tests/static_properties_with_typed_class_constants1.phpt new file mode 100644 index 0000000000000..cebfa1dddd3e0 --- /dev/null +++ b/ext/reflection/tests/static_properties_with_typed_class_constants1.phpt @@ -0,0 +1,24 @@ +--TEST-- +Typed class constant reflection +--FILE-- +getStaticProperties(); +} catch (TypeError $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Cannot assign string to class constant Foo::CONST1 of type int diff --git a/ext/standard/tests/constant_with_typed_class_constant.phpt b/ext/standard/tests/constant_with_typed_class_constant.phpt new file mode 100644 index 0000000000000..87a1c006426da --- /dev/null +++ b/ext/standard/tests/constant_with_typed_class_constant.phpt @@ -0,0 +1,25 @@ +--TEST-- +Calling constant() with a typed class constant +--FILE-- +getMessage() . "\n"; +} + +?> +--EXPECT-- +object(stdClass)#1 (0) { +} +Cannot assign stdClass to class constant Foo::CONST2 of type array From e4498cca47e409455adcad369cf955d5bf4e3109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 21 Mar 2023 20:43:29 +0100 Subject: [PATCH 06/11] New review round --- Zend/zend_API.c | 15 +++++------ Zend/zend_API.h | 2 +- Zend/zend_constants.c | 2 +- Zend/zend_execute.c | 44 ++++++++++++++++----------------- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 12 ++++----- ext/opcache/ZendAccelerator.c | 2 +- ext/reflection/php_reflection.c | 15 +++++++---- 8 files changed, 50 insertions(+), 44 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 452d9ca0cd50f..89f87b8557167 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1412,15 +1412,17 @@ static zend_result update_property(zval *val, zend_property_info *prop_info) { return zval_update_constant_ex(val, prop_info->ce); } -zend_result zend_update_class_constant(zend_class_constant *c, zval *value, zend_class_entry *scope) +ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, zend_class_entry *scope) { + ZEND_ASSERT(Z_TYPE(c->value) == IS_CONSTANT_AST); + if (EXPECTED(!ZEND_TYPE_IS_SET(c->type) || ZEND_TYPE_PURE_MASK(c->type) == MAY_BE_ANY)) { - return zval_update_constant_ex(value, scope); + return zval_update_constant_ex(&c->value, scope); } zval tmp; - ZVAL_COPY_OR_DUP(&tmp, value); + ZVAL_COPY(&tmp, &c->value); zend_result result = zval_update_constant_ex(&tmp, scope); if (result == FAILURE) { zval_ptr_dtor(&tmp); @@ -1432,9 +1434,8 @@ zend_result zend_update_class_constant(zend_class_constant *c, zval *value, zend return FAILURE; } - zval_ptr_dtor(value); - ZVAL_COPY_OR_DUP(value, &tmp); - zval_ptr_dtor(&tmp); + zval_ptr_dtor(&c->value); + ZVAL_COPY_VALUE(&c->value, &tmp); return SUCCESS; } @@ -1497,7 +1498,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / } val = &c->value; - if (UNEXPECTED(zend_update_class_constant(c, val, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { return FAILURE; } } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 4b5f3680a4eca..c6021fdf04375 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -439,7 +439,7 @@ ZEND_API void zend_declare_class_constant_double(zend_class_entry *ce, const cha ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length); ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value); -zend_result zend_update_class_constant(zend_class_constant *c, zval *value, zend_class_entry *scope); +ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, zend_class_entry *scope); ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type); ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index dc5631d142383..62d3ae8edd92a 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -361,7 +361,7 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * } MARK_CONSTANT_VISITED(ret_constant); - ret = zend_update_class_constant(c, ret_constant, c->ce); + ret = zend_update_class_constant(c, c->ce); RESET_CONSTANT_VISITED(ret_constant); if (UNEXPECTED(ret != SUCCESS)) { diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index ffbdb0e8b7ff8..a208b583ab394 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -918,7 +918,7 @@ static const zend_class_entry *resolve_single_class_type(zend_string *name, cons } static zend_always_inline const zend_class_entry *zend_ce_from_type( - const zend_class_entry *class_ce, const zend_type *type) { + const zend_class_entry *scope, const zend_type *type) { ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*type)); zend_string *name = ZEND_TYPE_NAME(*type); if (ZSTR_HAS_CE_CACHE(name)) { @@ -928,18 +928,18 @@ static zend_always_inline const zend_class_entry *zend_ce_from_type( } return ce; } - return resolve_single_class_type(name, class_ce); + return resolve_single_class_type(name, scope); } -static bool zend_check_intersection_for_property_or_class_constant_class_type(zend_type_list *intersection_type_list, - const zend_class_entry *class_ce, const zend_class_entry *object_ce) +static bool zend_check_intersection_for_property_or_class_constant_class_type( + const zend_class_entry *scope, zend_type_list *intersection_type_list, const zend_class_entry *value_ce) { zend_type *list_type; ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - const zend_class_entry *ce = zend_ce_from_type(class_ce, list_type); - if (!ce || !instanceof_function(object_ce, ce)) { + const zend_class_entry *ce = zend_ce_from_type(scope, list_type); + if (!ce || !instanceof_function(value_ce, ce)) { return false; } } ZEND_TYPE_LIST_FOREACH_END(); @@ -947,34 +947,34 @@ static bool zend_check_intersection_for_property_or_class_constant_class_type(ze } static bool zend_check_and_resolve_property_or_class_constant_class_type( - zend_type type, const zend_class_entry *class_ce, const zend_class_entry *object_ce) { - if (ZEND_TYPE_HAS_LIST(type)) { + const zend_class_entry *scope, zend_type member_type, const zend_class_entry *value_ce) { +if (ZEND_TYPE_HAS_LIST(member_type)) { zend_type *list_type; - if (ZEND_TYPE_IS_INTERSECTION(type)) { + if (ZEND_TYPE_IS_INTERSECTION(member_type)) { return zend_check_intersection_for_property_or_class_constant_class_type( - ZEND_TYPE_LIST(type), class_ce, object_ce); + scope, ZEND_TYPE_LIST(member_type), value_ce); } else { - ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(member_type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { if (zend_check_intersection_for_property_or_class_constant_class_type( - ZEND_TYPE_LIST(*list_type), class_ce, object_ce)) { + scope, ZEND_TYPE_LIST(*list_type), value_ce)) { return true; } continue; } ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - const zend_class_entry *ce = zend_ce_from_type(class_ce, list_type); - if (ce && instanceof_function(object_ce, ce)) { + const zend_class_entry *ce = zend_ce_from_type(scope, list_type); + if (ce && instanceof_function(value_ce, ce)) { return true; } } ZEND_TYPE_LIST_FOREACH_END(); return false; } - } else if ((ZEND_TYPE_PURE_MASK(type) & MAY_BE_STATIC)) { - return instanceof_function(object_ce, class_ce); + } else if ((ZEND_TYPE_PURE_MASK(member_type) & MAY_BE_STATIC)) { + return instanceof_function(value_ce, scope); } else { - const zend_class_entry *ce = zend_ce_from_type(class_ce, &type); - return ce && instanceof_function(object_ce, ce); + const zend_class_entry *ce = zend_ce_from_type(scope, &member_type); + return ce && instanceof_function(value_ce, ce); } } @@ -986,7 +986,7 @@ static zend_always_inline bool i_zend_check_property_type(const zend_property_in } if (ZEND_TYPE_IS_COMPLEX(info->type) && Z_TYPE_P(property) == IS_OBJECT - && zend_check_and_resolve_property_or_class_constant_class_type(info->type, info->ce, Z_OBJCE_P(property))) { + && zend_check_and_resolve_property_or_class_constant_class_type(info->ce, info->type, Z_OBJCE_P(property))) { return 1; } @@ -1475,13 +1475,13 @@ static zend_always_inline bool zend_check_class_constant_type(zend_class_constan } if (((ZEND_TYPE_PURE_MASK(c->type) & MAY_BE_STATIC) || ZEND_TYPE_IS_COMPLEX(c->type)) && Z_TYPE_P(constant) == IS_OBJECT - && zend_check_and_resolve_property_or_class_constant_class_type(c->type, c->ce, Z_OBJCE_P(constant))) { + && zend_check_and_resolve_property_or_class_constant_class_type(c->ce, c->type, Z_OBJCE_P(constant))) { return 1; } uint32_t type_mask = ZEND_TYPE_FULL_MASK(c->type); ZEND_ASSERT(!(type_mask & (MAY_BE_CALLABLE|MAY_BE_NEVER|MAY_BE_VOID))); - return zend_verify_scalar_type_hint(type_mask, constant, 1, 0); + return zend_verify_scalar_type_hint(type_mask, constant, true, false); } ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_constant *c, zval *constant) @@ -3512,7 +3512,7 @@ static zend_always_inline int i_zend_verify_type_assignable_zval( } if (ZEND_TYPE_IS_COMPLEX(type) && zv_type == IS_OBJECT - && zend_check_and_resolve_property_or_class_constant_class_type(info->type, info->ce, Z_OBJCE_P(zv))) { + && zend_check_and_resolve_property_or_class_constant_class_type(info->ce, info->type, Z_OBJCE_P(zv))) { return 1; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 9f9313c8ea6ce..9fb9da09c8847 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5991,7 +5991,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP2(); HANDLE_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 151f4f2fdc2cf..600d00d14d02c 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7265,7 +7265,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -8418,7 +8418,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); @@ -25082,7 +25082,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -25644,7 +25644,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); @@ -34228,7 +34228,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -34580,7 +34580,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, value, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 3d3cade1f14df..5be540bc1ca51 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3760,7 +3760,7 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) ZEND_HASH_MAP_FOREACH_PTR(&ce->constants_table, c) { val = &c->value; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - if (EXPECTED(zend_update_class_constant(c, val, c->ce) == SUCCESS)) { + if (EXPECTED(zend_update_class_constant(c, c->ce) == SUCCESS)) { was_changed = changed = true; } else { ok = false; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index c70c9c24e33ed..8e1f5d2096373 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -558,13 +558,14 @@ static void _const_string(smart_str *str, char *name, zval *value, char *indent) /* {{{ _class_const_string */ static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char *indent) { - if (zend_update_class_constant(c, &c->value, c->ce) == FAILURE) { + if (zend_update_class_constant(c, c->ce) == FAILURE) { return; } const char *visibility = zend_visibility_string(ZEND_CLASS_CONST_FLAGS(c)); const char *final = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_FINAL ? "final " : ""; - const char *type = ZEND_TYPE_IS_SET(c->type) ? ZSTR_VAL(zend_type_to_string(c->type)) : zend_zval_type_name(&c->value); + zend_string *type_str = ZEND_TYPE_IS_SET(c->type) ? zend_type_to_string(c->type) : NULL; + const char *type = type_str ? ZSTR_VAL(type_str) : zend_zval_type_name(&c->value); smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", indent, final, visibility, type, name); if (Z_TYPE(c->value) == IS_ARRAY) { @@ -578,6 +579,10 @@ static void _class_const_string(smart_str *str, char *name, zend_class_constant zend_tmp_string_release(tmp_value_str); } smart_str_appends(str, " }\n"); + + if (type_str) { + zend_string_release(type_str); + } } /* }}} */ @@ -3921,7 +3926,7 @@ ZEND_METHOD(ReflectionClassConstant, getValue) GET_REFLECTION_OBJECT_PTR(ref); if (Z_TYPE(ref->value) == IS_CONSTANT_AST) { - zend_result result = zend_update_class_constant(ref, &ref->value, ref->ce); + zend_result result = zend_update_class_constant(ref, ref->ce); if (result == FAILURE) { RETURN_THROWS(); } @@ -4730,7 +4735,7 @@ ZEND_METHOD(ReflectionClass, getConstants) array_init(return_value); ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) { - if (UNEXPECTED(zend_update_class_constant(constant, &constant->value, constant->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(constant, constant->ce) != SUCCESS)) { RETURN_THROWS(); } @@ -4789,7 +4794,7 @@ ZEND_METHOD(ReflectionClass, getConstant) GET_REFLECTION_OBJECT_PTR(ce); constants_table = CE_CONSTANTS_TABLE(ce); ZEND_HASH_MAP_FOREACH_PTR(constants_table, c) { - if (UNEXPECTED(zend_update_class_constant(c, &c->value, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { RETURN_THROWS(); } } ZEND_HASH_FOREACH_END(); From 715b5bd6bb9357b8233b509753dfe1e5453697c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Fri, 24 Mar 2023 16:32:41 +0100 Subject: [PATCH 07/11] Implement delayed variance checks and other fixes --- ...ed_class_constants_inheritance_error1.phpt | 2 +- ...ed_class_constants_inheritance_error2.phpt | 2 +- .../typed_class_constants_type_error10.phpt | 15 ++++ .../typed_class_constants_type_error11.phpt | 21 +++++ .../typed_class_constants_type_success3.phpt | 9 +++ Zend/zend_API.c | 7 +- Zend/zend_API.h | 2 +- Zend/zend_compile.h | 1 - Zend/zend_constants.c | 2 +- Zend/zend_execute.c | 10 +-- Zend/zend_execute.h | 4 +- Zend/zend_inheritance.c | 76 +++++++++++++++++-- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 12 +-- ext/opcache/ZendAccelerator.c | 8 +- ext/opcache/zend_file_cache.c | 6 +- ext/opcache/zend_persist.c | 1 - ext/opcache/zend_persist_calc.c | 1 - ext/reflection/php_reflection.c | 30 +++++--- 19 files changed, 159 insertions(+), 52 deletions(-) create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error10.phpt create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error11.phpt diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_error1.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_error1.phpt index 698d9934e03c5..d4bbe98d6f5ce 100644 --- a/Zend/tests/type_declarations/typed_class_constants_inheritance_error1.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_error1.phpt @@ -11,4 +11,4 @@ class B extends A { } ?> --EXPECTF-- -Fatal error: Declaration of B::CONST1 must be compatible with A::CONST1 in %s on line %d +Fatal error: Type of B::CONST1 must be compatible with A::CONST1 of type int in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_inheritance_error2.phpt b/Zend/tests/type_declarations/typed_class_constants_inheritance_error2.phpt index a1d9d2ad9f848..72fc4da0ffe2e 100644 --- a/Zend/tests/type_declarations/typed_class_constants_inheritance_error2.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_inheritance_error2.phpt @@ -11,4 +11,4 @@ class B extends A { } ?> --EXPECTF-- -Fatal error: Declaration of B::CONST1 must be compatible with A::CONST1 in %s on line %d +Fatal error: Type of B::CONST1 must be compatible with A::CONST1 of type int in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error10.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error10.phpt new file mode 100644 index 0000000000000..805edd9854f43 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error10.phpt @@ -0,0 +1,15 @@ +--TEST-- +Typed class constants (type error) +--FILE-- + +--EXPECTF-- +Fatal error: Type of A2::C must be compatible with A1::C of type ?B1 in %s on line %d diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error11.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error11.phpt new file mode 100644 index 0000000000000..714e5a00995cf --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error11.phpt @@ -0,0 +1,21 @@ +--TEST-- +Typed class constants (static type error) +--FILE-- +getMessage() . "\n"; +} + +?> +--EXPECT-- +Cannot assign E2 to class constant E1::C of type static diff --git a/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt index fba08013d2adf..cf3d7c9557202 100644 --- a/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt +++ b/Zend/tests/type_declarations/typed_class_constants_type_success3.phpt @@ -25,10 +25,15 @@ var_dump(A::CONST1); var_dump(A::CONST1); var_dump(A::CONST2); var_dump(A::CONST2); +var_dump(E::CONST3); +var_dump(E::CONST3); var_dump(A::CONST3); var_dump(A::CONST3); +var_dump(E::CONST4); +var_dump(E::CONST4); var_dump(A::CONST4); var_dump(A::CONST4); + ?> --EXPECT-- enum(E::Foo) @@ -41,3 +46,7 @@ enum(E::Foo) enum(E::Foo) enum(E::Foo) enum(E::Foo) +enum(E::Foo) +enum(E::Foo) +enum(E::Foo) +enum(E::Foo) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 89f87b8557167..af05631357a73 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1412,7 +1412,7 @@ static zend_result update_property(zval *val, zend_property_info *prop_info) { return zval_update_constant_ex(val, prop_info->ce); } -ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, zend_class_entry *scope) +ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, const zend_string *name, zend_class_entry *scope) { ZEND_ASSERT(Z_TYPE(c->value) == IS_CONSTANT_AST); @@ -1429,7 +1429,7 @@ ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, zend_cla return FAILURE; } - if (UNEXPECTED(!zend_verify_class_constant_type(c, &tmp))) { + if (UNEXPECTED(!zend_verify_class_constant_type(c, name, &tmp))) { zval_ptr_dtor(&tmp); return FAILURE; } @@ -1498,7 +1498,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / } val = &c->value; - if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, name, c->ce) != SUCCESS)) { return FAILURE; } } @@ -4593,7 +4593,6 @@ ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry c->doc_comment = doc_comment; c->attributes = NULL; c->ce = ce; - c->name = name; c->type = type; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index c6021fdf04375..9c1cf08aa78b3 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -439,7 +439,7 @@ ZEND_API void zend_declare_class_constant_double(zend_class_entry *ce, const cha ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length); ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value); -ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, zend_class_entry *scope); +ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, const zend_string *name, zend_class_entry *scope); ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type); ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index c9075858ecfda..0ef351ab274a6 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -404,7 +404,6 @@ typedef struct _zend_property_info { typedef struct _zend_class_constant { zval value; /* flags are stored in u2 */ - zend_string *name; zend_string *doc_comment; HashTable *attributes; zend_class_entry *ce; diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 62d3ae8edd92a..854f9c2116ee2 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -361,7 +361,7 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * } MARK_CONSTANT_VISITED(ret_constant); - ret = zend_update_class_constant(c, c->ce); + ret = zend_update_class_constant(c, constant_name, c->ce); RESET_CONSTANT_VISITED(ret_constant); if (UNEXPECTED(ret != SUCCESS)) { diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index a208b583ab394..a1f0fb8b1a3c3 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -820,12 +820,12 @@ ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool s return zend_verify_weak_scalar_type_hint(type_mask, arg); } -ZEND_COLD zend_never_inline void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant) +ZEND_COLD zend_never_inline void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant) { zend_string *type_str = zend_type_to_string(c->type); zend_type_error("Cannot assign %s to class constant %s::%s of type %s", - zend_zval_type_name(constant), ZSTR_VAL(c->ce->name), ZSTR_VAL(c->name), ZSTR_VAL(type_str)); + zend_zval_type_name(constant), ZSTR_VAL(c->ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str)); zend_string_release(type_str); } @@ -971,7 +971,7 @@ if (ZEND_TYPE_HAS_LIST(member_type)) { return false; } } else if ((ZEND_TYPE_PURE_MASK(member_type) & MAY_BE_STATIC)) { - return instanceof_function(value_ce, scope); + return value_ce == scope; } else { const zend_class_entry *ce = zend_ce_from_type(scope, &member_type); return ce && instanceof_function(value_ce, ce); @@ -1484,10 +1484,10 @@ static zend_always_inline bool zend_check_class_constant_type(zend_class_constan return zend_verify_scalar_type_hint(type_mask, constant, true, false); } -ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_constant *c, zval *constant) +ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant) { if (!zend_check_class_constant_type(c, constant)) { - zend_verify_class_constant_type_error(c, constant); + zend_verify_class_constant_type_error(c, name, constant); return 0; } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 316bc2d4c3b44..594561da1bb42 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -485,8 +485,8 @@ ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call); #define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS) -ZEND_API bool zend_verify_class_constant_type(zend_class_constant *c, zval *constant); -ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant); +ZEND_API bool zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant); +ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant); ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict); ZEND_COLD void zend_verify_property_type_error(const zend_property_info *info, const zval *property); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 429723492acdf..6fbfa4190e1ba 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -51,6 +51,9 @@ static void add_compatibility_obligation( static void add_property_compatibility_obligation( zend_class_entry *ce, const zend_property_info *child_prop, const zend_property_info *parent_prop); +static void add_class_constant_compatibility_obligation( + zend_class_entry *ce, const zend_class_constant *child_const, + const zend_class_constant *parent_const, const zend_string *const_name); static void ZEND_COLD emit_incompatible_method_error( const zend_function *child, zend_class_entry *child_scope, @@ -1359,8 +1362,22 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en } /* }}} */ +static void emit_incompatible_class_constant_error( + const zend_class_constant *child, const zend_class_constant *parent, const zend_string *const_name) { + zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce); + zend_error_noreturn(E_COMPILE_ERROR, + "Type of %s::%s must be compatible with %s::%s of type %s", + ZSTR_VAL(child->ce->name), + ZSTR_VAL(const_name), + ZSTR_VAL(parent->ce->name), + ZSTR_VAL(const_name), + ZSTR_VAL(type_str)); +} + static inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child) { + ZEND_ASSERT(ZEND_TYPE_IS_SET(parent->type)); + if (!ZEND_TYPE_IS_SET(child->type)) { return INHERITANCE_ERROR; } @@ -1393,10 +1410,11 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa } if (!(ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE) && UNEXPECTED(ZEND_TYPE_IS_SET(parent_const->type))) { - if (class_constant_types_compatible(parent_const, c) == INHERITANCE_ERROR) { - zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s::%s must be compatible with %s::%s", - ZSTR_VAL(c->ce->name), ZSTR_VAL(name), ZSTR_VAL(parent_const->ce->name), ZSTR_VAL(name) - ); + inheritance_status status = class_constant_types_compatible(parent_const, c); + if (status == INHERITANCE_ERROR) { + emit_incompatible_class_constant_error(c, parent_const, name); + } else if (status == INHERITANCE_UNRESOLVED) { + add_class_constant_compatibility_obligation(ce, c, parent_const, name); } } } else if (!(ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE)) { @@ -2557,7 +2575,8 @@ typedef struct { enum { OBLIGATION_DEPENDENCY, OBLIGATION_COMPATIBILITY, - OBLIGATION_PROPERTY_COMPATIBILITY + OBLIGATION_PROPERTY_COMPATIBILITY, + OBLIGATION_CLASS_CONSTANT_COMPATIBILITY } type; union { zend_class_entry *dependency_ce; @@ -2573,6 +2592,11 @@ typedef struct { const zend_property_info *parent_prop; const zend_property_info *child_prop; }; + struct { + const zend_string *const_name; + const zend_class_constant *parent_const; + const zend_class_constant *child_const; + }; }; } variance_obligation; @@ -2648,6 +2672,18 @@ static void add_property_compatibility_obligation( zend_hash_next_index_insert_ptr(obligations, obligation); } +static void add_class_constant_compatibility_obligation( + zend_class_entry *ce, const zend_class_constant *child_const, + const zend_class_constant *parent_const, const zend_string *const_name) { + HashTable *obligations = get_or_init_obligations_for_class(ce); + variance_obligation *obligation = emalloc(sizeof(variance_obligation)); + obligation->type = OBLIGATION_CLASS_CONSTANT_COMPATIBILITY; + obligation->const_name = const_name; + obligation->child_const = child_const; + obligation->parent_const = parent_const; + zend_hash_next_index_insert_ptr(obligations, obligation); +} + static void resolve_delayed_variance_obligations(zend_class_entry *ce); static void check_variance_obligation(variance_obligation *obligation) { @@ -2671,13 +2707,19 @@ static void check_variance_obligation(variance_obligation *obligation) { &obligation->parent_fn, obligation->parent_scope, status); } /* Either the compatibility check was successful or only threw a warning. */ - } else { - ZEND_ASSERT(obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY); + } else if (obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY) { inheritance_status status = property_types_compatible(obligation->parent_prop, obligation->child_prop); if (status != INHERITANCE_SUCCESS) { emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop); } + } else { + ZEND_ASSERT(obligation->type == OBLIGATION_CLASS_CONSTANT_COMPATIBILITY); + inheritance_status status = + class_constant_types_compatible(obligation->parent_const, obligation->child_const); + if (status != INHERITANCE_SUCCESS) { + emit_incompatible_class_constant_error(obligation->child_const, obligation->parent_const, obligation->const_name); + } } } @@ -3141,6 +3183,7 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e zend_string *key; zend_function *parent_func; zend_property_info *parent_info; + zend_class_constant *parent_const; inheritance_status overall_status = INHERITANCE_SUCCESS; ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) { @@ -3179,6 +3222,25 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e } } ZEND_HASH_FOREACH_END(); + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&parent_ce->constants_table, key, parent_const) { + zval *zv; + if ((ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE) || !ZEND_TYPE_IS_SET(parent_const->type)) { + continue; + } + + zv = zend_hash_find_known_hash(&ce->constants_table, key); + if (zv) { + zend_class_constant *child_const = Z_PTR_P(zv); + if (ZEND_TYPE_IS_SET(child_const->type)) { + inheritance_status status = class_constant_types_compatible(parent_const, child_const); + ZEND_ASSERT(status != INHERITANCE_WARNING); + if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { + return status; + } + } + } + } ZEND_HASH_FOREACH_END(); + return overall_status; } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 9fb9da09c8847..0b6604217fa35 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5991,7 +5991,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP2(); HANDLE_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 600d00d14d02c..21b927c02b895 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7265,7 +7265,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -8418,7 +8418,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); @@ -25082,7 +25082,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -25644,7 +25644,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); @@ -34228,7 +34228,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -34580,7 +34580,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 5be540bc1ca51..11ab472631b56 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -720,9 +720,6 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int if (Z_TYPE(c->value) == IS_STRING) { ZVAL_STR(&c->value, new_interned_string(Z_STR(c->value))); } - if (c->name) { - c->name = new_interned_string(c->name); - } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); @@ -3752,15 +3749,16 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) bool ok, changed, was_changed = false; zend_class_constant *c; zval *val; + zend_string *key; EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */ do { ok = true; changed = false; - ZEND_HASH_MAP_FOREACH_PTR(&ce->constants_table, c) { + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { val = &c->value; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - if (EXPECTED(zend_update_class_constant(c, c->ce) == SUCCESS)) { + if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) { was_changed = changed = true; } else { ok = false; diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 56e183d3830ee..f4c9a77996b96 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -687,12 +687,11 @@ static void zend_file_cache_serialize_class_constant(zval *z c = Z_PTR_P(zv); UNSERIALIZE_PTR(c); - ZEND_ASSERT(c->ce != NULL && c->name != NULL); + ZEND_ASSERT(c->ce != NULL); if (!IS_SERIALIZED(c->ce)) { SERIALIZE_PTR(c->ce); zend_file_cache_serialize_zval(&c->value, script, info, buf); - SERIALIZE_STR(c->name); if (c->doc_comment) { SERIALIZE_STR(c->doc_comment); } @@ -1522,12 +1521,11 @@ static void zend_file_cache_unserialize_class_constant(zval * UNSERIALIZE_PTR(Z_PTR_P(zv)); c = Z_PTR_P(zv); - ZEND_ASSERT(c->ce != NULL && c->name != NULL); + ZEND_ASSERT(c->ce != NULL); if (!IS_UNSERIALIZED(c->ce)) { UNSERIALIZE_PTR(c->ce); zend_file_cache_unserialize_zval(&c->value, script, buf); - UNSERIALIZE_STR(c->name); if (c->doc_comment) { UNSERIALIZE_STR(c->doc_comment); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 5c1acd73ccc8b..e21aaa069348a 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -816,7 +816,6 @@ static void zend_persist_class_constant(zval *zv) } c = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_class_constant)); zend_persist_zval(&c->value); - zend_accel_store_interned_string(c->name); ce = zend_shared_alloc_get_xlat_entry(c->ce); if (ce) { c->ce = ce; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 392be02088911..dfc281eb7f6f7 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -393,7 +393,6 @@ static void zend_persist_class_constant_calc(zval *zv) zend_shared_alloc_register_xlat_entry(c, c); ADD_SIZE(sizeof(zend_class_constant)); zend_persist_zval_calc(&c->value); - ADD_INTERNED_STRING(c->name); if (ZCG(accel_directives).save_comments && c->doc_comment) { ADD_STRING(c->doc_comment); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8e1f5d2096373..2f8acbfb00852 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -297,7 +297,7 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{ static void _const_string(smart_str *str, char *name, zval *value, char *indent); static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, char* indent); static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, char* indent); -static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char* indent); +static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char* indent); static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char *indent); static void _extension_string(smart_str *str, zend_module_entry *module, char *indent); static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent); @@ -384,7 +384,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char zend_class_constant *c; ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) { - _class_const_string(str, ZSTR_VAL(key), c, ZSTR_VAL(sub_indent)); + _class_const_string(str, key, c, ZSTR_VAL(sub_indent)); if (UNEXPECTED(EG(exception))) { zend_string_release(sub_indent); return; @@ -556,9 +556,9 @@ static void _const_string(smart_str *str, char *name, zval *value, char *indent) /* }}} */ /* {{{ _class_const_string */ -static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char *indent) +static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char *indent) { - if (zend_update_class_constant(c, c->ce) == FAILURE) { + if (Z_TYPE(c->value) == IS_CONSTANT_AST && zend_update_class_constant(c, name, c->ce) == FAILURE) { return; } @@ -567,7 +567,7 @@ static void _class_const_string(smart_str *str, char *name, zend_class_constant zend_string *type_str = ZEND_TYPE_IS_SET(c->type) ? zend_type_to_string(c->type) : NULL; const char *type = type_str ? ZSTR_VAL(type_str) : zend_zval_type_name(&c->value); smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", - indent, final, visibility, type, name); + indent, final, visibility, type, ZSTR_VAL(name)); if (Z_TYPE(c->value) == IS_ARRAY) { smart_str_appends(str, "Array"); } else if (Z_TYPE(c->value) == IS_OBJECT) { @@ -3801,7 +3801,7 @@ ZEND_METHOD(ReflectionClassConstant, __toString) ZVAL_DEREF(name); ZEND_ASSERT(Z_TYPE_P(name) == IS_STRING); - _class_const_string(&str, Z_STRVAL_P(name), ref, ""); + _class_const_string(&str, Z_STR_P(name), ref, ""); RETURN_STR(smart_str_extract(&str)); } /* }}} */ @@ -3925,8 +3925,16 @@ ZEND_METHOD(ReflectionClassConstant, getValue) } GET_REFLECTION_OBJECT_PTR(ref); + zval *name = reflection_prop_name(ZEND_THIS); + if (Z_ISUNDEF_P(name)) { + zend_throw_error(NULL, + "Typed property ReflectionClassConstant::$name " + "must not be accessed before initialization"); + RETURN_THROWS(); + } + if (Z_TYPE(ref->value) == IS_CONSTANT_AST) { - zend_result result = zend_update_class_constant(ref, ref->ce); + zend_result result = zend_update_class_constant(ref, Z_STR_P(name), ref->ce); if (result == FAILURE) { RETURN_THROWS(); } @@ -4735,7 +4743,7 @@ ZEND_METHOD(ReflectionClass, getConstants) array_init(return_value); ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) { - if (UNEXPECTED(zend_update_class_constant(constant, constant->ce) != SUCCESS)) { + if (UNEXPECTED(Z_TYPE(constant->value) == IS_CONSTANT_AST && zend_update_class_constant(constant, key, constant->ce) != SUCCESS)) { RETURN_THROWS(); } @@ -4785,7 +4793,7 @@ ZEND_METHOD(ReflectionClass, getConstant) zend_class_entry *ce; HashTable *constants_table; zend_class_constant *c; - zend_string *name; + zend_string *name, *key; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) { RETURN_THROWS(); @@ -4793,8 +4801,8 @@ ZEND_METHOD(ReflectionClass, getConstant) GET_REFLECTION_OBJECT_PTR(ce); constants_table = CE_CONSTANTS_TABLE(ce); - ZEND_HASH_MAP_FOREACH_PTR(constants_table, c) { - if (UNEXPECTED(zend_update_class_constant(c, c->ce) != SUCCESS)) { + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(constants_table, key, c) { + if (UNEXPECTED(Z_TYPE(c->value) == IS_CONSTANT_AST && zend_update_class_constant(c, key, c->ce) != SUCCESS)) { RETURN_THROWS(); } } ZEND_HASH_FOREACH_END(); From 0158e65fc30b52b191b784e7eae287dd2fed92e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 25 Mar 2023 14:23:16 +0100 Subject: [PATCH 08/11] Remove forgotten code --- Zend/zend_API.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index af05631357a73..e9058f3e43db9 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -4579,10 +4579,6 @@ ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry zval_make_interned_string(value); } - if (!ZSTR_IS_INTERNED(name)) { - name = zend_new_interned_string(zend_string_copy(name)); - } - if (ce->type == ZEND_INTERNAL_CLASS) { c = pemalloc(sizeof(zend_class_constant), 1); } else { From 4d7639a0b55a0d12fdc58bb2e4a9fbff00dbf331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 3 Apr 2023 15:39:31 +0200 Subject: [PATCH 09/11] Code review fixes --- .../typed_class_constants_type_error12.phpt | 15 +++++++++++++++ Zend/zend_execute.c | 15 +++++++++++---- Zend/zend_inheritance.c | 18 +++++++++++------- ext/opcache/ZendAccelerator.c | 3 +++ .../tests/ReflectionClassConstant_basic1.phpt | 10 ++-------- 5 files changed, 42 insertions(+), 19 deletions(-) create mode 100644 Zend/tests/type_declarations/typed_class_constants_type_error12.phpt diff --git a/Zend/tests/type_declarations/typed_class_constants_type_error12.phpt b/Zend/tests/type_declarations/typed_class_constants_type_error12.phpt new file mode 100644 index 0000000000000..fddc1bca0bae9 --- /dev/null +++ b/Zend/tests/type_declarations/typed_class_constants_type_error12.phpt @@ -0,0 +1,15 @@ +--TEST-- +Typed class constants with static in union +--FILE-- + +--EXPECT-- +object(A)#2 (0) { +} diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index a1f0fb8b1a3c3..42c9fcdc0fc1c 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -948,7 +948,7 @@ static bool zend_check_intersection_for_property_or_class_constant_class_type( static bool zend_check_and_resolve_property_or_class_constant_class_type( const zend_class_entry *scope, zend_type member_type, const zend_class_entry *value_ce) { -if (ZEND_TYPE_HAS_LIST(member_type)) { + if (ZEND_TYPE_HAS_LIST(member_type)) { zend_type *list_type; if (ZEND_TYPE_IS_INTERSECTION(member_type)) { return zend_check_intersection_for_property_or_class_constant_class_type( @@ -968,14 +968,21 @@ if (ZEND_TYPE_HAS_LIST(member_type)) { return true; } } ZEND_TYPE_LIST_FOREACH_END(); + + if ((ZEND_TYPE_PURE_MASK(member_type) & MAY_BE_STATIC)) { + return value_ce == scope; + } + return false; } - } else if ((ZEND_TYPE_PURE_MASK(member_type) & MAY_BE_STATIC)) { - return value_ce == scope; - } else { + } else if ((ZEND_TYPE_PURE_MASK(member_type) & MAY_BE_STATIC) && value_ce == scope) { + return true; + } else if (ZEND_TYPE_HAS_NAME(member_type)) { const zend_class_entry *ce = zend_ce_from_type(scope, &member_type); return ce && instanceof_function(value_ce, ce); } + + return false; } static zend_always_inline bool i_zend_check_property_type(const zend_property_info *info, zval *property, bool strict) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 6fbfa4190e1ba..ff3a4d7080751 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1682,6 +1682,7 @@ static zend_always_inline bool check_trait_property_or_constant_value_compatibil if (UNEXPECTED(Z_TYPE_P(op1) == IS_CONSTANT_AST)) { ZVAL_COPY_OR_DUP(&op1_tmp, op1); if (UNEXPECTED(zval_update_constant_ex(&op1_tmp, ce) != SUCCESS)) { + zval_ptr_dtor(&op1_tmp); return false; } op1 = &op1_tmp; @@ -1689,6 +1690,7 @@ static zend_always_inline bool check_trait_property_or_constant_value_compatibil if (UNEXPECTED(Z_TYPE_P(op2) == IS_CONSTANT_AST)) { ZVAL_COPY_OR_DUP(&op2_tmp, op2); if (UNEXPECTED(zval_update_constant_ex(&op2_tmp, ce) != SUCCESS)) { + zval_ptr_dtor(&op2_tmp); return false; } op2 = &op2_tmp; @@ -2275,7 +2277,7 @@ static zend_class_entry* find_first_constant_definition(zend_class_entry *ce, ze } /* }}} */ -static bool emit_incompatible_trait_constant_error( +static void emit_incompatible_trait_constant_error( zend_class_entry *ce, zend_class_constant *existing_constant, zend_class_constant *trait_constant, zend_string *name, zend_class_entry **traits, size_t current_trait ) { @@ -2286,8 +2288,6 @@ static bool emit_incompatible_trait_constant_error( ZSTR_VAL(name), ZSTR_VAL(ce->name) ); - - return false; } static bool do_trait_constant_check( @@ -2304,22 +2304,26 @@ static bool do_trait_constant_check( zend_class_constant *existing_constant = Z_PTR_P(zv); if ((ZEND_CLASS_CONST_FLAGS(trait_constant) & flags_mask) != (ZEND_CLASS_CONST_FLAGS(existing_constant) & flags_mask)) { - return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + return false; } if (ZEND_TYPE_IS_SET(trait_constant->type) != ZEND_TYPE_IS_SET(existing_constant->type)) { - return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + return false; } else if (ZEND_TYPE_IS_SET(trait_constant->type)) { inheritance_status status1 = zend_perform_covariant_type_check(ce, existing_constant->type, traits[current_trait], trait_constant->type); inheritance_status status2 = zend_perform_covariant_type_check(traits[current_trait], trait_constant->type, ce, existing_constant->type); if (status1 == INHERITANCE_ERROR || status2 == INHERITANCE_ERROR) { - return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + return false; } } if (!check_trait_property_or_constant_value_compatibility(ce, &trait_constant->value, &existing_constant->value)) { /* There is an existing constant of the same name, and it conflicts with the new one, so let's throw a fatal error */ - return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); + return false; } /* There is an existing constant which is compatible with the new one, so no need to add it */ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 11ab472631b56..1f2a27a02c8f1 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -731,6 +731,9 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int p->key = new_interned_string(p->key); } c = (zend_constant*)Z_PTR(p->val); + if (c->name) { + c->name = new_interned_string(c->name); + } if (Z_TYPE(c->value) == IS_STRING) { ZVAL_STR(&c->value, new_interned_string(Z_STR(c->value))); } diff --git a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt index 468ea736ce6ef..33d923d97130c 100644 --- a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt +++ b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt @@ -31,7 +31,7 @@ function reflectClassConstant($base, $constant) { echo "hasType():\n"; var_dump($constInfo->hasType()); echo "getType():\n"; - var_dump($constInfo->getType()); + echo $constInfo->getType() ?? "NULL"; echo "\n**********************************\n"; } @@ -83,9 +83,7 @@ string(21) "/** My Doc comment */" hasType(): bool(true) getType(): -object(ReflectionNamedType)#3 (0) { -} - +bool ********************************** ********************************** Reflecting on class constant TestClass::PROT @@ -118,7 +116,6 @@ hasType(): bool(false) getType(): NULL - ********************************** ********************************** Reflecting on class constant TestClass::PRIV @@ -151,7 +148,6 @@ hasType(): bool(false) getType(): NULL - ********************************** ********************************** Reflecting on class constant TestClass::FINAL @@ -184,7 +180,6 @@ hasType(): bool(false) getType(): NULL - ********************************** ********************************** Reflecting on class constant TestClass::PRIV @@ -217,7 +212,6 @@ hasType(): bool(false) getType(): NULL - ********************************** Fatal error: Uncaught ReflectionException: Constant TestClass::BAD_CONST does not exist in %s:%d From e68b560556bed11d5a4117e9055d4bbed060e08e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 16 Apr 2023 19:53:28 +0200 Subject: [PATCH 10/11] Remove already removed field --- ext/opcache/ZendAccelerator.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 1f2a27a02c8f1..11ab472631b56 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -731,9 +731,6 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int p->key = new_interned_string(p->key); } c = (zend_constant*)Z_PTR(p->val); - if (c->name) { - c->name = new_interned_string(c->name); - } if (Z_TYPE(c->value) == IS_STRING) { ZVAL_STR(&c->value, new_interned_string(Z_STR(c->value))); } From 5a6443d52004318dfcd1e9421b106ee905d80108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 16 Apr 2023 22:17:24 +0200 Subject: [PATCH 11/11] [skip ci] Add upgrading note --- UPGRADING | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UPGRADING b/UPGRADING index b55d018f30953..a22bc46328e90 100644 --- a/UPGRADING +++ b/UPGRADING @@ -52,6 +52,8 @@ PHP 8.3 UPGRADE NOTES . Anonymous classes may now be marked as readonly. . Readonly properties can now be reinitialized during cloning. RFC: https://wiki.php.net/rfc/readonly_amendments + . Class, interface, trait, and enum constants now support type + declarations. RFC: https://wiki.php.net/rfc/typed_class_constants - Posix . posix_getrlimit() now takes an optional $res parameter to allow fetching a