Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow declaring abstract final methods or classes to allow interface/abstract class providers to enforce exactly one non-abstract child implementation #18118

Closed
stepo2 opened this issue Mar 20, 2025 · 5 comments

Comments

@stepo2
Copy link

stepo2 commented Mar 20, 2025

Description

I always wondered why PHP does not allow abstract final methods or classes. This way an interface or an abstract class could enforce that only the first non-abstract child class in the hierarchy must implement the class or the method body, preventing further inheritance of the class/method.

Of course, the implementor of the first non-abstract class/method can optionally mark the first implementation as final already if he likes. But the interface/abstract provider currently can not enforce this, however. If it is optionally implementable from the concrete child perspective it should also be optionally enforcable from the abstract parent perspective.

@stepo2 stepo2 changed the title Allow declaring abstract final methods or classes to allow interface/abstract class providers to enforce _exactly one_ non-abstract child implementation Allow declaring abstract final methods or classes to allow interface/abstract class providers to enforce exactly one non-abstract child implementation Mar 20, 2025
@iluuu1994
Copy link
Member

Hi @stepo2

Your definition of final doesn't match the implementation though (nor the implementation of any other language, as far as I'm aware). final means the very declaration containing the keyword is locked, making it a "leaf" in the inheritance tree. For the engine, final means we can guarantee the method call is not polymorphic. By allowing final on abstract classes, that definition is no longer correct.

I'm also unsure what use-cases this feature would actually solve. Can you elaborate on that?

In any case, this is likely controversial enough to require an RFC. Check out the howto guide on how to do that. The first step is sending an e-mail to the internals mailing list and seeing how your idea is perceived.

@stepo2
Copy link
Author

stepo2 commented Mar 21, 2025

If re-using final is not possible, then a solution would be another keyword for this very purpose, like once for example (that is only allowed within/on interface and abstract classes/methods, and that enfores the implementor to mark the very first implementation as final.

I'm also unsure what use-cases this feature would actually solve.

For once, pure academic completeness. I can use PHP without declaring any explicit types, but I also optionally can use PHP to declare types and enforce them. I can use classes without write-access - I can however also optionally enforce them as such by using readonly. I can respect visibility by pure will-power, but I can also optionally enforce it (protected, private). And so on. So, also being able to enforce final from an abstract scope in some way just seems logical.

But to come to the real world: some languages have classes "final-by-default", it is good practice to not have too deep inheritance trees. Imagine an extendable library providing a set of final classes: I may want to enforce the library-users to stick to this architecture in their user-land code when they extend from the interfaces or abstract base classes - or to enforce contributors to my library to stick to this pattern. The use-case here is "the possibility to enforce keeping architecture consistent".

@kallesommernielsen
Copy link

There was a discussion about this from a decade ago that might be a worthwhile addition to this conversation:
https://externals.io/message/79211#79211

@nielsdos
Copy link
Member

Thanks for the link. Given that this was RFC'd before and declined, introducing this change would definitely require a new RFC.

@iluuu1994
Copy link
Member

Also note that the linked RFC has a different semantic meaning for abstract final, namely by keeping the original meaning of final to make the class fully uninstantiable.

I can use PHP without declaring any explicit types, but I also optionally can use PHP to declare types and enforce them.

But what you can't do is to omit a type, while enforcing the child to declare one. That's closer what this new keyword would do.

it is good practice to not have too deep inheritance trees

Sure, I agree. But this seems like the wrong way to enforce that. 1. Many classes are deeply nested without implementing an interface or inheriting an abstract class. 2. The very point of an interface is to abstract away the details of a class. Why then does it care whether the class has sub-classes?

My feelings aside, I think the only way for this to move forward is to discuss it on the internals mailing list. If you would like to see this happen, please push the discussion forward. Thank you for the proposal!

@iluuu1994 iluuu1994 closed this as not planned Won't fix, can't repro, duplicate, stale Mar 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants