构造函数可以访问PHP中的父私有属性


Constructor can access parent private properties in PHP?

在PHP中处理继承时,我发现一些知识缺乏,主要是关于构造函数私有属性。

让我们以这个代码为例:

<?php
class Module
{
    public $type;
    public function __construct($type)
    {
        $this->type = $type;
    }
}
class BModule extends Module
{
}
class CModule extends BModule
{
}
class A
{
    private $module;
    public function __construct(Module $module)
    {
        echo 'Set module for '.__CLASS__.' to '.$module->type . PHP_EOL;
        echo "<br>";
        $this->module = $module;
    }
    public function getModule()
    {
        echo "I (as " . __CLASS__ . ") have a module of type " . $this->module->type;
        return $this->module->type;
    }
}
class B extends A
{
}

$m = new Module('base-module');
$bm = new BModule('bi-module');
echo "<br>--------A---------<br>";
$a = new A($m);
echo "<br>A is of type " . $a->getModule();
echo "<br>--------B---------<br>";
$b = new B($bm);
echo "<br>B is of type " . $b->getModule();

一些问题:

  1. B构造不应该在B的上下文中调用构造函数吗?(所以我预计它会失败,因为它没有继承私有属性$module
    • 或者PHP会简单地调用A构造函数,使用/引用A中的方法和属性?(包括私有
  2. 我可以将ModuleBModule对象传递给$b;这是因为BModuleModule的子级。在验证类型提示时,PHP是否正在检查传递对象的继承链(检查父对象)?
    • 因此,我可以向构造函数传递ModuleBModule类型的对象吗

这是另一个例子:

<?php

class a
{
    private $a;
    protected $a_copy;
    public function __construct($a_value)
    {
        $this->a = $a_value;
        $this->a_copy = $this->a;
    }
    public function getA()
    {
        return $this->a;
    }
    public function getCopyA()
    {
        return $this->a;
    }
}
class b extends a
{
}

$a = new a('value for a');
$b = new b('value for b');
echo "<br>-----A-----<br>";
echo $a->getA()."<br>";
echo $a->getCopyA()."<br>";
echo "<br>-----B-----<br>";
echo $b->getA()." (I would expect to have no access to '$a)<br>";
echo $b->getCopyA()."<br>";

作为属性$a私有,我将无法访问它或从类b中对它做任何事情。对我的实际理解来说,这有点不合理。

这是预期的功能,尽管B继承了A的所有方法,但它们不是在B的上下文中调用的,而是在A的上下文中进行调用的。因此调用了A构造函数。这意味着,即使对象被扩展,在A中定义的函数也可以访问A的属性。但是,在A中定义的方法不能访问B的属性,这似乎是您的理解。

因此,很快回答您的问题:

  1. 不,函数总是在定义的上下文中调用,而不是从调用它们的位置调用
  2. PHP将检查继承链的所有路径,看看它是否正确。类的任何子级都可以被假定具有相同的函数。因此,如果B扩展了A,当它的类型被提示为A时,你可以使用B或A作为参数,但只在它的类型提示为B时使用B

Ad 1:否,被调用方法的上下文是声明方法(在本例中为构造函数)的地方。如果上下文是类B,那么任何人都可以通过扩展它来破坏你的类

看看这个例子:

class A
{
    private $module;
    public function __construct(Module $module)
    {
        echo 'Set module for '.__CLASS__.' to '.$module->type . PHP_EOL;
        echo "<br>";
        $this->module = $module;
    }
}
class B extends A
{
    public function __construct()
    {
        parent::__construct(new Module()); // call the parent (which is A)
    }
}

这说明了A::__construct()的作用域实际上是A类。

广告2:是的,每个作为子类实例的对象都可以用来代替超级类。这就是为什么您应该编写类,以便在静态类型需要一个超级类时可以替换它们。有关此主题的更多信息,请参阅利斯科夫替代原理。

至于最后一个例子:同样,子类中没有任何代码可以对超级类的私有成员进行操作。所有代码都在超类上下文中操作。所以这里没有问题。

如果你试图重载超类的方法并像这样使用它的私有成员,就会出现问题:

class b extends a
{
    public function getA()
    {
        return $this->a . "_suffix"; // error
    }
}

在这种情况下,您必须依赖于超类中getA()方法的实现:

class b extends a
{
    public function getA()
    {
        return parent::getA() . "_suffix"; // ok, we are depending on the super class implementation
    }
}