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
.
To fix the Notice, simply change foo()
in the Child to
public function foo($arg = null) {
As 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 uses Base
, you should be able to replace Base
with Child
and the program should still work as if it was using Base
.
Assume your Base
also has a public method bar()
.
class SomeClientUsingBase
{
public function doSomethingWithBase(Base $base)
{
$result = $base->bar();
// …
Now imagine Child
changes bar()
to require an argument. If you then pass Child
for Base
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 a Base
then, because it doesn't behave like a Base
. It's broken inheritance then.
Now the funny thing is, if you remove that $arg
from foo()
, 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 used Base
will still work with a Child
because the Child
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.
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).