Gcoop
Gcoop

Reputation: 3382

PHP assign value from array by reference to variable. Change variable would change value in array

I have a bunch of strings like "memory.caching" and "server.base.url". Within my configuration object each part of the key "." is equal to a key within an array, the value could be another array and the last would be the value so "memory.caching" would equate to.

$config = array(
  "memory" => array(
    "caching" => true
  )
);

I want to create a setter method. I have ended up with the below code, but that won't work for three or four levels of depth. How can I do it without adding multiple else/if clauses.

public function set($k, $v) {
  $parts = explode(".", $k);

  // Start sucky code.
  if (count($parts) == 2)
  {
    $this->config[$parts[0]][$parts[1]] = $val;
  }
}

I was thinking some form of loop with assigning by reference like below, but I couldn't get it to work.

public function set($k, $v) {
  $parts = explode(".", $k);

  $tmp = $this->config;
  foreach ($parts as $p) {
    $tmp &= $tmp[$p];
  }

  $tmp = $v;
}

Any ideas on how I could achieve this?

Upvotes: 1

Views: 559

Answers (3)

Dan Lugg
Dan Lugg

Reputation: 20592

While this question has since been answered (and accepted) I'll just throw this version out there for future visitors:

(Might be missing some checks, I have a few versions of this kicking around)

class Map
{

    protected $_array = array();

    protected $_separator;

    public static function get(Array $array, $key, $separator)
    {
        $parts = explode($separator, $key);
        $key = array_shift($parts);
        while (!empty($parts))
        {
            if (!isset($array[$key]) || !is_array($array[$key]))
            {
                return null;
            }
            $array = &$array[$key];
            $key = array_shift($parts);
        }
        return isset($array[$key]) ? $array[$key] : null;
    }

    public static function has(Array $array, $key, $separator)
    {
        $parts = explode($separator, $key);
        $key = array_shift($parts);
        while (!empty($parts))
        {
            if (!isset($array[$key]) || !is_array($array[$key]))
            {
                return false;
            }
            $array = &$array[$key];
            $key = array_shift($parts);
        }
        return isset($array[$key]);
    }

    public static function set(&$array, $key, $value, $separator)
    {
        $parts = explode($separator, $key);
        $key = array_shift($parts);
        while (!empty($parts))
        {
            if (!isset($array[$key]) || !is_array($array[$key]))
            {
                $array[$key] = array();
            }
            $array = &$array[$key];
            $key = array_shift($parts);
        }
        $array[$key] = $value;
    }

    public static function bind(&$array, $key, &$variable, $separator)
    {
        $parts = explode($separator, $key);
        $key = array_shift($parts);
        while (!empty($parts))
        {
            if (!isset($array[$key]) || !is_array($array[$key]))
            {
                $array[$key] = array();
            }
            $array = &$array[$key];
            $key = array_shift($parts);
        }
        if (isset($array[$key]))
        {
            $variable = $array[$key];
        }
        $array[$key] = &$variable;
    }

    public static function remove(&$array, $key, $separator)
    {
        $parts = explode($separator, $key);
        $key = array_shift($parts);
        while (!empty($parts))
        {
            if (!isset($array[$key]) || !is_array($array[$key]))
            {
                return;
            }
            $array = &$array[$key];
            $key = array_shift($parts);
        }
        unset($array[$key]);
    }

    public function __construct(&$array, $separator)
    {
        if (!is_array($array))
        {
            $array = array();
        }
        $this->_array = $array;
        $this->_separator = (string) $separator;
    }

    public function __get($key)
    {
        return static::get($this->_array, $key, $this->_separator);
    }

    public function __isset($key)
    {
        return static::has($this->_array, $key, $this->_separator);
    }

    public function __set($key, $value)
    {
        static::set($this->_array, $key, $value, $this->_separator);
    }

    public function __unset($key)
    {
        static::remove($this->_array, $key, $this->_separator);
    }

    public function get_array()
    {
        return $this->_array;
    }

}

And use it like:

$array = array(
    'foo' => array(
        'bar' => array(
            'hello' => 'world',
        ),
    ),
);
$map = new Map($array, '.');

var_dump($map->{'foo.bar.hello'}); 
//string(5) "world"

$map->{'foo.bar.goodbye'} = 'universe';
unset($map->{'foo.bar.hello'});
var_dump($map->get_array());
// array(1) {
//   ["foo"]=>
//   array(1) {
//     ["bar"]=>
//     array(1) {
//       ["goodbye"]=>
//       string(8) "universe"
//     }
//   }
// }

var_dump(isset($map->{'foo.bar.goodbye'}));
// true

Bind is very useful, but doesn't have a magic method to alias it with semantically.

Upvotes: 0

alex
alex

Reputation: 490173

To set a value...

public function set($path, $value, $delimiter = '.') {

    $tokens = explode($delimiter, $path);

    $currentPiece = &$this->config;

    foreach($tokens as $token) {
       $currentPiece = &$currentPiece[$token];
    }

    $currentPiece = $value;  

}

CodePad.

To get a value...

public function get($path, $delimiter = '.') {

    $tokens = explode($delimiter, $path);

    $currentPiece = $this->config;

    while ($token = array_shift($tokens)) {
        $currentPiece = $currentPiece[$token];
    }

    return $currentPiece;

}

CodePad.

Upvotes: 2

DaveRandom
DaveRandom

Reputation: 88647

One slightly horrible, but definitely works approach to this is to use eval() (evil):

public function set($k, $v) {
  eval('$this->config["'.implode('"]["', explode(".", $k)).'"] = $v;');
}

Upvotes: 0

Related Questions