【问题标题】:Constructor can access parent private properties in PHP?构造函数可以访问 PHP 中的父私有属性吗?
【发布时间】:2014-12-10 20:29:16
【问题描述】:

在处理 PHP 中的继承时,我发现缺乏一些知识,主要是关于 constructorsprivate 属性。

我们以这段代码为例:

<?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 是否检查传递对象的某些继承链(检查父级)?
    • 那么我可以将 ModuleBModuleCModule 类型的对象传递给构造函数吗?

这是另一个例子:

<?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 对其进行任何操作. 对我的实际理解来说有点无厘头。

【问题讨论】:

    标签: php oop inheritance type-hinting


    【解决方案1】:

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

    所以简短地回答您的问题:

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

    【讨论】:

    • 我有点困惑。所以如果我有像class a { getClass() {return get_class($this);}}class b extends a {} 这样简单的东西,那么在b 的实例上调用getClass() 不会返回a? (因为我们说过该方法是在父 a 的上下文中继承和执行的)。很抱歉代码格式错误,也很抱歉最终出现愚蠢的问题。
    • 没错,它会返回 B 而不是 A。这是因为 function 是在 A 的上下文中调用的,但实际的 object $this 所指的仍然是 B 类型
    【解决方案2】:

    广告 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:是的,每个作为子类实例的对象都可以用来代替超类。这就是为什么您应该编写类以便在静态类型需要超类时可以替换它们的原因。有关此主题的更多信息,请参阅Liskov substitution principle

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

    如果您尝试重载超类的方法并像这样使用其私有成员,则会出现问题:

    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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-07-06
      • 2012-12-25
      • 1970-01-01
      • 2016-07-26
      • 2019-05-21
      • 2021-04-17
      • 1970-01-01
      • 2011-12-08
      • 2011-08-16
      相关资源
      最近更新 更多