PHP继承和受保护成员可见性

14

我在PHP中遇到了一个看起来似乎是奇怪的继承问题。

来自PHP手册

声明为protected的成员只能被类本身、继承的子类和父类访问。

对我而言,这意味着: 如果 A instanceof B 或者 B instanceof A,则 A 可以访问 B 的受保护成员。

然而,如果A和B都扩展自Foo,并且Foo有一个未被B覆盖的受保护构造函数,则我可以在A内部创建B的实例。这对我来说没有意义,因为A不是B的实例,B也不是A的实例。我还可以调用 $b->test() 这个受保护方法,在A中执行B中的方法实现。(如果B没有重新声明 test(),那么会执行Foo中的实现。)对我来说,更奇怪的是,如果B直接实现了一个受保护的构造函数,则我无法从A中创建B的实例。这似乎很奇怪,因为我无法访问受保护的构造函数(也在父类中声明),但是访问受保护的方法(也在父类中声明)却没有问题。

请注意,当我使用一个不扩展Foo的类C时,我会得到预期的行为。如果我尝试从C中实例化B,则会出现致命错误,因为我正在尝试访问受保护的构造函数。如果我向B添加一个公共构造函数,则可以实例化它(这是预期的),但我仍然无法访问受保护的方法 test()(这也是预期行为)。我期望使用A时会得到相同的行为。

以下是解释样例的代码:

class Foo {
    protected function __construct() {
        echo('Constructing ' . get_called_class());
    }

    protected function test() {
        echo('Hello world ' . __METHOD__);
    }
}

class A extends Foo {
    public function __construct() {
        parent::__construct();
    }

    public function testB() {
        // Both of these lines work
        $b = new B();
        $b->test();
    }
}

class B extends Foo {
    protected function test() {
        echo('Hello world Again ' . __METHOD__);
    }
}

class C {
    public function __construct() {
    }

    public function testB() {
        // Both of these lines cause fatal errors
        $b = new B();
        $b->test();
    }
}

$a = new A();
$a->testB();

$c = new C();
$c->testB();

我可能看漏了什么,但是我找不到问题所在。有人能够解释一下这种行为吗?


确实非常奇怪的行为。 - Sherlock
您想要关于此行为背后原理或实现的解释吗?由于这是PHP,很可能根本没有任何原理。 - Jon
如果有理由的话,我想知道是什么。 - Arjan
如果在B中添加了一个受保护的构造函数,则A::testB将无法工作。因此,我认为当A::testB尝试调用B构造函数时,它会直接调用Foo构造函数(因为它在B中缺失)。Foo::_constructor可以从A访问,因此它可以工作(也许它没有子类的记忆)。我不在这里说明它是对还是错...但我认为会发生这种情况。 - Luca Rainone
2个回答

6
由于在您的父类Foo中声明了这些方法为受保护的,所以您可以访问这些方法。如果您从父类中删除该声明,并在B中声明受保护的方法,则会出现致命错误。
这在PHP中被报告为一个错误:https://bugs.php.net/bug.php?id=50892

即使 Foo 声明了,如果 B 重写了 __construct,你也不能从 A 中调用 new B() - Newbo.O
2
谢谢你指引我去 bugs.php.net 的链接。我在那里搜索过,但是找不到这个bug报告。 - Arjan

1

谢谢提供链接。我认为调用继承的受保护方法比无法调用继承的构造函数更奇怪(在我看来这是预期行为)。 - Arjan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接