Question

I have a following class hierarchy, which shown in a reproduction script below:

<?php
header('Content-Type: text/plain');

class A
    {
        public $config = array(
            'param1' => 1,
            'param2' => 2
        );

        public function __construct(array $config = null){
            $this->config = (object)(empty($config) ? $this->config : array_merge($this->config, $config));
        }
    }

class B extends A
    {
        public $config = array(
            'param3' => 1
        );

        public function __construct(array $config = null){
            parent::__construct($config);

            // other actions
        }
    }

$test = new B();

var_dump($test);
?>

Output:

object(B)#1 (1) {
  ["config"]=>
  object(stdClass)#2 (1) {
    ["param3"]=>
    int(1)
  }
}

What I wanted, is that A::$config not be overriden by B::$config. There might be a lot of descendant classes from B, where I would like to change $config, but I need that those $config values to merge / overwrite if match $config values of all it's parents.

Q: How can I do that ?

I've tried to use array_merge() but in non-static mode those variables just override themselves. Is there a way to achieve merge effect for class tree without static (late static binding) ?

Was it helpful?

Solution 2

You can restructure how your extended class is instantiated

class B extends A
{
    private $defaults = array('param3' => 1);
    public function __construct(array $config = null){
        parent::__construct($config?array_merge($this->defaults, $config):$this->defaults);
    }
}

OTHER TIPS

Instead of declaring a $config property with values that you're going to change in the constructor, it's better to declare those values as default values. This is also described in Orangepill's answer:

class A
{
    public $config;

    private $defaults = array(
        'param1' => 1,
        'param2' => 2,
    );

    public function __construct(array $config = array())
    {
        $this->config = (object)($config + $this->defaults);
    }
}

A few twists there; by declaring the default value of the $config constructor argument as an empty array, you can simplify your code by using array operators like I did above. Undefined keys in $config are filled in by $this->defaults.

The extended class will look very similar:

class B extends A
{
    private $defaults = array(
        'param3' => 1
    );

    public function __construct(array $config = array())
    {
        parent::__construct($config + $this->defaults);
    }
}

You can do that using ReflectionClass. Start by introspecting $this, use getProperty(), then use getParentClass() to do the same on the parent class and its parent etc. and merge the resulting arrays together.

This is probably not the best solution for the problem you're facing though.

I believe the following is what you are looking for. Adapted from Inherit static properties in subclass without redeclaration?

<?php
class MyParent {
    public static $config = array('a' => 1, 'b' => 2);

    public static function getConfig() {
        $ret = array();
        $c = get_called_class();
        do {
            $ret = array_merge($c::$config, $ret);
        } while(($c = get_parent_class($c)) !== false);
        return $ret;
    }
}

class MyChild extends MyParent {
    public static $config = array('a' => 5, 'c' => 3, 'd' => 4);
    public function myMethod($config) {
        $config = array_merge(self::getConfig(), $config);
    }
}

class SubChild extends MyChild {
    public static $config = array('e' => 7);
}

var_export(MyChild::getConfig());
// result: array ( 'a' => 5, 'b' => 2, 'c' => 3, 'd' => 4, )

$mc = new MyChild();
var_export($mc->myMethod(array('b' => 6)));
// result: array ( 'a' => 5, 'b' => 6, 'c' => 3, 'd' => 4, )

var_export(SubChild::getConfig());
// result: array ( 'a' => 5, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 7, ) 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top