[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(
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.