注意:以下是使用 PHP 5.3.8 测试的。其他版本可能会表现出不同的行为。
由于没有正式的 PHP 规范,因此无法从 应该 发生什么的角度来回答这个问题。我们能得到的最接近的是 PHP 手册中关于 protected 的声明:
声明为 protected 的成员只能在类本身内以及被继承的类和父类访问。
尽管该成员可能在ChildClass 中被覆盖(保留“受保护”说明符),但它最初是在BaseClass 中声明的,因此它在BaseClass 的后代中仍然可见。
直接反对这种解释,比较受保护财产的行为:
<?php
abstract class BaseClass {
protected $_foo = 'foo';
abstract protected function __construct();
}
class MommasBoy extends BaseClass {
protected $_foo = 'foobar';
protected function __construct(){
echo __METHOD__, "\n";
}
}
class LatchkeyKid extends BaseClass {
public function __construct() {
echo 'In ', __CLASS__, ":\n";
$kid = new MommasBoy();
echo $kid->_foo, "\n";
}
}
$obj = new LatchkeyKid();
输出:
在 LatchkeyKid 中:
MommasBoy::__构造
致命错误:无法在第 18 行访问受保护的属性 MommasBoy::$_foo
将抽象 __construct 更改为具有空实现的具体函数会产生所需的行为。
abstract class BaseClass {
protected function __construct() {}
}
但是,非魔法方法在亲属中是可见的,无论它们是否是抽象的(大多数魔法方法必须是公共的)。
<?php
abstract class BaseClass {
abstract protected function abstract_protected();
protected function concrete() {}
}
class MommasBoy extends BaseClass {
/* accessible in relatives */
protected function abstract_protected() {
return __METHOD__;
}
protected function concrete() {
return __METHOD__;
}
}
class LatchkeyKid extends BaseClass {
function abstract_protected() {}
public function __construct() {
echo 'In ', __CLASS__, ":\n";
$kid = new MommasBoy();
echo $kid->abstract_protected(), "\n", $kid->concrete(), "\n";
}
}
$obj = new LatchkeyKid();
输出:
在 LatchkeyKid 中:
MommasBoy::abstract_protected
MommasBoy::混凝土
如果您忽略警告并将魔术方法(__construct、__destruct 和 __clone 除外)声明为 protected,则它们似乎可以在亲属中访问,与非魔术方法一样。
受保护的__clone 和__destruct 在亲属中是不可访问的,无论它们是否抽象。这让我相信 abstract __construct 的行为是一个错误。
<?php
abstract class BaseClass {
abstract protected function __clone();
}
class MommasBoy extends BaseClass {
protected function __clone() {
echo __METHOD__, "\n";
}
}
class LatchkeyKid extends BaseClass {
public function __construct() {
echo 'In ', __CLASS__, ": \n";
$kid = new MommasBoy();
$kid = clone $kid;
}
public function __clone() {}
}
$obj = new LatchkeyKid();
输出:
在 LatchkeyKid 中:
致命错误:从上下文 'LatchkeyKid' 调用受保护的 MommasBoy::__clone() - 第 16 行
对__clone 的访问在zend_vm_def.h(特别是ZEND_CLONE 操作码处理程序)中强制执行。这是对方法的访问检查的补充,这可能是它具有不同行为的原因。但是,我没有看到访问__destruct 的特殊处理,所以显然还有更多。
Stas Malyshev(嗨,Stas!),其中一位 PHP 开发人员,查看了 __construct、__clone 和 __destruct 并说:
一般来说,在基类中定义的函数应该可以被所有人访问
那个阶级的[后代]。它背后的基本原理是,如果你定义
你的基类中的函数(甚至是抽象的),你说它会是
可用于此类的任何实例(包括扩展实例)。所以
这个类的任何后代都可以使用它。
[...] 我检查了为什么 ctor 行为不同,这是因为父 ctor
被认为是子ctor的原型(带有签名
强制执行等)仅当它被声明为抽象的或从
界面。因此,通过将 ctor 声明为抽象或使其成为
接口,你使它成为合同的一部分,因此所有人都可以访问
等级制度。如果您不这样做,则 ctor 与每个都完全无关
其他(这对于所有其他非静态方法都不同),因此
拥有父母 ctor 并没有说明孩子 ctor,所以父母
ctor 的可见性不会延续。所以对于ctor来说不是一个错误。 [注意:这类似于 J. Bruni 的回答。]
我仍然认为这很可能是 __clone 和 __destruct 的错误。
[...]
我已提交 bug #61782 以跟踪 __clone 和 __destruct 的问题。