重写私有方法时的奇怪行为

Strange behavior when overriding private methods

提问人:NullUserException 提问时间:9/21/2010 最后编辑:NullUserException 更新时间:9/21/2010 访问量:21016

问:

请考虑以下代码段:

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();

现在,更改方法的可见性,我得到:
( for , for
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

我本来以为一切都会像两者都声明时那样运行.但是,尽管 和本质上是一回事,但它们会产生不同的结果,具体取决于 in 和 的可见性。为什么会这样?publicfoo->call()bar->callbar()m()foobar

PHP 覆盖 私有 可见性

评论

0赞 user2556058 8/4/2013
它在结果的第三行播放不同,我的是 foo->m() bar->m(),没有发生错误
0赞 user2556058 8/4/2013
至于澄清目的,这里是您的第三个案例的链接 stackoverflow.com/questions/18010637/......
0赞 StasM 8/17/2015
foo::call() 和 bar::callbar() 不是一回事。它们具有不同的范围。作用域定义是否可以调用私有方法。可以这样想:私有方法是类的实现细节,因此只有该类中的代码才能调用它。

答:

20赞 Sam Day 9/21/2010 #1

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

如果要从父类使用方法,但希望子类能够修改其行为,并且不希望此方法在外部可用,请使用 .如果希望父类中的功能不能被子类以任何方式修改,请将方法定义为 。protectedprivate

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

考虑一下:

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();
    }
}

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

评论

0赞 NullUserException 9/21/2010
“私有方法是不可覆盖的”,这显然是;如果不是,那么为什么会这样做呢?bar->callbar()
0赞 Chris Laplante 9/21/2010
当 in 时,可以覆盖 。否则,将覆盖任何内容;这就是为什么只有在第 4 种情况下才会产生:m()foopublicbarm()barbar->call()bar->m()
1赞 Sam Day 9/21/2010
@NullUserException:当你调用 bar->callbar() 时,它正在调用 bar->m。仅仅因为两个方法具有相同的名称并不意味着一个方法被重写(尤其是在处理私有方法时)。
1赞 Sam Day 9/21/2010
@NUE:我试图进一步澄清。如果这仍然不能解决您的问题,请告诉我。
3赞 Artefacto 9/21/2010
-1 私有方法对它的子类并不是完全不可见的,它实际上是复制到它们的子类中。所以他们知道它们存在,他们只是不能调用它(错误消息甚至不同)。
3赞 Chris Laplante 9/21/2010 #2

根据PHP手册:

被声明为私人的会员只能 由定义 成员。

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

编辑

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

如果 in 是公共的,则它是可覆盖的。在这种情况下,从 中的覆盖。m()foom()barm()foo

26赞 Artefacto 9/21/2010 #3

继承/重写私有方法

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

结论

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

评论

0赞 AlexBor 12/26/2022
您能否给我们一个参考(可能来自手册),在 PHP 中,子类中的方法(包括私有方法)被复制;原有功能的范围是否保持不变?我在任何地方都找不到它,我认为这将帮助我理解 LSB 和 static:: 在非静态上下文中的用法;php.net/manual/en/......
0赞 2/3/2023
嗨 @Artefacto+ 1 - 当使用 static:: 和继承时,php 将首先查看初始调用类,如果没有找到,它会去寻找父类?提前致谢