views:

581

answers:

3

I love the Hash implementation of Ruby where you can initialize the Hash object with a default value. At the moment I'm struggling with implementing a similar object in PHP. This is my first (non-working) shot at this.

class DefaultArray extends ArrayObject {

  protected $_defaultValue;

  public function setDefault($defaultValue) {
    $this->_defaultValue  = $defaultValue;
  }

  public function offsetExists($index) {
    return true;
  }

  public function offsetGet($index) {
    if(!parent::offsetExists($index)) {
      if(is_object($this->_defaultValue))
        $default = clone $this->_defaultValue;
      else 
        $default = $this->_defaultValue;

      parent::offsetSet($index, $default);
    }
    return parent::offsetGet($index);
  }
}

$da = new DefaultArray();
assert($da["dummy"] == null);
$da->setDefault = 1;
assert($da["dummy2"] == 1);

The second assertion will fail. Stepping through the code shows that offsetGet is called and the if clause is executed. Nevertheless any array value is null. Any ideas for alternative implementations?

I'm tired of writing

if(!isset($myarr['value']))
    $myarr['value'] = new MyObj();
$myarr['value']->myVal=5;

instead of just writing

$myarr['value']->myVal=5;
+3  A: 
$da->setDefault(1);

You can also use the __construct magic function:

class DefaultArray extends ArrayObject
{
    public function __construct($value = null){
        if(if(is_null($value)))
        {
            $this->value = 'default';
        } else {
            $this->value = $value;
        }
    }
}
erenon
Thanks, you spotted my error! It's $da->setDefault(1) instead of $da->setDefault = 1! Oh the embarassment!With this modification my example works as expected.I also thought about passing the default value in the constructor but wanted to preserve the original constructor arguments of ArrayObject.
chiborg
A: 

Why so complicated?

function initVal($value) {
    global $myarr;
    if(!isset($myarr['value']))
        $myarr['value'] = new MyObj();
}

Now you just have to call:

initVal('bla');
$myarr['bla']->bla = 'bla';

But I see, yours is much more neat.

cimnine
Using the ArrayObject class keeps all of the variables and supporting methods in one place. Where as using the *global* function has a much higher probability to lead to maintenance problems in the future.
null
+2  A: 

Try the magic methods __get.

class DefaultArray extends ArrayObject {
    protected $_defaultValue;

    public function setDefault($defaultValue) {
        $this->_defaultValue  = $defaultValue;
    }

    public function __get($index) {
        return $this->offsetGet($index);
    }

    public function offsetGet($index) {
        if(!parent::offsetExists($index)) {
            if (is_object($this->_defaultValue)) {
                $default = clone $this->_defaultValue;
            } else {
                $default = $this->_defaultValue;
            }
            parent::offsetSet($index, $default);
        }
        return parent::offsetGet($index);
    }
}

Now you just need to use different keys as the read access will initialize that array items:

$da = new DefaultArray();
assert($da['foo'] == null);
$da->setDefault(1);
assert($da['bar'] == 1);
Gumbo