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

发布于 2024-09-24 05:45:23 字数 1141 浏览 3 评论 0原文

考虑以下代码:

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时一样。但是,尽管 foo->call()bar->callbar() 本质上是同一件事,但它们会根据 m 的可见性产生不同的结果()foobar 中。为什么会出现这种情况?

Consider the following piece of code:

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

Now, changing the visibility of the m() method, I get:
(+ 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 seems to behave like public).

I was expecting everything to behave like it does when both are declared public. But although foo->call() and bar->callbar() are essentially the same thing, they yield different results depending on the visibility of m() in foo and bar. Why does this happen?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

妄想挽回 2024-10-01 05:45:23

继承/重写私有方法

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

结论

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

Inheriting/overriding private methods

In PHP, methods (including private ones) in the subclasses are either:

  • Copied; the scope of the original function is maintained.
  • Replaced ("overridden", if you want).

You can see this with this code:

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

Now if you override the private method, its new scope will not be A, it will be B, and the call will fail because A::callH() runs in 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'

Calling methods

Here the rules are as follows:

  • Look in the method table of the actual class of the object (in your case, bar).
    • If this yields a private method:
      • If the scope where the method was defined is the same as the scope of the calling function and is the same as the class of the object, use it.
      • Otherwise, look in the parent classes for a private method with the same scope as the one of the calling function and with the same name.
      • If no method is found that satisfies one of the above requirements, fail.
    • If this yields a public/protected method:
      • If the scope of the method is marked as having changed, we may have overridden a private method with a public/protected method. So in that case, and if, additionally, there's a method with the same name that is private as is defined for the scope of the calling function, use that instead.
      • Otherwise, use the found method.

Conclusion

  1. (Both private) For bar->call(), the scope of call is foo. Calling $this->m() elicits a lookup in the method table of bar for m, yielding a private bar::m(). However, the scope of bar::m() is different from the calling scope, which foo. The method foo:m() is found when traversing up the hierarchy and is used instead.
  2. (Private in foo, public in bar) The scope of call is still foo. The lookup yields a public bar::m(). However, its scope is marked as having changed, so a lookup is made in the function table of the calling scope foo for method m(). This yields a private method foo:m() with the same scope as the calling scope, so it's used instead.
  3. Nothing to see here, error because visibility was lowered.
  4. (Both public) The scope of call is still foo. The lookup yields a public bar::m(). Its scope isn't marked as having changed (they're both public), so bar::m() is used.
请止步禁区 2024-10-01 05:45:23

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

如果您想在父类中使用某个方法,但希望子类能够修改其行为,并且不希望该方法在外部可用,请使用 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();将产生错误,因为 z 根本不存在于子类中,即使作为继承方法也不存在。

A private method is not overridable, as a private method is not visible even to its subclasses. Defining a method as protected means it is not visible outside of the class itself or its subclasses.

If you have a method that you want to use from your parent class but want children to able to modify its behaviour, and don't want this method available externally, use protected. If you want functionality in your parent class that cannot be modified in any way by subclasses, define the method as private.

EDIT: to clarify further, if you have two methods with the same name in a parent and subclass, and these methods are defined as private, essentially the subclass method has absolutely no relation to the parent method. As stated, a private method is COMPLETELY INVISIBLE to the subclass.

Consider this:

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

Calling $bar->callz(); is going to produce an ERROR, because z does not exist in the subclass at all, not even as an inherited method.

南街九尾狐 2024-10-01 05:45:23

根据 PHP 手册:

声明为私人的成员只能
由定义的类访问
会员。

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

编辑

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

如果 foo 中的 m() 是公共的,则它是可重写的。在这种情况下,bar 中的 m() 会覆盖 foo 中的 m()

According to the PHP manual:

Members declared as private may only
be accessed by the class that defines
the member.

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

EDIT

they yield different results depending
on the visibility of m() in foo and
bar. Why does this happen?

If m() in foo is public, it is overridable. When this is the case m() from bar overrides m() in foo.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文