I know I cannot overload methods in PHP. And, as far as I know, private
methods in a class are invisible to classes that extend the base class. So why this does not work?
class Base {
private function foo($arg) {
print "Base $arg";
}
}
class Child extends Base {
public function foo() {
print "Child";
}
}
$c = new Child;
print $c->foo();
The error:
PHP Strict Standards: Declaration of Child::foo() should be compatible with Base::foo($arg) in /var/www/boludo.php on line 17
I assumed that foo($arg)
method is invisible in Child
class because is private
. So, I'm not overloading foo
, I'm just creating a method called foo
.
You can do function overloading in PHP using __call function: http://www.php.net/manual/en/language.oop5.overloading.php#object.call
Apart from that, your problem is that in that way, you violate the Substitutability principle: http://en.wikipedia.org/wiki/Liskov_substitution_principle
Something that PHP uses. In that way, if you replace an object of Base class type to one with a Child class type, Substitutability is violated. You are changing the interface of the base class in the derived one, removing the argument of method foo(...) and in this way, objects of Base class type can not be replaced with objects of Child class type without breaking the program, thus violating Liskov's Substitutability Principle (LSP).
To fix the Notice, simply change
foo()
in the Child toAs to the question "why does this not work":
Visibility in PHP is strictly about runtime access. It doesn't affect how you can extend/compose/overload classes and methods. Loosening the visibility of a private method from a Supertype in a Subtype will add a separate method in the subtype with no access to the same named method in the supertype. However, PHP will assume a parent-child relationship for these. That didn't cause the Notice though. At least, not on it's own.
The reason why you get the Notice, is that you are then also trying to change the method signature. Your
foo()
does no longer require$arg
to be passed to it. When you assume a parent-child relationship between the methods, this is a problem because the Liskov Substitution Principle states that "if S is a subtype of T, then objects of type T may be replaced with objects of type S" without breaking the program. In other words: if you have code that usesBase
, you should be able to replaceBase
withChild
and the program should still work as if it was usingBase
.Assume your
Base
also has a public methodbar()
.Now imagine
Child
changesbar()
to require an argument. If you then passChild
forBase
into the client, you will break the client, because the client calls$base->bar();
without an argument.Obviously, you could change the client to pass an argument, but then the code really depends on how
Child
defined the method, so the Typehint is wrong. In fact,Child
is not aBase
then, because it doesn't behave like aBase
. It's broken inheritance then.Now the funny thing is, if you remove that
$arg
fromfoo()
, you are technically not violating LSP, because the client would still work. The Notice is wrong here. Calling$base->foo(42)
in a client that previously usedBase
will still work with aChild
because theChild
can simply ignore the argument. But PHP wants you to make the argument optional then.Note that LSP also applies to what a method may return. PHP just doesn't include the return type in the signature, so you have take that into account yourself. Your methods have to return what the Supertype returned or something that is behaviorally equivalent.