rfc:interface-default-methods
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:interface-default-methods [2022/09/28 23:18] – Fix isEmpty impl levim | rfc:interface-default-methods [2023/07/17 15:06] (current) – Status: declined levim | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Interface Default Methods ====== | ====== PHP RFC: Interface Default Methods ====== | ||
- | * Version: | + | * Version: |
* Date: 2022-06-27 | * Date: 2022-06-27 | ||
* Author: Levi Morrison, levim@php.net | * Author: Levi Morrison, levim@php.net | ||
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
Line 9: | Line 9: | ||
===== Introduction ===== | ===== Introduction ===== | ||
Over time, authors sometimes want to add methods to an interface. Today, this causes large breakages to every implementor. This RFC proposes a way to reduce the scale of breakage. | Over time, authors sometimes want to add methods to an interface. Today, this causes large breakages to every implementor. This RFC proposes a way to reduce the scale of breakage. | ||
+ | |||
+ | Additionally, | ||
+ | |||
+ | For example, see [[https:// | ||
===== Proposal ===== | ===== Proposal ===== | ||
- | Interface methods can now provide a default | + | Interface methods can now provide a method body. This implementation |
<PHP> | <PHP> | ||
Line 26: | Line 30: | ||
} | } | ||
</ | </ | ||
+ | |||
+ | Interface methods can also now be < | ||
+ | |||
+ | The methods with default implementations are inherited similarly to abstract base classes, as opposed to how traits behave. For instance, a private method on the interface is not accessible to the class which implements the interface; with a trait it would be. | ||
==== Default Method Resolution ==== | ==== Default Method Resolution ==== | ||
Line 36: | Line 44: | ||
- If the above rules fail, then the class needs to implement the method itself, possibly delegating to one of the interfaces. | - If the above rules fail, then the class needs to implement the method itself, possibly delegating to one of the interfaces. | ||
- | This RFC proposes the same. However, rule 2 has not yet been implemented so I am unsure how feasible it is. | + | This RFC proposes the same. However, rule 2 is only partially implemented at this time. |
Here's an example of a class delegating to another method: | Here's an example of a class delegating to another method: | ||
Line 57: | Line 65: | ||
(new Class1())-> | (new Class1())-> | ||
</ | </ | ||
+ | |||
+ | ==== Parent Scoping ==== | ||
+ | In interface default methods, you cannot use the < | ||
+ | |||
+ | <PHP> | ||
+ | interface Interface1 { | ||
+ | function method1() { echo __METHOD__, PHP_EOL; } | ||
+ | } | ||
+ | |||
+ | interface Interface2 extends Interface1 { | ||
+ | function method1() { parent:: | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The reason is that interfaces can extend multiple other interfaces e.g. | ||
+ | |||
+ | <PHP> | ||
+ | interface Interface3 extends Interface1, Interface2 {} | ||
+ | </ | ||
+ | |||
+ | The name of the interface should be used instead: | ||
+ | |||
+ | <PHP> | ||
+ | interface Interface2 extends Interface1 { | ||
+ | function method1() { Interface1:: | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | However, if a sub-class of the inheriting class calls '' | ||
+ | |||
+ | <PHP> | ||
+ | interface Interface1 { | ||
+ | function method1() { echo __METHOD__, PHP_EOL; } | ||
+ | } | ||
+ | |||
+ | class Class1 implements Interface1 { | ||
+ | // Inherits Interface1:: | ||
+ | } | ||
+ | |||
+ | class Class2 extends Class1 { | ||
+ | function method1() { parent:: | ||
+ | } | ||
+ | |||
+ | (new Class2())-> | ||
+ | // output: | ||
+ | // Interface1:: | ||
+ | </ | ||
+ | |||
+ | ==== Cancelling Default Methods ==== | ||
+ | If an interface method extends a parent interface method which has a default, this prevents using the default method for classes which implement the child interface but do not directly implement the parent one: | ||
+ | |||
+ | <PHP> | ||
+ | interface Interface1 { | ||
+ | function method1() { echo __METHOD__, " | ||
+ | } | ||
+ | |||
+ | interface Interface2 extends Interface1 { | ||
+ | function method1(); | ||
+ | } | ||
+ | |||
+ | /* Would be an error because method1 has not been implemented. | ||
+ | class Class1 implements Interface2 { | ||
+ | // error: method1 has not been implemented. | ||
+ | } | ||
+ | */ | ||
+ | |||
+ | // This is subtly different, but valid: | ||
+ | class Class1 implements Interface1, Interface2 {} | ||
+ | </ | ||
+ | |||
+ | The behavior could go either way. I picked this behavior because if it's wrong, it's easier to correct than the other way around. | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
Line 63: | Line 142: | ||
If you do use the feature: | If you do use the feature: | ||
* Adding a default implementation to an existing interface method will not break existing code, because every existing usage has a higher priority than the default. | * Adding a default implementation to an existing interface method will not break existing code, because every existing usage has a higher priority than the default. | ||
- | * If you add a new method to an interface, there is a compatibility break. The impact of the break is limited to places where the usage of the interface has a method of the same name. | + | * If you add a new method to an interface, there is a compatibility break. The impact of the break is limited to places where the implementor/ |
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
- | PHP 8.NEXT. | + | PHP 8.3. |
===== RFC Impact ===== | ===== RFC Impact ===== | ||
- | ==== To Existing | + | ==== To Extensions ==== |
- | Modules can specify an interface implementation as well. | + | Modules can specify an interface implementation as well. These internal default methods should not be marked with '' |
==== To Opcache ==== | ==== To Opcache ==== | ||
- | Opcache should also work with this feature. | + | Opcache should also work with this feature. The proof of concept implementation has not triggered any issues so far in CI. |
+ | |||
+ | ==== To the Ecosystem ==== | ||
+ | Previously, interface methods were not allowed to have method bodies. These tools such as parsers, code analyzers, etc will need to be updated. | ||
===== Open Issues ===== | ===== Open Issues ===== | ||
Line 87: | Line 169: | ||
===== Voting ===== | ===== Voting ===== | ||
The vote will be a simple yes/no vote on whether to include the feature. | The vote will be a simple yes/no vote on whether to include the feature. | ||
+ | <doodle title=" | ||
+ | * Yes | ||
+ | * No | ||
+ | </ | ||
+ | |||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | WIP: https:// | + | Here is a work-in-progress pull request: https:// |
===== Implementation ===== | ===== Implementation ===== |
rfc/interface-default-methods.1664407105.txt.gz · Last modified: 2022/09/28 23:18 by levim