Omid
Omid

Reputation: 4715

How to force parent class to keep value of its protected property?

I have two classes Foo and Bar that Bar extends Foo as below:

class Foo {
    protected 
        $options = array(), 
        $defaults = array(1, 2);

    public function __construct($options){
        array_push($this->options, $this->defaults);
    }
}

class Bar extends Foo {
    protected $defaults = array(3, 4);

    public function print(){
        print_r($this->$options);
    }
}

$bar = new Bar();
$bar->print();

i thought that result should be array(1,2,3,4) but is array(3,4).
how to solve that ?

edit
i don't want Bar class to have constructor because i'm just implementer of superclass and don't know what really will happen in child class.

Upvotes: 0

Views: 207

Answers (5)

umlcat
umlcat

Reputation: 4143

"I just want each child class could have it's default values".

You want to have specific data for each class.

You can use "static fields", make class act as a variable, by itself. Im not very fan of "static members", but I think, it applies to your "use case".

class Foo {
    private 
      // (1) "private" only accessed by the class itself,
      // neither external code, or subclasses,
      // (2) "static", specific to the class,
      static $defaults = array(1, 2);

    protected 
      // want to be accessed only by class & subclasses
      $options = array();

    // when using "static fields" in constructor,
    // you need to override constructor
    public function __construct($options){
        array_push($this->options, Foo::$defaults);
    }

    // ops, "print" is reserved identifier
    // public function print(){

    public function display_options() {
        print_r($this->$options);
    }

    public function display_defaultoptions() {
      // in order to acccess "static fields",
      // you use the class id, followed by double colon,
      // not "$this->*"
      print_r(Foo::$defaults);
    }
} // class Foo

class Bar extends Foo {
private 
    // (1) "private" only accessed by the class itself,
    // neither external code, or subclasses,
    // (2) "static", specific to the class,
    static $defaults = array(1, 2);

    // when using "static fields" in constructor,
    // you need to override constructor
    public function __construct($options){
        array_push($this->options, Bar::$defaults);
    }

    public function display_defaultoptions() {
      // in order to acccess "static fields",
      // you use the class id, followed by double colon
      // not "$this->*"
      print_r(Bar::$defaults);
    }
} // class Bar

$bar = new Bar();
$bar->print();

Cheers

Upvotes: 0

Jeff Lambert
Jeff Lambert

Reputation: 24671

First- You cannot have a method named print. print is a language construct and cannot be overridden.

Second- you should make the default values for a class private, and override them in child classes. Then you can combine them in the parent when you call the constructor. It's not 100% clear what you're trying to accomplish, but the following will merge the sub-class' default options with the superclass':

Updated to remove constructor

abstract class Foo {
    protected $options = array();

    private $defaults = array(1, 2);

   // Implementations of this class MUST define this method
   abstract function overrideDefaults(); 

    public function __construct($options = array()){
        // Merge any incoming options with the default options
        $this->options = array_merge($this->defaults, $options);

    }

    // Concrete children can use this method to modify the current options by 
    // passing in their own defaults.
    protected function modifyDefaults( $defaults ) {
        $this->options= array_merge( $this->defaults, $defaults );
    }

    public function printOps(){
        print_r($this->options);
    }
}

class Bar extends Foo {
    private $defaults = array(3, 4);

    public function overrideDefaults() {
        parent::modifyDefaults( $this->defaults );
    }
}

$bar = new Bar();
$bar->overrideDefaults();
$bar->printOps();

Notice I also moved the now printOps method to the superclass. Output:

Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 ) 

Upvotes: 1

Madara's Ghost
Madara's Ghost

Reputation: 175098

There are a couple of solutions, the simplest would be a second variable to be used as an extended defaults, then merge the arrays.

class Foo {
    protected
        $options = array(),
        $original_defaults = array(1, 2),
        $extended_defaults = array();

    public function __construct($options){
        array_merge($this->extended_defaults, $this->original_defaults);
        array_push($this->options, $this->original_defaults);
    }
}

class Bar extends Foo {
    protected $extended_defaults = array(3, 4);

    public function print(){
        print_r($this->$options);
    }
}

$bar = new Bar();
$bar->print();

Upvotes: 2

deanWombourne
deanWombourne

Reputation: 38485

Why would it combine your arrays?

You set $defaults to (1,2) and then to (3,4) - nowhere are you concatenating them.

Your constructor adds (1,2) onto $options. That's all it does.

Your print method outputs $defaults which at this point would be (3,4) because you initialise them as a protected var.

Upvotes: 2

John Conde
John Conde

Reputation: 219924

If you don't want those values to be overridden then use private instead of protected. This will prevent subclasses from overriding those values.

Upvotes: 1

Related Questions