Forcing method override of parent PHP class
I have a situation that, despite a significant amount of googling / SO'ing, seems to have eluded me. Here's the problem in a nutshell:
Situation
PHP 5.3+ (but not defined yet - I am actually running 5.5 / 6, but the environment tends to be flexible).
I am trying to write a solid implementation that will get other developers to follow the best practices.
I have a parent class:
class A {
public function doSomething() {
// does something
}
}
The standard use case is A::doSomething
.
Extension is required in special cases.
class B extends A {
public function doSomething() {
// does something .. AND
$this->doSomethingElse()
}
private function doSomethingElse() {
// does something else
}
}
If I want to do something, then I can A::doSomething
.
If I want to do something and do something, then I can B::doSomething
.
To be consistent with other developers' forc [ing] transition to best practice, I implemented the interface:
interface C {
public function doSomething() {
// does something else
}
public function doSomethingElse() {
// does something else
}
}
Class B then implements interface C. Sounds simple enough?
class B implements C {
public function doSomething() {
// does something .. AND
$this->doSomethingElse()
}
private function doSomethingElse() {
// does something else
}
}
I don't want to assume that the child B
implements C
.
There may be a number of well-founded reasons for expanding A
to E
where one E
does not want to implement C
.
My question is:
If I do the following and don't implement the method doSomething
, then the following has no errors:
class B implements C {
private function doSomethingElse() {
// does something else
}
}
.. as expected; A
provides doSomething
for B
to meet the requirement for C
.
The problem is that this would allow someone to write an incomplete method that would not throw errors. In the above case, B::doSomething
does not call B::doSomethingElse
.
Is it possible for a method, interface (or similar method) to require a child in order to implement the method at the current inheritance level?
Of course I can write notes, documentation, etc., but the point of the interface is to get people to do it right!
Other people who asked similar questions were either (sometimes understandable) or misunderstood, or the response to stocks was "wrong architecture" ... Which I would like to dispute, as shown in the example below:
Here's an example with real world elements:
- A = Shop for sale: ShopItem
- B = Beer Shop Item: Beer
- C = BoozeInterface: the interface that .. talks about the requirements for startup IDs
- D = Vegetable shop item: Vegetables
A might have a method called for example ... A::attemptToBuy
C
will apply methods like id.
Beer
and Vegetable
- both ShopItem types.
This is standard and logical inheritance.
Beer
should use BoozeInterface
(or something suitable). This also applies to not yet implemented, but possibly future requirements Wine
, Spirit
etc. Etc.
Some elements are perfect for the general class ShopItem
. They don't need any additional functionality.
ShopItem::attemptToBuy
is a common use case.
However - I have to rely on someone to remember the overrides Wine::attemptToBuy
and Spirit::attemptToBuy
.
As you can see - if I really want to block it, I would ideally be able to force it to override the current inheritance level. (I'm sure there are better examples of this, but I've tried to make it as obvious as possible.)
I'm glad if the answer is "no, you can't do that" ... I just want to know if you can. I'm working with code, but just with a direct override that is not provided. But I want it to be forced!
Thanks in advance. Rick
source to share
I think you are going backwards on it - I am not sure if you want to apply override (easy to do with abstract methods) in subclasses created by the developer client, you want to provide a method that cannot be overridden in the abstract classes you provide. to ensure that it does what you want?
If you provide abstract classes ShopItem
and BoozeItem
thus:
<?php
// base ShopItem with default `attemptToBuy` functionality
abstract class ShopItem {
public function attemptToBuy() {
echo "buying ShopItem";
}
}
// Booze sub-class, with an override on `attemptToBuy`
// (e.g. applying age restrictions check)
abstract class BoozeItem extends ShopItem {
final public function attemptToBuy() {
echo "buying BoozeItem";
}
}
?>
The client developer can then happily create their own class Beer
that extends the subclass BoozeItem
.
<?php
class Beer extends BoozeItem { }
$oBevvy = new Beer();
$oBevvy->attemptToBuy();
?>
Outputs: Buying BoozeItem
Since it is BoozeItem::attemptToBuy()
declared final
, it cannot be overridden Booze::attemptToBuy()
(trying to create this method will throw an error) - therefore the functionality of your object is locked.
Beer
can be extended to Lager
or Ale
(for example) and they inherit attemptToBuy()
from BoozeItem
(as Beer
extends BoozeItem
) , but they won't be allowed to override this method as it is declared final
- it just inherits with certain behavior. This is not what you want, but it is probably the closest thing you can think of.
source to share