[php-src] master: [RFC] Allow `#[\Override]` on class constants (#20478)
Daniel Scherzer via GitHub <[email protected]>
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: Daniel Scherzer (DanielEScherzer)
Committer: GitHub (web-flow)
Pusher: DanielEScherzer
Date: 2026-07-03T13:03:27-07:00
Commit: https://github.com/php/php-src/commit/4629b3e1fa1abd5e720af5c3885756a975db1812
Raw diff: https://github.com/php/php-src/commit/4629b3e1fa1abd5e720af5c3885756a975db1812.diff
[RFC] Allow `#[\Override]` on class constants (#20478)
https://wiki.php.net/rfc/override_constants
Changed paths:
A Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt
A Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt
A Zend/tests/attributes/override/constants/anon_failure.phpt
A Zend/tests/attributes/override/constants/anon_interface.phpt
A Zend/tests/attributes/override/constants/anon_parent.phpt
A Zend/tests/attributes/override/constants/basic.phpt
A Zend/tests/attributes/override/constants/enum_failure.phpt
A Zend/tests/attributes/override/constants/enum_failure_case.phpt
A Zend/tests/attributes/override/constants/failure.phpt
A Zend/tests/attributes/override/constants/interface_failure.phpt
A Zend/tests/attributes/override/constants/trait_failure.phpt
A Zend/tests/attributes/override/constants/trait_interface.phpt
A Zend/tests/attributes/override/constants/trait_parent.phpt
A Zend/tests/attributes/override/constants/trait_redeclared.phpt
A Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt
A Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt
A Zend/tests/attributes/override/constants/trait_unused.phpt
A Zend/tests/attributes/override/constants/visibility_01.phpt
A Zend/tests/attributes/override/constants/visibility_02.phpt
A Zend/tests/attributes/override/constants/visibility_03.phpt
A Zend/tests/attributes/override/constants/visibility_04.phpt
M UPGRADING
M Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt
M Zend/zend_attributes.stub.php
M Zend/zend_attributes_arginfo.h
M Zend/zend_compile.c
M Zend/zend_inheritance.c
Diff:
diff --git a/UPGRADING b/UPGRADING
index a42d5ec355a6..b9c659ccc405 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -215,6 +215,8 @@ PHP 8.6 UPGRADE NOTES
needing to be present beforehand.
. It is now possible to define the __debugInfo() magic method on enums.
RFC: https://wiki.php.net/rfc/debugable-enums
+ . #[\Override] can now be applied to class constants, including enum cases.
+ RFC: https://wiki.php.net/rfc/override_constants
- Curl:
. curl_getinfo() return array now includes a new size_delivered key, which
diff --git a/Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt b/Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt
new file mode 100644
index 000000000000..9ebb8e4c0202
--- /dev/null
+++ b/Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\DelayedTargetValidation] with #[\Override]: non-overrides still error (class constant)
+--FILE--
+<?php
+
+class DemoClass {
+
+ #[DelayedTargetValidation]
+ #[Override] // Does something here
+ public const CLASS_CONSTANT = 'FOO';
+}
+
+?>
+--EXPECTF--
+Fatal error: DemoClass::CLASS_CONSTANT has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt b/Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt
new file mode 100644
index 000000000000..53d75412a849
--- /dev/null
+++ b/Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\DelayedTargetValidation] with #[\Override]: non-overrides still error (enum case)
+--FILE--
+<?php
+
+enum DemoEnum {
+
+ #[DelayedTargetValidation]
+ #[Override] // Does something here
+ case MyCase;
+}
+
+?>
+--EXPECTF--
+Fatal error: DemoEnum::MyCase has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt b/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt
index dd077f4b9cbd..494d85eaea9c 100644
--- a/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt
+++ b/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt
@@ -12,6 +12,8 @@ class Base {
set => $value;
}
+ public const CLASS_CONST = '';
+
public function printVal() {
echo __METHOD__ . "\n";
}
@@ -34,7 +36,7 @@ class DemoClass extends Base {
}
#[DelayedTargetValidation]
- #[Override] // Does nothing here
+ #[Override] // Does something here
public const CLASS_CONST = 'FOO';
public function __construct(
diff --git a/Zend/tests/attributes/override/constants/anon_failure.phpt b/Zend/tests/attributes/override/constants/anon_failure.phpt
new file mode 100644
index 000000000000..c43cedda81d7
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/anon_failure.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - anonymous class, no interface or parent class
+--FILE--
+<?php
+
+new class () {
+ #[\Override]
+ public const C = 'C';
+};
+
+echo "Done";
+
+?>
+--EXPECTF--
+Fatal error: class@anonymous::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/anon_interface.phpt b/Zend/tests/attributes/override/constants/anon_interface.phpt
new file mode 100644
index 000000000000..b19abfbf4d3a
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/anon_interface.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - anonymous class overrides interface
+--FILE--
+<?php
+
+interface IFace {
+ public const I = 'I';
+}
+
+new class () implements IFace {
+ #[\Override]
+ public const I = 'Changed';
+};
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/anon_parent.phpt b/Zend/tests/attributes/override/constants/anon_parent.phpt
new file mode 100644
index 000000000000..b430705c3c9d
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/anon_parent.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - anonymous class overrides parent class
+--FILE--
+<?php
+
+class Base {
+ public const C = 'C';
+}
+
+new class () extends Base {
+ #[\Override]
+ public const C = 'Changed';
+};
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/basic.phpt b/Zend/tests/attributes/override/constants/basic.phpt
new file mode 100644
index 000000000000..c89007022051
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/basic.phpt
@@ -0,0 +1,57 @@
+--TEST--
+#[\Override]: Constants - basic
+--FILE--
+<?php
+
+interface I {
+ public const I = 'I';
+}
+
+interface II extends I {
+ #[\Override]
+ public const I = 'I';
+}
+
+class P {
+ public const C1 = 'C1';
+ public const C2 = 'C2';
+ public const C3 = 'C3';
+ public const C4 = 'C4';
+}
+
+class PP extends P {
+ #[\Override]
+ public const C1 = 'C1';
+ public const C2 = 'C2';
+ #[\Override]
+ public const C3 = 'C3';
+}
+
+class C extends PP implements I {
+ #[\Override]
+ public const I = 'I';
+ #[\Override]
+ public const C1 = 'C1';
+ #[\Override]
+ public const C2 = 'C2';
+ public const C3 = 'C3';
+ #[\Override]
+ public const C4 = 'C4';
+ public const C = 'C';
+}
+
+enum E implements I {
+ #[\Override]
+ public const I = 'I';
+}
+
+enum WithCase implements I {
+ #[\Override]
+ case I;
+}
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/enum_failure.phpt b/Zend/tests/attributes/override/constants/enum_failure.phpt
new file mode 100644
index 000000000000..77ba225e9b9e
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/enum_failure.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - no interface for enum constant
+--FILE--
+<?php
+
+enum Demo {
+ #[\Override]
+ public const C = 'C';
+}
+
+echo "Done";
+
+?>
+--EXPECTF--
+Fatal error: Demo::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/enum_failure_case.phpt b/Zend/tests/attributes/override/constants/enum_failure_case.phpt
new file mode 100644
index 000000000000..2f2c6fc84a08
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/enum_failure_case.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - no interface for enum case
+--FILE--
+<?php
+
+enum Demo {
+ #[\Override]
+ case C;
+}
+
+echo "Done";
+
+?>
+--EXPECTF--
+Fatal error: Demo::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/failure.phpt b/Zend/tests/attributes/override/constants/failure.phpt
new file mode 100644
index 000000000000..be621915d927
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/failure.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - no interface or parent class
+--FILE--
+<?php
+
+class Demo {
+ #[\Override]
+ public const C = 'C';
+}
+
+echo "Done";
+
+?>
+--EXPECTF--
+Fatal error: Demo::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/interface_failure.phpt b/Zend/tests/attributes/override/constants/interface_failure.phpt
new file mode 100644
index 000000000000..0aa4d33d1301
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/interface_failure.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - no parent interface
+--FILE--
+<?php
+
+interface IFace {
+ #[\Override]
+ public const I = 'I';
+}
+
+echo "Done";
+
+?>
+--EXPECTF--
+Fatal error: IFace::I has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/trait_failure.phpt b/Zend/tests/attributes/override/constants/trait_failure.phpt
new file mode 100644
index 000000000000..8d4af0e57977
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_failure.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - on a trait, no interface or parent class
+--FILE--
+<?php
+
+trait DemoTrait {
+ #[\Override]
+ public const T = 'T';
+}
+
+class UsesTrait {
+ use DemoTrait;
+}
+
+echo "Done";
+
+?>
+--EXPECTF--
+Fatal error: UsesTrait::T has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/trait_interface.phpt b/Zend/tests/attributes/override/constants/trait_interface.phpt
new file mode 100644
index 000000000000..3a2ba799ae4e
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_interface.phpt
@@ -0,0 +1,23 @@
+--TEST--
+#[\Override]: Constants - on a trait, overrides interface
+--FILE--
+<?php
+
+trait DemoTrait {
+ #[\Override]
+ public const C = 'Changed';
+}
+
+interface IFace {
+ public const C = 'C';
+}
+
+class UsesTrait implements IFace {
+ use DemoTrait;
+}
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/trait_parent.phpt b/Zend/tests/attributes/override/constants/trait_parent.phpt
new file mode 100644
index 000000000000..2753aaf546a2
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_parent.phpt
@@ -0,0 +1,23 @@
+--TEST--
+#[\Override]: Constants - on a trait, overrides parent class
+--FILE--
+<?php
+
+trait DemoTrait {
+ #[\Override]
+ public const C = 'Changed';
+}
+
+class Base {
+ public const C = 'C';
+}
+
+class UsesTrait extends Base {
+ use DemoTrait;
+}
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/trait_redeclared.phpt b/Zend/tests/attributes/override/constants/trait_redeclared.phpt
new file mode 100644
index 000000000000..984f9eedb668
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_redeclared.phpt
@@ -0,0 +1,21 @@
+--TEST--
+#[\Override]: Constants - trait constant redeclared, not overridden
+--FILE--
+<?php
+
+trait DemoTrait {
+ public const T = 'T';
+}
+
+class UsesTrait {
+ use DemoTrait;
+
+ #[\Override]
+ public const T = 'T';
+}
+
+echo "Done";
+
+?>
+--EXPECTF--
+Fatal error: UsesTrait::T has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt b/Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt
new file mode 100644
index 000000000000..21a1aa532249
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt
@@ -0,0 +1,25 @@
+--TEST--
+#[\Override]: Constants - trait constant redeclared, overrides interface
+--FILE--
+<?php
+
+interface IFace {
+ public const I = 'I';
+}
+
+trait DemoTrait {
+ public const I = 'I';
+}
+
+class UsesTrait implements IFace {
+ use DemoTrait;
+
+ #[\Override]
+ public const I = 'I';
+}
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt b/Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt
new file mode 100644
index 000000000000..c3d1e43d2534
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt
@@ -0,0 +1,25 @@
+--TEST--
+#[\Override]: Constants - trait constant redeclared, overrides parent class
+--FILE--
+<?php
+
+class Base {
+ public const I = 'I';
+}
+
+trait DemoTrait {
+ public const I = 'I';
+}
+
+class UsesTrait extends Base {
+ use DemoTrait;
+
+ #[\Override]
+ public const I = 'I';
+}
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/trait_unused.phpt b/Zend/tests/attributes/override/constants/trait_unused.phpt
new file mode 100644
index 000000000000..785f2888e90b
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_unused.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - on a trait, unused
+--FILE--
+<?php
+
+trait Demo {
+ #[\Override]
+ public const T = 'T';
+}
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/visibility_01.phpt b/Zend/tests/attributes/override/constants/visibility_01.phpt
new file mode 100644
index 000000000000..49671471f758
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/visibility_01.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - private constant not overridden (by public constant)
+--FILE--
+<?php
+
+class Base {
+ private const C = 'C';
+}
+
+class Child extends Base {
+ #[\Override]
+ public const C = 'Changed';
+}
+
+echo "Done";
+
+?>
+--EXPECTF--
+Fatal error: Child::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/visibility_02.phpt b/Zend/tests/attributes/override/constants/visibility_02.phpt
new file mode 100644
index 000000000000..c160a29a6180
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/visibility_02.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - private constant not overridden (by private constant)
+--FILE--
+<?php
+
+class Base {
+ private const C = 'C';
+}
+
+class Child extends Base {
+ #[\Override]
+ private const C = 'Changed';
+}
+
+echo "Done";
+
+?>
+--EXPECTF--
+Fatal error: Child::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/visibility_03.phpt b/Zend/tests/attributes/override/constants/visibility_03.phpt
new file mode 100644
index 000000000000..db460f24375e
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/visibility_03.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - protected constant is overridden (by public constant)
+--FILE--
+<?php
+
+class Base {
+ protected const C = 'C';
+}
+
+class Child extends Base {
+ #[\Override]
+ public const C = 'Changed';
+}
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/visibility_04.phpt b/Zend/tests/attributes/override/constants/visibility_04.phpt
new file mode 100644
index 000000000000..0ad46b022f43
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/visibility_04.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - protected constant is overridden (by protected constant)
+--FILE--
+<?php
+
+class Base {
+ protected const C = 'C';
+}
+
+class Child extends Base {
+ #[\Override]
+ protected const C = 'Changed';
+}
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php
index ded9c89593a3..05fd285d5b9f 100644
--- a/Zend/zend_attributes.stub.php
+++ b/Zend/zend_attributes.stub.php
@@ -68,7 +68,7 @@ public function __debugInfo(): array {}
/**
* @strict-properties
*/
-#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_PROPERTY)]
+#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_PROPERTY|Attribute::TARGET_CLASS_CONSTANT)]
final class Override
{
public function __construct() {}
diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h
index 54a66af29966..8acac398426f 100644
--- a/Zend/zend_attributes_arginfo.h
+++ b/Zend/zend_attributes_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit zend_attributes.stub.php instead.
- * Stub hash: b868cb33f41d9442f42d0cec84e33fcc09f5d88c */
+ * Stub hash: bae42fcb945360f90a1e2762c6c22177f25efb9e */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL")
@@ -230,7 +230,7 @@ static zend_class_entry *register_class_Override(void)
zend_string *attribute_name_Attribute_class_Override_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true);
zend_attribute *attribute_Attribute_class_Override_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Override_0, 1);
zend_string_release_ex(attribute_name_Attribute_class_Override_0, true);
- ZVAL_LONG(&attribute_Attribute_class_Override_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_PROPERTY);
+ ZVAL_LONG(&attribute_Attribute_class_Override_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_PROPERTY | ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
return class_entry;
}
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 4c1375215888..c75333e6d3c5 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -9370,6 +9370,15 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
+
+ const zend_attribute *override = zend_get_attribute_str(c->attributes, "override", sizeof("override") - 1);
+ if (override) {
+ ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_OVERRIDE;
+ /* We need to be able to remove the flag once the override is
+ * resolved. See ZEND_ACC_DEPRECATED above. */
+ ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
}
}
}
@@ -9829,6 +9838,16 @@ static void zend_compile_enum_case(zend_ast *ast)
if (deprecated) {
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED;
}
+
+ const zend_attribute *override = zend_get_attribute_str(c->attributes, "override", sizeof("override") - 1);
+ if (override) {
+ ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_OVERRIDE;
+ /* We need to be able to remove the flag once the override is
+ * resolved. See ZEND_ACC_DEPRECATED handling in
+ * zend_compile_class_const_decl(). */
+ enum_class->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
+ enum_class->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
}
}
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 848b9d209b2d..583b10d34188 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -2115,6 +2115,12 @@ static bool do_inherit_constant_check(
);
}
+ if (!(ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_PRIVATE)) {
+ if (child_constant->ce == ce) {
+ ZEND_CLASS_CONST_FLAGS(child_constant) &= ~ZEND_ACC_OVERRIDE;
+ }
+ }
+
if (!(ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_PRIVATE) && ZEND_TYPE_IS_SET(parent_constant->type)) {
inheritance_status status = class_constant_types_compatible(parent_constant, child_constant);
if (status == INHERITANCE_ERROR) {
@@ -2317,6 +2323,15 @@ void zend_inheritance_check_override(const zend_class_entry *ce)
}
} ZEND_HASH_FOREACH_END();
+ ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, zend_string *name, zend_class_constant *c) {
+ if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_OVERRIDE) {
+ zend_error_noreturn(
+ E_COMPILE_ERROR,
+ "%s::%s has #[\\Override] attribute, but no matching parent constant exists",
+ ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ }
+ } ZEND_HASH_FOREACH_END();
+
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, zend_property_info *prop) {
if (prop->flags & ZEND_ACC_OVERRIDE) {
zend_error_noreturn(