覆盖私有方法时的奇怪行为

时间:2010-09-21 01:06:24

标签: php override private visibility

考虑以下代码:

class foo {
    private function m() {
        echo 'foo->m() ';
    }
    public function call() {
        $this->m();
    }
}

class bar extends foo {
    private function m() {
        echo 'bar->m() ';
    }
    public function callbar() {
        $this->m();
    }
}

$bar = new bar;

$bar->call();
$bar->callbar();

现在,更改m()方法的可见性,我得到:
+public-private

Visibility              bar->call()    bar->callbar() 
======================================================
-foo->m(), -bar->m()    foo->m()       bar->m()
-foo->m(), +bar->m()    foo->m()       bar->m()
+foo->m(), -bar->m()    ERROR          ERROR
+foo->m(), +bar->m()    bar->m()       bar->m()

protected似乎表现得像public)。

我期待一切都像宣布public时的行为一样。但是,虽然foo->call()bar->callbar()基本上是相同的,但它们会产生不同的结果,具体取决于m()foobar的可见性。为什么会这样?

3 个答案:

答案 0 :(得分:25)

继承/覆盖私有方法

在PHP中,子类中的方法(包括私有方法)是:

  • 复制;保持原始功能的范围。
  • 替换(“覆盖”,如果你愿意的话)。

您可以使用以下代码查看:

<?php
class A {
    //calling B::h, because static:: resolves to B::
    function callH() { static::h(); }
    private function h() { echo "in A::h"; }
}
class B extends A {
    //not necessary; just to make explicit what's happening
    function callH() { parent::callH(); }
}
$b = new B;
$b->callH();

现在,如果你覆盖私有方法,它的新范围将不是A,它将是B,并且调用将失败,因为A::callH()在范围A中运行:

<?php
class A {
    //calling B::h, because static:: resolves to B::
    function callH() { static::h(); }
    private function h() { echo "in A::h"; }
}
class B extends A {
    private function h() { echo "in B::h"; }
}
$b = new B;
$b->callH(); //fatal error; call to private method B::h() from context 'A'

调用方法

这里的规则如下:

  • 查看对象的实际类的方法表(在您的情况下为bar)。
    • 如果这会产生私有方法
      • 如果定义方法的范围与调用函数的范围相同并且与对象的类相同,则使用它。
      • 否则,在父类中查找与调用函数具有相同范围且名称相同的私有方法。
      • 如果没有找到满足上述要求之一的方法,则失败。
    • 如果这会产生公开/受保护的方法
      • 如果方法的范围被标记为已更改,我们可能已使用public / protected方法覆盖了私有方法。因此,在这种情况下,如果另外一个方法具有与调用函数范围定义的私有相同的名称,请使用该方法。
      • 否则,请使用找到的方法。

结论

  1. (均为私人)对于bar->call()call的范围为foo。调用$this->m()会在bar的{​​{1}}方法表中引发查找,从而产生私有m。但是,bar::m()的范围与调用范围不同,bar::m()。在遍历层次结构时会找到方法foo,而不是使用它。
  2. foo:m()中的私人,foo中的公开)bar的范围仍为call。查找产生公共foo。但是,其范围标记为已更改,因此在方法bar::m()的调用范围foo的函数表中进行查找。这会产生一个私有方法m(),其范围与调用范围相同,因此可以使用它。
  3. 这里没什么可看的,错误是因为能见度降低了。
  4. (两者都是公开的)foo:m()的范围仍为call。查找产生公共foo。其范围未标记为已更改(它们都是公开的),因此使用bar::m()

答案 1 :(得分:13)

私有方法不可覆盖,因为私有方法甚至对其子类也不可见。将方法定义为受保护意味着它在类本身或其子类之外是不可见的。

如果您要在父类中使用某个方法,但希望子级能够修改其行为,并且不希望外部使用此方法,请使用protected。如果您希望子类中的功能无法以任何方式由子类进行修改,请将方法定义为private

编辑:进一步澄清,如果在父类和子类中有两个同名的方法,并且这些方法被定义为私有,本质上子类方法与父类完全没有关系方法。如上所述,私有方法完全不可见于子类。

考虑一下:

class foo {
    private function m() {
        echo 'foo->m() ';
    }
    private function z() { echo "foo->z();"; }

    public function call() {
        $this->m();
    }
}

class bar extends foo {
    private function m() {
        echo 'bar->m() ';
    }
    public function callbar() {
        $this->m();
    }
    public function callz()
    {
       $this->z();
    }
}

致电$bar->callz();将产生一个ERROR,因为z根本不存在于子类中,甚至不作为继承方法。

答案 2 :(得分:3)

根据PHP手册:

  

宣布为私人的会员可能只是   由定义的类访问   会员。

http://www.php.net/manual/en/language.oop5.visibility.php

修改

  

它们会产生不同的结果   关于foo和f中m()的可见性   酒吧。为什么会这样?

如果m()中的foo是公开的,则可以覆盖。如果是m()来自bar的{​​{1}}覆盖m()中的foo