When using the interface, B
and C
are independently verified against the interface declaration. It doesn't matter that C extends B
. C
indirectly implements A
, and C::myfunction
is compatible with A::myfunction
, so no problem there. B::myfunction
is also compatible with A::myfunction
, so no problem there either.
Without the interface, B::myfunction
is the canonical declaration for that method, and since it accepts a parameter but the overriding C::myfunction
does not, a STRICT
warning is raised.
Basically, you want to make sure this code works:
if ($obj instanceof <classWhoseInterfaceIExpect>) {
$obj-><interfaceIExpect>();
}
In more concrete terms:
if ($obj instanceof A) {
$obj->myfunction();
}
Since A::myfunction
is canonically declared as accepting no arguments, the above code will always work. However, without the interface:
if ($obj instanceof B) {
$obj->myfunction('foo');
}
If B::myfunction
is the canonical declaration which accepts an argument, but C
delivers an implementation which does not, you've created a conflict. Hence the warning. This is to answer the question why it works the way it does, it's PHP's thinking explained.
Caveat: Yes, even if you use the interface, this will still create the same conflict:
interface A { ... }
class B implements A { ... }
class C extends B { ... } // conflicting implementation to B
$obj = new C;
if ($obj instanceof B) {
$obj->myfunction('foo');
}
C
is both an instanceof
A
and B
, yet it isn't compatible with both implementations of myfunction
at the same time. This is your problem for creating two conflicting implementations. Whether PHP should warn here as well or not is debatable. PHP can't have your cake and eat it too. Its type system is there to help you catch certain errors as early as possible; it's certainly not perfect and can't protect you from shooting your own foot from all possible angles.
You basically shouldn't change method signatures ever if you want to avoid such issues. That, or have the change be cascading downwards; meaning that each extending class is compatible with the method signature of its direct parent class. So C::myfunction
should accept at least one optional argument. Maybe PHP should catch this case, maybe it could be considered a bug that you're not getting a warning in all cases. Again, it's debatable and you should avoid getting into this situation to begin with.