考虑下面的代码:
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()
and bar->callbar()
本质上是同一件事,它们会根据可见性产生不同的结果m()
in foo
and bar
。为什么会出现这种情况?
继承/重写私有方法
在 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'
调用方法
这里的规则如下:
- Look in the method table of the actual class of the object (in your case,
bar
).
- If this yields a private method:
- 如果定义方法的作用域与调用函数的作用域相同并且与对象的类相同,则使用它。
- 否则,请在父类中查找与调用函数具有相同作用域且具有相同名称的私有方法。
- 如果没有找到满足上述要求之一的方法,则失败。
- If this yields a public/protected method:
- 如果方法的范围被标记为已更改,则我们可能已使用公共/受保护方法覆盖了私有方法。因此,在这种情况下,如果另外存在一个与调用函数范围定义的私有方法同名的方法,请改用该方法。
- 否则,使用找到的方法。
结论
- (均为私人)对于
bar->call()
,范围call
is foo
。呼唤$this->m()
引发在方法表中的查找bar
for m
,产生私有的bar::m()
。然而,范围bar::m()
与调用范围不同,foo
。方法foo:m()
在向上遍历层次结构时找到并使用它。
- (私人在
foo
,公开于bar
) 范围call
还是foo
。查找产生一个公共bar::m()
。然而,它的作用域被标记为已更改,因此在调用作用域的函数表中进行查找foo
对于方法m()
。这会产生一个私有方法foo:m()
与调用范围具有相同的范围,因此使用它来代替。
- 这里没什么可看的,因为能见度降低而出错。
- (均为公开)范围
call
还是foo
。查找产生一个公共bar::m()
。它的范围没有被标记为已更改(它们都是公开的),所以bar::m()
用来。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)