php - 覆盖私有(private)方法时的奇怪行为

标签 php overriding 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 中。为什么会这样?

最佳答案

继承/覆盖私有(private)方法

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

  • 已复制;保持原有功能的范围。
  • 已替换(“覆盖”,如果需要)。

你可以用这段代码看到这一点:

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

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

结论

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

关于php - 覆盖私有(private)方法时的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3756609/

相关文章:

php - SHOW TABLES 未返回数组中的正确项目

php - mysql 中的嵌套选择

overriding - css:如何用更大(限制更少)的值覆盖最大宽度值

c# - C# 如何访问另一个类中一个类的私有(private)函数?

java - 如何避免忘记在类里面将属性设为私有(private)

php - 使用内置的 Laravel 5.2 身份验证并加载 SPA,然后为所有其他路由加载 Dingo API

php - 如何从另一个 .php 文件中输入的 .php 文件访问变量?

java - 将方法从库覆盖到不同的项目

c# - 当基类中有更通用的方法时,如何重写基类中的方法?

msmq - 私有(private)或公共(public) MSMQ