From a6fc677f3102edbeff547c415504d623d561dcdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 18 Apr 2021 11:38:00 +0200 Subject: [PATCH 1/6] Add support for final constants --- .../final_constants/final_const1.phpt | 13 ++++++ .../final_constants/final_const2.phpt | 18 ++++++++ .../final_constants/final_const3.phpt | 13 ++++++ Zend/tests/errmsg_038.phpt | 2 +- Zend/zend_compile.c | 11 ++++- Zend/zend_inheritance.c | 7 ++++ ext/reflection/php_reflection.c | 6 +++ ext/reflection/php_reflection.stub.php | 2 + ext/reflection/php_reflection_arginfo.h | 6 ++- .../tests/ReflectionClassConstant_basic1.phpt | 41 +++++++++++++++++++ tests/classes/constants_visibility_007.phpt | 10 ----- 11 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 Zend/tests/constants/final_constants/final_const1.phpt create mode 100644 Zend/tests/constants/final_constants/final_const2.phpt create mode 100644 Zend/tests/constants/final_constants/final_const3.phpt delete mode 100644 tests/classes/constants_visibility_007.phpt diff --git a/Zend/tests/constants/final_constants/final_const1.phpt b/Zend/tests/constants/final_constants/final_const1.phpt new file mode 100644 index 0000000000000..767d745d30195 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const1.phpt @@ -0,0 +1,13 @@ +--TEST-- +Class constants support the final modifier +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/constants/final_constants/final_const2.phpt b/Zend/tests/constants/final_constants/final_const2.phpt new file mode 100644 index 0000000000000..031039cf47b88 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const2.phpt @@ -0,0 +1,18 @@ +--TEST-- +Final class constants cannot be overridden +--FILE-- + +--EXPECTF-- +Fatal error: Bar::A cannot override final constant Foo::A in %s on line %d diff --git a/Zend/tests/constants/final_constants/final_const3.phpt b/Zend/tests/constants/final_constants/final_const3.phpt new file mode 100644 index 0000000000000..80e0163a6a021 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const3.phpt @@ -0,0 +1,13 @@ +--TEST-- +Private class constants cannot be final +--FILE-- + +--EXPECTF-- +Fatal error: Private constant Foo::A cannot be final as it is never overridden in %s on line %d diff --git a/Zend/tests/errmsg_038.phpt b/Zend/tests/errmsg_038.phpt index d5ae50ded0f92..0682e67246fe8 100644 --- a/Zend/tests/errmsg_038.phpt +++ b/Zend/tests/errmsg_038.phpt @@ -10,4 +10,4 @@ class test { echo "Done\n"; ?> --EXPECTF-- -Fatal error: Cannot declare property test::$var final, the final modifier is allowed only for methods and classes in %s on line %d +Fatal error: Cannot declare property test::$var final, the final modifier is allowed only for methods, classes, and class constants in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 78a39fbc6cb02..bf386b8106d85 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7270,7 +7270,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z if (flags & ZEND_ACC_FINAL) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, " - "the final modifier is allowed only for methods and classes", + "the final modifier is allowed only for methods, classes, and class constants", ZSTR_VAL(ce->name), ZSTR_VAL(name)); } @@ -7358,10 +7358,17 @@ void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL; zval value_zv; - if (UNEXPECTED(flags & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_FINAL))) { + if (UNEXPECTED(flags & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT))) { zend_check_const_and_trait_alias_attr(flags, "constant"); } + if (UNEXPECTED((flags & ZEND_ACC_PRIVATE) && (flags & ZEND_ACC_FINAL))) { + zend_error_noreturn( + E_COMPILE_ERROR, "Private constant %s::%s cannot be final as it is never overridden", + ZSTR_VAL(ce->name), ZSTR_VAL(name) + ); + } + zend_const_expr_to_zval(&value_zv, value_ast_ptr); c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index f7ad6c5a33532..d44b2b2a81d1b 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1337,6 +1337,13 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa 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"); } + + if (UNEXPECTED((ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_FINAL))) { + zend_error_noreturn( + E_COMPILE_ERROR, "%s::%s cannot override final constant %s::%s", + ZSTR_VAL(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/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index c63f99976e0ca..5a79190850878 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3831,6 +3831,12 @@ ZEND_METHOD(ReflectionClassConstant, isProtected) } /* }}} */ +/* Returns whether this constant is final */ +ZEND_METHOD(ReflectionClassConstant, isFinal) +{ + _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_FINAL); +} + /* {{{ Returns a bitfield of the access modifiers for this constant */ ZEND_METHOD(ReflectionClassConstant, getModifiers) { diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 3b883bb7e32ad..c70a49c77561e 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -475,6 +475,8 @@ public function isPrivate(): bool {} /** @tentative-return-type */ public function isProtected(): bool {} + public function isFinal(): bool {} + /** @tentative-return-type */ public function getModifiers(): int {} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index dc39e428719ff..7330fe680f393 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: d8e686125cf213e019c1d706867e3c178fa057d2 */ + * Stub hash: 2564122a201ca462ee35ead1562c94da3ea3c8a3 */ 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) @@ -396,6 +396,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassConstant_isProtected arginfo_class_ReflectionFunctionAbstract_inNamespace +#define arginfo_class_ReflectionClassConstant_isFinal arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + #define arginfo_class_ReflectionClassConstant_getModifiers arginfo_class_ReflectionFunctionAbstract_getNumberOfParameters #define arginfo_class_ReflectionClassConstant_getDeclaringClass arginfo_class_ReflectionMethod_getDeclaringClass @@ -736,6 +738,7 @@ ZEND_METHOD(ReflectionClassConstant, getValue); ZEND_METHOD(ReflectionClassConstant, isPublic); ZEND_METHOD(ReflectionClassConstant, isPrivate); ZEND_METHOD(ReflectionClassConstant, isProtected); +ZEND_METHOD(ReflectionClassConstant, isFinal); ZEND_METHOD(ReflectionClassConstant, getModifiers); ZEND_METHOD(ReflectionClassConstant, getDeclaringClass); ZEND_METHOD(ReflectionClassConstant, getDocComment); @@ -1015,6 +1018,7 @@ static const zend_function_entry class_ReflectionClassConstant_methods[] = { ZEND_ME(ReflectionClassConstant, isPublic, arginfo_class_ReflectionClassConstant_isPublic, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, isPrivate, arginfo_class_ReflectionClassConstant_isPrivate, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, isProtected, arginfo_class_ReflectionClassConstant_isProtected, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClassConstant, isFinal, arginfo_class_ReflectionClassConstant_isFinal, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, getModifiers, arginfo_class_ReflectionClassConstant_getModifiers, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, getDeclaringClass, arginfo_class_ReflectionClassConstant_getDeclaringClass, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, getDocComment, arginfo_class_ReflectionClassConstant_getDocComment, ZEND_ACC_PUBLIC) diff --git a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt index 2ab8990d95282..2058f4212bca6 100644 --- a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt +++ b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt @@ -20,6 +20,8 @@ function reflectClassConstant($base, $constant) { var_dump($constInfo->isPrivate()); echo "isProtected():\n"; var_dump($constInfo->isProtected()); + echo "isFinal():\n"; + var_dump($constInfo->isFinal()); echo "getModifiers():\n"; var_dump($constInfo->getModifiers()); echo "getDeclaringClass():\n"; @@ -34,12 +36,14 @@ class TestClass { /** Another doc comment */ protected const PROT = 4; private const PRIV = "keepOut"; + public final const FINAL = "foo"; } $instance = new TestClass(); reflectClassConstant("TestClass", "PUB"); reflectClassConstant("TestClass", "PROT"); reflectClassConstant("TestClass", "PRIV"); +reflectClassConstant("TestClass", "FINAL"); reflectClassConstant($instance, "PRIV"); reflectClassConstant($instance, "BAD_CONST"); @@ -61,6 +65,8 @@ isPrivate(): bool(false) isProtected(): bool(false) +isFinal(): +bool(false) getModifiers(): int(1) getDeclaringClass(): @@ -88,6 +94,8 @@ isPrivate(): bool(false) isProtected(): bool(true) +isFinal(): +bool(false) getModifiers(): int(2) getDeclaringClass(): @@ -115,6 +123,8 @@ isPrivate(): bool(true) isProtected(): bool(false) +isFinal(): +bool(false) getModifiers(): int(4) getDeclaringClass(): @@ -125,6 +135,35 @@ object(ReflectionClass)#3 (1) { getDocComment(): bool(false) +********************************** +********************************** +Reflecting on class constant TestClass::FINAL + +__toString(): +string(41) "Constant [ public string FINAL ] { foo } +" +getName(): +string(5) "FINAL" +getValue(): +string(3) "foo" +isPublic(): +bool(true) +isPrivate(): +bool(false) +isProtected(): +bool(false) +isFinal(): +bool(true) +getModifiers(): +int(1) +getDeclaringClass(): +object(ReflectionClass)#3 (1) { + ["name"]=> + string(9) "TestClass" +} +getDocComment(): +bool(false) + ********************************** ********************************** Reflecting on class constant TestClass::PRIV @@ -142,6 +181,8 @@ isPrivate(): bool(true) isProtected(): bool(false) +isFinal(): +bool(false) getModifiers(): int(4) getDeclaringClass(): diff --git a/tests/classes/constants_visibility_007.phpt b/tests/classes/constants_visibility_007.phpt deleted file mode 100644 index 37b52855869a1..0000000000000 --- a/tests/classes/constants_visibility_007.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -Final constants are not allowed ---FILE-- - ---EXPECTF-- -Fatal error: Cannot use 'final' as constant modifier in %s on line 3 From 14c05eacecc7555915663a2fef8b997b4e710e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 13 May 2021 23:18:44 +0200 Subject: [PATCH 2/6] Make interface constants overridable by default --- .../final_constants/final_const4.phpt | 17 +++++++++++++++++ .../final_constants/final_const5.phpt | 18 ++++++++++++++++++ .../final_constants/final_const6.phpt | 16 ++++++++++++++++ .../final_constants/final_const7.phpt | 19 +++++++++++++++++++ Zend/tests/inter_01.phpt | 18 ------------------ Zend/zend_API.c | 2 +- Zend/zend_inheritance.c | 9 ++++++++- .../interface_constant_inheritance_002.phpt | 16 ---------------- .../interface_constant_inheritance_003.phpt | 19 ------------------- 9 files changed, 79 insertions(+), 55 deletions(-) create mode 100644 Zend/tests/constants/final_constants/final_const4.phpt create mode 100644 Zend/tests/constants/final_constants/final_const5.phpt create mode 100644 Zend/tests/constants/final_constants/final_const6.phpt create mode 100644 Zend/tests/constants/final_constants/final_const7.phpt delete mode 100644 Zend/tests/inter_01.phpt delete mode 100644 tests/classes/interface_constant_inheritance_002.phpt delete mode 100644 tests/classes/interface_constant_inheritance_003.phpt diff --git a/Zend/tests/constants/final_constants/final_const4.phpt b/Zend/tests/constants/final_constants/final_const4.phpt new file mode 100644 index 0000000000000..8b13321734bf7 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const4.phpt @@ -0,0 +1,17 @@ +--TEST-- +Interface constants can be overridden directly +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/constants/final_constants/final_const5.phpt b/Zend/tests/constants/final_constants/final_const5.phpt new file mode 100644 index 0000000000000..c8f3e5801e359 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const5.phpt @@ -0,0 +1,18 @@ +--TEST-- +Final interface constants cannot be overridden directly +--FILE-- + +--EXPECTF-- +Fatal error: C::X cannot override final constant I::X in %s on line %d diff --git a/Zend/tests/constants/final_constants/final_const6.phpt b/Zend/tests/constants/final_constants/final_const6.phpt new file mode 100644 index 0000000000000..8d5ce3415dacb --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const6.phpt @@ -0,0 +1,16 @@ +--TEST-- +Final interface constants can be inherited +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/constants/final_constants/final_const7.phpt b/Zend/tests/constants/final_constants/final_const7.phpt new file mode 100644 index 0000000000000..d662060697fe6 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const7.phpt @@ -0,0 +1,19 @@ +--TEST-- +Interface constants can be overridden indirectly +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/inter_01.phpt b/Zend/tests/inter_01.phpt deleted file mode 100644 index 71c50c2595efb..0000000000000 --- a/Zend/tests/inter_01.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -Inherited constant from interface ---FILE-- - ---EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant foo from interface foo in %s on line %d diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 0df51b2f62413..069077377908e 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -4336,7 +4336,7 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c zend_class_constant *c; if (ce->ce_flags & ZEND_ACC_INTERFACE) { - if (access_type != ZEND_ACC_PUBLIC) { + if (!(access_type & ZEND_ACC_PUBLIC)) { zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface constant %s::%s must be public", ZSTR_VAL(ce->name), ZSTR_VAL(name)); } } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index d44b2b2a81d1b..e57d2bc1f0a65 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1636,7 +1636,14 @@ static bool do_inherit_constant_check(HashTable *child_constants_table, zend_cla if (zv != NULL) { old_constant = (zend_class_constant*)Z_PTR_P(zv); - if (old_constant->ce != parent_constant->ce) { + + if ((ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_FINAL)) { + zend_error_noreturn(E_COMPILE_ERROR, "%s::%s cannot override final constant %s::%s", + ZSTR_VAL(old_constant->ce->name), ZSTR_VAL(name), ZSTR_VAL(iface->name), ZSTR_VAL(name) + ); + } + + if (old_constant->ce != parent_constant->ce && old_constant->ce->ce_flags & ZEND_ACC_INTERFACE) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s from interface %s", ZSTR_VAL(name), ZSTR_VAL(iface->name)); } return 0; diff --git a/tests/classes/interface_constant_inheritance_002.phpt b/tests/classes/interface_constant_inheritance_002.phpt deleted file mode 100644 index 8840f7caf4a33..0000000000000 --- a/tests/classes/interface_constant_inheritance_002.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -Ensure a class may not shadow a constant inherited from an interface. ---FILE-- - ---EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant FOO from interface I in %s on line 6 diff --git a/tests/classes/interface_constant_inheritance_003.phpt b/tests/classes/interface_constant_inheritance_003.phpt deleted file mode 100644 index de867f5dfce9e..0000000000000 --- a/tests/classes/interface_constant_inheritance_003.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -Ensure a class may not inherit two constants with the same name from two separate interfaces. ---FILE-- - ---EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant FOO from interface I2 in %s on line 10 From 22244d12d75e0f5addb808c845c57b5790955d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 3 Jul 2021 22:53:27 +0200 Subject: [PATCH 3/6] Address review comments --- Zend/tests/bug49472.phpt | 2 +- .../final_constants/final_const10.phpt | 22 +++++++++++ .../final_constants/final_const11.phpt | 22 +++++++++++ .../final_constants/final_const12.phpt | 22 +++++++++++ .../final_constants/final_const3.phpt | 2 +- .../final_constants/final_const8.phpt | 22 +++++++++++ .../final_constants/final_const9.phpt | 22 +++++++++++ Zend/tests/errmsg_025.phpt | 2 +- Zend/zend_API.c | 6 +-- Zend/zend_compile.c | 2 +- Zend/zend_inheritance.c | 38 ++++++++++--------- ext/reflection/php_reflection.c | 9 +++-- .../tests/ReflectionClassConstant_basic1.phpt | 4 +- .../interface_constant_inheritance_001.phpt | 2 +- 14 files changed, 147 insertions(+), 30 deletions(-) create mode 100644 Zend/tests/constants/final_constants/final_const10.phpt create mode 100644 Zend/tests/constants/final_constants/final_const11.phpt create mode 100644 Zend/tests/constants/final_constants/final_const12.phpt create mode 100644 Zend/tests/constants/final_constants/final_const8.phpt create mode 100644 Zend/tests/constants/final_constants/final_const9.phpt diff --git a/Zend/tests/bug49472.phpt b/Zend/tests/bug49472.phpt index e5b21fd500802..8b7046724824a 100644 --- a/Zend/tests/bug49472.phpt +++ b/Zend/tests/bug49472.phpt @@ -24,4 +24,4 @@ new FooBar; ?> --EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant c from interface ia in %s on line %d +Fatal error: Cannot inherit previously-inherited or override constant FooBar::c from interface ia in %s on line %d diff --git a/Zend/tests/constants/final_constants/final_const10.phpt b/Zend/tests/constants/final_constants/final_const10.phpt new file mode 100644 index 0000000000000..c550cec1b9f06 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const10.phpt @@ -0,0 +1,22 @@ +--TEST-- +Interface constants inherited from other interfaces can be redeclared +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/constants/final_constants/final_const11.phpt b/Zend/tests/constants/final_constants/final_const11.phpt new file mode 100644 index 0000000000000..9546a9f2f5847 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const11.phpt @@ -0,0 +1,22 @@ +--TEST-- +Class constants cannot be inherited from both a class and an interface +--FILE-- + +--EXPECTF-- +Fatal error: Cannot inherit previously-inherited or override constant C2::C from interface I in %s on line %d diff --git a/Zend/tests/constants/final_constants/final_const12.phpt b/Zend/tests/constants/final_constants/final_const12.phpt new file mode 100644 index 0000000000000..d15a0df75c321 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const12.phpt @@ -0,0 +1,22 @@ +--TEST-- +Interface constants cannot be inherited from other interfaces +--FILE-- + +--EXPECTF-- +Fatal error: Cannot inherit previously-inherited or override constant I3::C from interface I2 in %s on line %d diff --git a/Zend/tests/constants/final_constants/final_const3.phpt b/Zend/tests/constants/final_constants/final_const3.phpt index 80e0163a6a021..9f189dac44d64 100644 --- a/Zend/tests/constants/final_constants/final_const3.phpt +++ b/Zend/tests/constants/final_constants/final_const3.phpt @@ -10,4 +10,4 @@ class Foo ?> --EXPECTF-- -Fatal error: Private constant Foo::A cannot be final as it is never overridden in %s on line %d +Fatal error: Private constant Foo::A cannot be final as it is not visible to other classes in %s on line %d diff --git a/Zend/tests/constants/final_constants/final_const8.phpt b/Zend/tests/constants/final_constants/final_const8.phpt new file mode 100644 index 0000000000000..cf97cc3f77019 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const8.phpt @@ -0,0 +1,22 @@ +--TEST-- +Class constants cannot be inherited from two different interfaces +--FILE-- + +--EXPECTF-- +Fatal error: Cannot inherit previously-inherited or override constant C::C from interface I2 in %s on line %d diff --git a/Zend/tests/constants/final_constants/final_const9.phpt b/Zend/tests/constants/final_constants/final_const9.phpt new file mode 100644 index 0000000000000..eca462c436b21 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const9.phpt @@ -0,0 +1,22 @@ +--TEST-- +Class constants inherited from interfaces can be redeclared +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/errmsg_025.phpt b/Zend/tests/errmsg_025.phpt index d003ec859daec..b86a4f9d70116 100644 --- a/Zend/tests/errmsg_025.phpt +++ b/Zend/tests/errmsg_025.phpt @@ -17,4 +17,4 @@ class test implements test1, test2 { echo "Done\n"; ?> --EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant FOO from interface test2 in %s on line %d +Fatal error: Cannot inherit previously-inherited or override constant test::FOO from interface test2 in %s on line %d diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 069077377908e..96cfb1b46e7dc 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -4331,12 +4331,12 @@ 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 access_type, zend_string *doc_comment) /* {{{ */ +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_class_constant *c; if (ce->ce_flags & ZEND_ACC_INTERFACE) { - if (!(access_type & ZEND_ACC_PUBLIC)) { + if (!(flags & ZEND_ACC_PUBLIC)) { zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface constant %s::%s must be public", ZSTR_VAL(ce->name), ZSTR_VAL(name)); } } @@ -4356,7 +4356,7 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); } ZVAL_COPY_VALUE(&c->value, value); - ZEND_CLASS_CONST_FLAGS(c) = access_type; + ZEND_CLASS_CONST_FLAGS(c) = flags; c->doc_comment = doc_comment; c->attributes = NULL; c->ce = ce; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index bf386b8106d85..7713ffa2c6c7e 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7364,7 +7364,7 @@ void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr if (UNEXPECTED((flags & ZEND_ACC_PRIVATE) && (flags & ZEND_ACC_FINAL))) { zend_error_noreturn( - E_COMPILE_ERROR, "Private constant %s::%s cannot be final as it is never overridden", + E_COMPILE_ERROR, "Private constant %s::%s cannot be final as it is not visible to other classes", ZSTR_VAL(ce->name), ZSTR_VAL(name) ); } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index e57d2bc1f0a65..67ef374ca4a0f 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1629,32 +1629,36 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par } /* }}} */ -static bool do_inherit_constant_check(HashTable *child_constants_table, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */ -{ +static bool do_inherit_constant_check( + zend_class_entry *ce, HashTable *child_constants_table, zend_class_constant *parent_constant, + zend_string *name, const zend_class_entry *iface +) { zval *zv = zend_hash_find_ex(child_constants_table, name, 1); zend_class_constant *old_constant; - if (zv != NULL) { - old_constant = (zend_class_constant*)Z_PTR_P(zv); + if (zv == NULL) { + return true; + } - if ((ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_FINAL)) { - zend_error_noreturn(E_COMPILE_ERROR, "%s::%s cannot override final constant %s::%s", - ZSTR_VAL(old_constant->ce->name), ZSTR_VAL(name), ZSTR_VAL(iface->name), ZSTR_VAL(name) - ); - } + old_constant = (zend_class_constant*)Z_PTR_P(zv); - if (old_constant->ce != parent_constant->ce && old_constant->ce->ce_flags & ZEND_ACC_INTERFACE) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s from interface %s", ZSTR_VAL(name), ZSTR_VAL(iface->name)); - } - return 0; + if ((ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_FINAL)) { + zend_error_noreturn(E_COMPILE_ERROR, "%s::%s cannot override final constant %s::%s", + ZSTR_VAL(old_constant->ce->name), ZSTR_VAL(name), ZSTR_VAL(iface->name), ZSTR_VAL(name) + ); } - return 1; + + if (old_constant->ce != parent_constant->ce && (old_constant->ce->ce_flags & ZEND_ACC_INTERFACE)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s::%s from interface %s", ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(iface->name)); + } + + return false; } /* }}} */ static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */ { - if (do_inherit_constant_check(&ce->constants_table, c, name, iface)) { + if (do_inherit_constant_check(ce, &ce->constants_table, c, name, iface)) { zend_class_constant *ct; if (Z_TYPE(c->value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; @@ -1721,7 +1725,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry if (ignore) { /* Check for attempt to redeclare interface constants */ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { - do_inherit_constant_check(&iface->constants_table, c, key, iface); + do_inherit_constant_check(ce, &iface->constants_table, c, key, iface); } ZEND_HASH_FOREACH_END(); } else { if (ce->num_interfaces >= current_iface_num) { @@ -1766,7 +1770,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry } /* skip duplications */ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { - do_inherit_constant_check(&iface->constants_table, c, key, iface); + do_inherit_constant_check(ce, &iface->constants_table, c, key, iface); } ZEND_HASH_FOREACH_END(); iface = NULL; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 5a79190850878..44e8cbe25bfa5 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -565,9 +565,10 @@ static void _class_const_string(smart_str *str, char *name, zend_class_constant } 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); - smart_str_append_printf(str, "%sConstant [ %s %s %s ] { ", - indent, visibility, type, name); + smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", + indent, final, visibility, type, name); if (Z_TYPE(c->value) == IS_ARRAY) { smart_str_appends(str, "Array"); } else if (Z_TYPE(c->value) == IS_OBJECT) { @@ -3842,13 +3843,14 @@ ZEND_METHOD(ReflectionClassConstant, getModifiers) { reflection_object *intern; zend_class_constant *ref; + uint32_t keep_flags = ZEND_ACC_FINAL | ZEND_ACC_PPP_MASK; if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); } GET_REFLECTION_OBJECT_PTR(ref); - RETURN_LONG(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_ACC_PPP_MASK); + RETURN_LONG(ZEND_CLASS_CONST_FLAGS(ref) & keep_flags); } /* }}} */ @@ -7108,6 +7110,7 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ REGISTER_REFLECTION_CLASS_CONST_LONG(class_constant, "IS_PUBLIC", ZEND_ACC_PUBLIC); REGISTER_REFLECTION_CLASS_CONST_LONG(class_constant, "IS_PROTECTED", ZEND_ACC_PROTECTED); REGISTER_REFLECTION_CLASS_CONST_LONG(class_constant, "IS_PRIVATE", ZEND_ACC_PRIVATE); + REGISTER_REFLECTION_CLASS_CONST_LONG(class_constant, "IS_FINAL", ZEND_ACC_FINAL); reflection_extension_ptr = register_class_ReflectionExtension(reflector_ptr); reflection_init_class_handlers(reflection_extension_ptr); diff --git a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt index 2058f4212bca6..7d563bdf3bc16 100644 --- a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt +++ b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt @@ -140,7 +140,7 @@ bool(false) Reflecting on class constant TestClass::FINAL __toString(): -string(41) "Constant [ public string FINAL ] { foo } +string(47) "Constant [ final public string FINAL ] { foo } " getName(): string(5) "FINAL" @@ -155,7 +155,7 @@ bool(false) isFinal(): bool(true) getModifiers(): -int(1) +int(33) getDeclaringClass(): object(ReflectionClass)#3 (1) { ["name"]=> diff --git a/tests/classes/interface_constant_inheritance_001.phpt b/tests/classes/interface_constant_inheritance_001.phpt index a925097cdbbaf..1cd94bfb78788 100644 --- a/tests/classes/interface_constant_inheritance_001.phpt +++ b/tests/classes/interface_constant_inheritance_001.phpt @@ -13,4 +13,4 @@ interface I2 extends I1 { echo "Done\n"; ?> --EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant FOO from interface I1 in %s on line 6 +Fatal error: Cannot inherit previously-inherited or override constant I2::FOO from interface I1 in %s on line 6 From cd2506f766d5568a99ce56c8f02d76ff30fa7df3 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 6 Jul 2021 16:41:04 +0200 Subject: [PATCH 4/6] Fix constant inheritance check --- Zend/zend_inheritance.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 67ef374ca4a0f..5cb418cc8fd66 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1648,7 +1648,7 @@ static bool do_inherit_constant_check( ); } - if (old_constant->ce != parent_constant->ce && (old_constant->ce->ce_flags & ZEND_ACC_INTERFACE)) { + if (old_constant->ce != parent_constant->ce && old_constant->ce != ce) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s::%s from interface %s", ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(iface->name)); } From b46cf286ff5fcff5cd5cfe31719a38df13efc804 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 6 Jul 2021 16:45:22 +0200 Subject: [PATCH 5/6] Use correct constants table --- Zend/tests/bug49472.phpt | 5 +++-- Zend/zend_inheritance.c | 19 ++++++++----------- .../interface_constant_inheritance_001.phpt | 4 ++-- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Zend/tests/bug49472.phpt b/Zend/tests/bug49472.phpt index 8b7046724824a..617b8ce03a831 100644 --- a/Zend/tests/bug49472.phpt +++ b/Zend/tests/bug49472.phpt @@ -23,5 +23,6 @@ class FooBar extends Foo implements ia { new FooBar; ?> ---EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant FooBar::c from interface ia in %s on line %d +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 5cb418cc8fd66..382412337d1db 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1630,18 +1630,15 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par /* }}} */ static bool do_inherit_constant_check( - zend_class_entry *ce, HashTable *child_constants_table, zend_class_constant *parent_constant, + zend_class_entry *ce, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface ) { - zval *zv = zend_hash_find_ex(child_constants_table, name, 1); - zend_class_constant *old_constant; - + zval *zv = zend_hash_find_ex(&ce->constants_table, name, 1); if (zv == NULL) { return true; } - old_constant = (zend_class_constant*)Z_PTR_P(zv); - + zend_class_constant *old_constant = Z_PTR_P(zv); if ((ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_FINAL)) { zend_error_noreturn(E_COMPILE_ERROR, "%s::%s cannot override final constant %s::%s", ZSTR_VAL(old_constant->ce->name), ZSTR_VAL(name), ZSTR_VAL(iface->name), ZSTR_VAL(name) @@ -1658,7 +1655,7 @@ static bool do_inherit_constant_check( static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */ { - if (do_inherit_constant_check(ce, &ce->constants_table, c, name, iface)) { + if (do_inherit_constant_check(ce, c, name, iface)) { zend_class_constant *ct; if (Z_TYPE(c->value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; @@ -1724,8 +1721,8 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry } if (ignore) { /* Check for attempt to redeclare interface constants */ - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { - do_inherit_constant_check(ce, &iface->constants_table, c, key, iface); + ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) { + do_inherit_constant_check(ce, c, key, iface); } ZEND_HASH_FOREACH_END(); } else { if (ce->num_interfaces >= current_iface_num) { @@ -1769,8 +1766,8 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry return; } /* skip duplications */ - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { - do_inherit_constant_check(ce, &iface->constants_table, c, key, iface); + ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) { + do_inherit_constant_check(ce, c, key, iface); } ZEND_HASH_FOREACH_END(); iface = NULL; diff --git a/tests/classes/interface_constant_inheritance_001.phpt b/tests/classes/interface_constant_inheritance_001.phpt index 1cd94bfb78788..7c83ec2fcfba5 100644 --- a/tests/classes/interface_constant_inheritance_001.phpt +++ b/tests/classes/interface_constant_inheritance_001.phpt @@ -12,5 +12,5 @@ interface I2 extends I1 { echo "Done\n"; ?> ---EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant I2::FOO from interface I1 in %s on line 6 +--EXPECT-- +Done From 0fbf3fefd54c744100360951cbc1f1e2aeebe7fe Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 6 Jul 2021 16:54:35 +0200 Subject: [PATCH 6/6] Improve error message --- .../final_constants/final_const11.phpt | 2 +- .../final_constants/final_const12.phpt | 2 +- .../final_constants/final_const8.phpt | 2 +- Zend/tests/errmsg_025.phpt | 2 +- Zend/zend_inheritance.c | 18 +++++++++++------- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Zend/tests/constants/final_constants/final_const11.phpt b/Zend/tests/constants/final_constants/final_const11.phpt index 9546a9f2f5847..b4ed92064eefa 100644 --- a/Zend/tests/constants/final_constants/final_const11.phpt +++ b/Zend/tests/constants/final_constants/final_const11.phpt @@ -19,4 +19,4 @@ class C2 extends C implements I ?> --EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant C2::C from interface I in %s on line %d +Fatal error: Class C2 inherits both C::C and I::C, which is ambiguous in %s on line %d diff --git a/Zend/tests/constants/final_constants/final_const12.phpt b/Zend/tests/constants/final_constants/final_const12.phpt index d15a0df75c321..300239f6a630e 100644 --- a/Zend/tests/constants/final_constants/final_const12.phpt +++ b/Zend/tests/constants/final_constants/final_const12.phpt @@ -19,4 +19,4 @@ interface I3 extends I1, I2 ?> --EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant I3::C from interface I2 in %s on line %d +Fatal error: Class I3 inherits both I1::C and I2::C, which is ambiguous in %s on line %d diff --git a/Zend/tests/constants/final_constants/final_const8.phpt b/Zend/tests/constants/final_constants/final_const8.phpt index cf97cc3f77019..74bf1b2356d6d 100644 --- a/Zend/tests/constants/final_constants/final_const8.phpt +++ b/Zend/tests/constants/final_constants/final_const8.phpt @@ -19,4 +19,4 @@ class C implements I1, I2 ?> --EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant C::C from interface I2 in %s on line %d +Fatal error: Class C inherits both I1::C and I2::C, which is ambiguous in %s on line %d diff --git a/Zend/tests/errmsg_025.phpt b/Zend/tests/errmsg_025.phpt index b86a4f9d70116..f2e8bc0ceb1bf 100644 --- a/Zend/tests/errmsg_025.phpt +++ b/Zend/tests/errmsg_025.phpt @@ -17,4 +17,4 @@ class test implements test1, test2 { echo "Done\n"; ?> --EXPECTF-- -Fatal error: Cannot inherit previously-inherited or override constant test::FOO from interface test2 in %s on line %d +Fatal error: Class test inherits both test1::FOO and test2::FOO, which is ambiguous in %s on line %d diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 382412337d1db..42283ce536bf9 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1630,8 +1630,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par /* }}} */ static bool do_inherit_constant_check( - zend_class_entry *ce, zend_class_constant *parent_constant, - zend_string *name, const zend_class_entry *iface + zend_class_entry *ce, zend_class_constant *parent_constant, zend_string *name ) { zval *zv = zend_hash_find_ex(&ce->constants_table, name, 1); if (zv == NULL) { @@ -1641,12 +1640,17 @@ static bool do_inherit_constant_check( zend_class_constant *old_constant = Z_PTR_P(zv); if ((ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_FINAL)) { zend_error_noreturn(E_COMPILE_ERROR, "%s::%s cannot override final constant %s::%s", - ZSTR_VAL(old_constant->ce->name), ZSTR_VAL(name), ZSTR_VAL(iface->name), ZSTR_VAL(name) + ZSTR_VAL(old_constant->ce->name), ZSTR_VAL(name), + ZSTR_VAL(parent_constant->ce->name), ZSTR_VAL(name) ); } if (old_constant->ce != parent_constant->ce && old_constant->ce != ce) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s::%s from interface %s", ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(iface->name)); + zend_error_noreturn(E_COMPILE_ERROR, + "Class %s inherits both %s::%s and %s::%s, which is ambiguous", + ZSTR_VAL(ce->name), + ZSTR_VAL(old_constant->ce->name), ZSTR_VAL(name), + ZSTR_VAL(parent_constant->ce->name), ZSTR_VAL(name)); } return false; @@ -1655,7 +1659,7 @@ static bool do_inherit_constant_check( static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */ { - if (do_inherit_constant_check(ce, c, name, iface)) { + if (do_inherit_constant_check(ce, c, name)) { zend_class_constant *ct; if (Z_TYPE(c->value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; @@ -1722,7 +1726,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry if (ignore) { /* Check for attempt to redeclare interface constants */ ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) { - do_inherit_constant_check(ce, c, key, iface); + do_inherit_constant_check(ce, c, key); } ZEND_HASH_FOREACH_END(); } else { if (ce->num_interfaces >= current_iface_num) { @@ -1767,7 +1771,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry } /* skip duplications */ ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) { - do_inherit_constant_check(ce, c, key, iface); + do_inherit_constant_check(ce, c, key); } ZEND_HASH_FOREACH_END(); iface = NULL;