在PHP中“重载”一个私有方法

时间:2013-08-17 12:22:52

标签: php oop overloading

我知道我不能在PHP中重载方法。而且,据我所知,类中的private方法对于扩展基类的类是不可见的。那为什么这不起作用?

class Base {
  private function foo($arg) {
     print "Base $arg";
  }
}

class Child extends Base {
  public function foo() {
     print "Child";
  }
}

$c = new Child;
print $c->foo();

错误:

PHP Strict Standards: Declaration of Child::foo() should be compatible with Base::foo($arg) in /var/www/boludo.php on line 17

我认为foo($arg)类在Child类中是不可见的,因为privatefoo。所以,我没有超载foo,我只是创建了一个名为{{1}}的方法。

2 个答案:

答案 0 :(得分:3)

您可以使用__call函数在PHP中执行函数重载:http://www.php.net/manual/en/language.oop5.overloading.php#object.call

除此之外,你的问题是这样,你违反了Substitutability原则: http://en.wikipedia.org/wiki/Liskov_substitution_principle

PHP使用的东西。这样,如果将Base类类型的对象替换为具有Child类类型的对象,则违反Substitutability。您正在更改派生类中的基类的接口,删除方法foo(...)的参数,这样,在不破坏程序的情况下,Base类类型的对象不能替换为Child类类型的对象,因此违反了Liskov的可替代性原则(LSP)。

答案 1 :(得分:3)

要修复通知,只需将子级中的foo()更改为

即可
public function foo($arg = null) {

关于“为什么这不起作用”的问题:

PHP中的可见性严格来说是运行时访问。它不会影响扩展/组合/重载类和方法的方式。从子类型中的超类型中松开私有方法的可见性将在子类型中添加单独的方法,而不能访问超类型中的相同命名方法。但是,PHP将为这些提供父子关系。但这并没有引起通知。至少,不是它本身。

您收到通知的原因是,您当时也在尝试更改方法签名。您的foo()不再需要$arg传递给它。假设方法之间存在父子关系时,这是一个问题,因为Liskov Substitution Principle表示“如果S是T的子类型,那么类型T的对象可以用S类型的对象替换“没有破坏程序。换句话说:如果您的代码使用Base,则应该能够将Base替换为Child,并且该程序仍然可以像使用Base一样工作。

假设您的Base也有公开方法bar()

class SomeClientUsingBase
{
    public function doSomethingWithBase(Base $base)
    {
        $result = $base->bar();
        // …

现在假设Child更改bar()需要参数。如果然后将Child Base传递到客户端,则会中断客户端,因为客户端在没有参数的情况下调用$base->bar();

显然,您可以更改客户端以传递参数,但是代码实际上取决于Child如何定义方法,因此Typehint是错误的。事实上,Child不是Base,因为它的行为不像Base。那时它的遗传就破了。

现在有趣的是,如果从$arg中删除foo(),那么从技术上讲,您不会违反LSP,因为客户端仍然有效。通知在这里是错误的。在先前使用$base->foo(42)的客户端中调用Base仍然可以使用Child,因为Child可以简单地忽略该参数。但PHP希望你让参数可选。

请注意,LSP也适用于方法可能返回的内容。 PHP只是在签名中不包含返回类型,因此您自己考虑了这一点。您的方法必须返回Supertype返回的内容或行为等效的内容。