【问题标题】:Access parent and child variables in method defined in parent - PHP在父项中定义的方法中访问父项和子项变量 - PHP
【发布时间】:2021-01-25 15:49:22
【问题描述】:

我有一个继承自父对象的子对象。两者都有一个静态变量,每个对象都有不同的值;当我实例化孩子时,我想将该变量从父母和孩子添加到数组中。为了节省重复代码,我在父构造函数和子构造函数都调用的父构造函数中编写了一个方法 (addFoo)。但是,当从子构造函数调用父构造函数时,我似乎找不到区分调用的方法(如下所示,无论使用$this,两种情况下方法的输出都是相同的, selfstatic)。

class A {
    public static $foo = 'foo';
    
    public $thisvars = array();
    public $selfvars = array();
    public $staticvars = array();
    
    public function __construct() {
        $this->addFoo();
    }
    
    public function addFoo() {
        $this->selfvars[] = self::$foo;
        $this->staticvars[] = static::$foo;
        $this->thisvars[] = $this::$foo;
    }
    
}

class B extends A {
    public static $foo = 'bar';
    
    public function __construct() {
        parent::__construct();
        $this->addFoo();
    }
}

$b = new B;
print_r($b->selfvars);
print_r($b->staticvars);
print_r($b->thisvars);

输出:

Array
(
    [0] => foo
    [1] => foo
)
Array
(
    [0] => bar
    [1] => bar
)
Array
(
    [0] => bar
    [1] => bar
)

我可以通过将调用类传递给addFoo 函数来解决这个问题(见下文),但有更好的(正确的?)方法吗?

class C {
    public static $foo = 'foo';
    
    public $vars = array();
    
    public function __construct() {
        $this->addFoo(__CLASS__);
    }
    
    public function addFoo($class) {
        $this->vars[] = $class::$foo;
    }
    
}

class D extends C {
    public static $foo = 'bar';
    
    public function __construct() {
        parent::__construct();
        $this->addFoo(__CLASS__);
    }
}

$d = new D;
print_r($d->vars);

输出:

Array
(
    [0] => foo
    [1] => bar
)

Demo on 3v4l.org

【问题讨论】:

  • say 的角度来看,当前对象是B 类的一个实例(因为这就是你要实例化的对象),所以从哪里调用它并不重要:它总是会给出相对于B 的结果(__CLASS__self 除外,因为它们本身指的是当前类,但这无济于事)。恐怕其他任何试图弄清楚的事情都必须是一个肮脏的技巧,例如解析堆栈跟踪。知道您尝试使用该代码解决哪些问题会很有趣,因为可能还有其他方法可以解决这些问题。
  • @Jeto 谢谢;这就是我的理解。我要解决的问题是我有一个父对象,该对象具有一组需要传递给构造函数的必需属性;这些需要存在于父级及其子级的所有实例中。子可能有额外的必需属性,我想使用相同的函数来处理父(来自父构造函数)和子(来自子构造函数)的必需属性。
  • @Jeto 我已经重写了这个问题,使其更接近我的实际情况,并希望更加不言自明。
  • 如果希望特定的父函数可以被子函数调用,可以设置protected,例如protected function addFoo()
  • @DonnyAkhmadSeptaUtama 这些函数是公开的,因此孩子已经可以调用它们。

标签: php oop methods


【解决方案1】:

我会考虑另一种更直接的方法:

class A
{
    public static $foo = 'foo';

    public function getVars()
    {
        return [self::$foo];
    }
}

class B extends A
{
    public static $foo = 'bar';

    public function getVars()
    {
        return array_merge(parent::getVars(), [self::$foo]);
    }
}

class C extends B
{
    public static $foo = 'baz';

    public function getVars()
    {
        return array_merge(parent::getVars(), [self::$foo]);
    }
}

$a = new A;
print_r($a->getVars());  // ['foo']

$b = new B;
print_r($b->getVars());  // ['foo', 'bar']

$c = new C;
print_r($c->getVars());  // ['foo', 'bar', 'baz']

在这种情况下,您确实必须在每个子类上重新定义 getVars,但毕竟,每个类决定应该公开哪些变量是有意义的。您的代码在此过程中变得不那么晦涩/更易于维护。

如果一个类不需要/不想“贡献”,您可以简单地省略该类的静态属性和 getVars 扩展。

Demo

注意事项:

  • 如果需要,您可以轻松地将变量缓存到 A 内的 $vars 属性中,
  • 订单已从其他答案交换,但您显然可以根据需要交换 array_merge
  • 在此示例和来自其他答案的示例中,我不确定 $foo 静态属性是否需要为 public,但我已将它们留在您的问题中。

【讨论】:

  • 感谢您的帮助 - 非常感谢您在这个问题上花费的时间。
  • 这也是一个很好的解决方案,尽管它确实需要在每个继承级别重新声明getVars。我喜欢你可以在A 的构造函数中使用$this->vars = $this->getVars(); 来缓存所有结果。
【解决方案2】:

不是让每个子构造函数调用addFoo,一种方法是在基构造函数调用的基类中有一个addFoos 方法,它会附加所有$foo 值从后期静态绑定类开始:

class A
{
    public static $foo = 'foo';

    public $vars = [];

    public function __construct()
    {
        $this->addFoos();
    }

    private function addFoos()
    {
        $class = static::class;
        do {
            $this->vars[] = $class::$foo;
        } while ($class = get_parent_class($class));
    }

}

class B extends A
{
    public static $foo = 'bar';
}

class C extends B
{
    public static $foo = 'baz';
}

$a = new A;
print_r($a->vars);  // ['foo']

$b = new B;
print_r($b->vars);  // ['bar', 'foo']

$c = new C;
print_r($c->vars);  // ['baz', 'bar', 'foo']

该方法被标记为private,因为它不应该在这种情况下扩展(也不应该从外部调用)。

Demo

【讨论】:

  • 这看起来不错。我特别喜欢addFoos 可以设为私有的事实。我不确定在我的代码中执行 child->parent 与 parent->child 的顺序是否会对我产生影响,我需要稍微尝试一下。
  • 这样做的一个问题是,如果在 B 类中没有 $foo 的值,它将再次添加从 A 类继承的值。
  • 好吧,从技术上讲,如果 B::$foo 没有定义,那么它与使用与 A::$foo 相同的值定义时完全相同。所以这有点道理。
  • 不确定print_r($c->vars); // ['baz', 'foo', 'foo'] 是否是用户想要的。没有定义 B 中的值,我希望不会添加任何内容。
  • 已经测试了两个答案,这个对我的特定问题更有效。再次感谢。
猜你喜欢
  • 2020-02-13
  • 1970-01-01
  • 1970-01-01
  • 2012-06-06
  • 2011-05-30
  • 1970-01-01
  • 1970-01-01
  • 2022-01-24
  • 2011-06-23
相关资源
最近更新 更多