Nick
Nick

Reputation: 147146

Access parent and child variables in method defined in parent - PHP

I have a child object that inherits from a parent. Both have a static variable which has a different value in each object; I want to add that variable from both parent and child to an array when I instantiate the child. To save duplicating code, I've written a method (addFoo) in the parent which is called from both the parent and the child constructors. However, I can't seem to find a way to distinguish between the calls when the parent constructor is called from the child constructor (as you can see below, the output from the method is the same in both cases whether using $this, self or static).

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);

Output:

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

I can workaround this by passing the calling class through to the addFoo function (see below), but is there a better (correct?) way?

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);

Output:

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

Demo on 3v4l.org

Upvotes: 1

Views: 1304

Answers (2)

Jeto
Jeto

Reputation: 14927

I would alternatively consider another, more straightforward approach:

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']

You do have to redefine getVars on each subclass in this case, but after all, it makes sense for each class to decide which variables should be exposed. Your code becomes a bit less obscure / easier to maintain in the process.

And if a class doesn't need/want to "contribute", you can simply omit both the static property and the getVars extension for that class.

Demo

Notes:

  • you can easily cache the variables into a $vars property inside A if needed,
  • order is swapped from the other answer but you can obviously swap the array_merge if needed,
  • in both this sample and the one from the other answer, I'm not sure if the $foo static properties need to be public, but I've left them like they were in your question.

Upvotes: 1

Jeto
Jeto

Reputation: 14927

Instead of having addFoo be called by every sub-constructor, one way would be to have a single addFoos method in the base class that is called by the base constructor, that would append all the $foo values starting from the late static binding class:

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']

That method is marked private as it's not supposed to be extended in this scenario (nor called from the outside).

Demo

Upvotes: 3

Related Questions