====== PHP RFC: Extend #[\Override] to target properties ====== * Version: 1.0 * Date: 2025-07-07 * Author: Jiří Pudil, me@jiripudil.cz * Status: Under Discussion * Implementation: https://github.com/php/php-src/pull/19061 ===== Introduction ===== This is a follow-up to [[https://wiki.php.net/rfc/marking_overriden_methods|a previous RFC]] which introduced the ''#[\Override]'' attribute to explicitly express the intent of a method to override a parent method or implement an interface. During inheritance, PHP then checks that the method actually exists in the parent hierarchy or in one of the implemented interfaces. The original RFC explicitly excluded properties:
As of now properties may not be part of an interface and thus only properties of a parent class can be overridden. The type of properties is enforced to be invariant and properties do not have behavior attached. A property can only ever be overridden by a compatible property with possibly added attributes.
A lot has changed in PHP 8.4 and, as a result, some of these premises are no longer true: * Abstract properties can be declared in classes and interfaces. * Under specific circumstances, property types may be covariant during inheritance. * Properties may include behaviour in the form of hooks. (This is not relevant here, though. Since hooks are implemented as methods, they already support the ''#[\Override]'' attribute.) Overall, these changes have allowed properties to become a more prominent part of the public API of a class or an interface, and I think it warrants the original decision to be revisited. ===== Proposal ===== I propose to extend the target of the ''#[\Override]'' attribute to include properties. If this attribute is added to a property, the engine validates that a property with the same name exists in a parent class or any of the implemented interfaces and emits a compilation error if no such property exists. The semantics are the same as with methods: * Public and protected properties of a parent class or implemented interface satisfy ''#[\Override]''. * Abstract properties satisfy ''#[\Override]''. * Static properties behave as instance properties. * Private properties of a parent class do not satisfy ''#[\Override]'' because they are not part of the public API. * ''#[\Override]'' is ignored on traits, but properties from a used trait behave as if the property definition was copied and pasted into the target class. Specifically the ''#[\Override]'' attribute on a trait property requires the existence of a matching property in a parent class or implemented interface. * ''#[\Override]'' works as expected on anonymous classes. * ''#[\Override]'' works as expected on interfaces: a matching property needs to exist in a parent interface. * ''#[\Override]'' in this context does not apply to enums because they are not allowed to have properties. * ''#[\Override]'' can be attached to promoted properties. ===== Examples ===== class P { abstract public mixed $p { get; } } class C extends P { #[\Override] public mixed $p; } trait T { #[\Override] public mixed $p; } interface I { public mixed $p { get; } } class C implements I { use T; } class C { #[\Override] public mixed $c; // Fatal error: C::$c has #[\Override] attribute, but no matching parent property exists } interface I { #[\Override] public mixed $i; // Fatal error: I::$i has #[\Override] attribute, but no matching parent property exists } interface I { public mixed $i { get; } } class P { #[\Override] public mixed $i; // Fatal error: P::$i has #[\Override] attribute, but no matching parent property exists } class C extends P implements I {} class P { private mixed $p; } class C extends P { #[\Override] public mixed $p; // Fatal error: C::$p has #[\Override] attribute, but no matching parent property exists } ===== Precedent in other programming languages ===== * TypeScript's ''override'' keyword [[https://www.typescriptlang.org/play/?#code/MYGwhgzhAEBiD29oG8C+AodpIwEJgCdoBTADwBdiA7AExgSWXWhegAcBXAIxAEtho8AG7ECBXjWLRSALmhUOAWy6joAXmgAWAEwBudBiAhttps://www.typescriptlang.org/play/?#code/MYGwhgzhAEBiD29oG8C+AodpIwEJgCdoBTADwBdiA7AExgSWXWhegAcBXAIxAEtho8AG7ECBXjWLRSALmhUOAWy6joAXmgAWAEwBudBiA|works with properties]] too. * C# has a //required// ''override'' modifier as part of the property declaration: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/override * Swift has a //required// ''override'' modifier as part of the property declaration: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/inheritance/#Overriding-Properties * Kotlin has a //required// ''override'' modifier as part of the property declaration: https://kotlinlang.org/docs/inheritance.html#overriding-properties ===== Backward Incompatible Changes ===== None. ===== Proposed PHP Version(s) ===== Next minor (8.5) ===== RFC Impact ===== ==== To the Ecosystem ==== IDEs and static analyzers will likely want to extend their rules for ''#[\Override]'' to properties. ==== To Existing Extensions ==== Extensions might want to add the attribute to their properties where appropriate. ==== To SAPIs ==== None. ===== Voting Choices ===== * Yes * No ===== Patches and Tests ===== * https://github.com/php/php-src/pull/19061 ===== Implementation ===== After the RFC is implemented, this section should contain: - the version(s) it was merged into - a link to the git commit(s) - a link to the PHP manual entry for the feature ===== References ===== * [[https://wiki.php.net/rfc/marking_overriden_methods|RFC: Marking overridden methods]] ===== Rejected Features ===== Keep this updated with features that were discussed on the mail lists. ===== Changelog ===== If there are major changes to the initial proposal, please include a short summary with a date or a link to the mailing list announcement here, as not everyone has access to the wikis' version history.