构造函数可以在PHP中访问父私有属性吗?

时间:2014-10-15 17:00:27

标签: php oop inheritance type-hinting

在处理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. 我可以将模块 BModule 对象传递给 $ b ;这是因为 BModule Module 的孩子。在验证类型提示时,PHP是否检查传递对象的某些继承链(检查父项)?
    • 我可以将 Module BModule CModule 类型的对象传递给构造函数吗?
  3. 这是另一个例子:

    <?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 类中执行任何操作。 对于我的实际理解,这有点无意义。

2 个答案:

答案 0 :(得分:1)

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

所以很快回答你的问题:

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

答案 1 :(得分:1)

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类。

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