문제

I often use properties in my classes that store an array of options. I'd like to be able to somehow merge those options from defaults declared in a parent class.

I demonstrated with some code.

class A
{
    public $options = array('display'=>false,'name'=>'John');
}

class B extends A
{
    public $options = array('name'=>'Mathew');
}

Now when I create B, then I'd like $options to contain a merged array from A::options

What happens now is this.

$b = new B();
print_r($b);
array('name'=>'Mathew');

I would like something like this using array_merge_recursive().

array('display'=>false,'name'=>'Mathew');
  • Maybe it's something I could do in the constructor?
  • Is it possible to make this a behavior of class A? So that I don't always have to implement the same code in all subclasses.
  • Could I use reflection to auto find array properties in both classes and merge them?
도움이 되었습니까?

해결책 2

I realize I changed your interface from a public variable to a method, but maybe it works for you. Beware, adding a naive setOps($ops) method may work unexpected if you allow the parent ops to continue to be merged in.

class A
{
    private $ops = array('display'=>false, 'name'=>'John');
    public function getops() { return $this->ops; }
}
class B extends A
{
    private $ops = array('name'=>'Mathew');
    public function getops() { return array_merge(parent::getOps(), $this->ops); }
}
class c extends B
{
    private $ops = array('c'=>'c');
    public function getops() { return array_merge(parent::getOps(), $this->ops); }
}

$c = new C();
print_r($c->getops());

out:

Array
(
    [display] => 
    [name] => Mathew
    [c] => c
)

다른 팁

In addition to the previous answers, another approach that may be suited for certain cases would be to use PHP Reflection or built-in class functions. Here is a basic example using the latter:

class Organism
{
    public $settings;
    public $defaults = [
        'living' => true,
        'neocortex' => false,
    ];
    public function __construct($options = [])
    {
        $class = get_called_class();
        while ($class = get_parent_class($class)) {
            $this->defaults += get_class_vars($class)['defaults'];
        }
        $this->settings = $options + $this->defaults;
    }
}
class Animal extends Organism
{
    public $defaults = [
        'motile' => true,
    ];
}
class Mammal extends Animal
{
    public $defaults = [
        'neocortex' => true,
    ];
}

$fish = new Animal();
print_r($fish->settings); // motile: true, living: true, neocortex: false
$human = new Mammal(['speech' => true]);
print_r($human->settings); // motile: true, living: true, neocortex: true, speech: true

You can use a simple pattern like so:

abstract class Parent {

    protected $_settings = array();

    protected $_defaultSettings = array(
        'foo' => 'bar'
    );

    public __construct($settings = array()) {
        $this->_settings = $settings + $this->_defaultSettings;
    }

}

In this way it's easily possible to modify the defaults applied in child classes:

class Child extends Parent {

    protected $_defaultSettings = array(
        'something' => 'different';
    );

}

Or to apply something more complex:

class OtherChild extends Parent {

    function __construct($settings = array()) {
        $this->_defaultSettings = Configure::read('OtherChild');
        return parent::__construct($settings);
    }

}

Merging variables

Cake does come with a function for merging variables. It's used for controller properties such as components, helpers etc. But be careful applying this function to none trivial arrays - you may find that it doesn't quite do what you want/expect.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top