小编典典

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

php

考虑以下代码:

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()方法的可见性,我得到:
+for public-for 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()in
foo和的可见性,它们会产生不同的结果bar。为什么会这样?


阅读 216

收藏
2020-05-29

共1个答案

小编典典

继承/覆盖私有方法

在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()在scope中运行而失败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)。
    • 如果产生 私有方法
    • 如果定义方法的范围与调用函数的范围相同并且与对象的类相同,请使用它。
    • 否则,请在父类中查找一个私有方法,该私有方法的范围与调用函数之一的范围相同,并且名称相同。
    • 如果找不到满足上述要求之一的方法,则失败。
    • 如果这产生了 公共/受保护的方法
    • 如果方法的范围标记为已更改,则我们可能已用私有/受保护的方法覆盖了私有方法。因此,在这种情况下,如果另外还有一个与调用函数的作用域定义的私有名称相同的方法,请改用该方法。
    • 否则,请使用找到的方法。

结论

  1. (均为私人)对于bar->call(),范围callfoo。调用会$this->m()barfor 的方法表中进行查找m,从而产生一个private bar::m()。但是,的范围bar::m()不同于调用范围foofoo:m()在遍历层次结构时找到该方法,而使用该方法。
  2. (私人foo,公共参与bar)的范围call仍然foo。查找产生公众bar::m()。但是,其作用域被标记为已更改,因此在foomethod 的调用作用域的功能表中进行了查找m()。这将产生一个foo:m()与调用范围具有相同范围的私有方法,因此将其代替。
  3. 在这里什么也看不到,因为可见度降低而出错。
  4. call均为公众)的范围仍然是foo。查找产生公众bar::m()。它的范围未标记为已更改(它们都是公开的),因此bar::m()使用。
2020-05-29