12

I can access anywhere inside the multi-dimensional an array via reference method. And I can change the its value. For example:

$conf = array(
    'type' => 'mysql',
    'conf' => array(
            'name' => 'mydatabase',
            'user' => 'root',
            'pass' => '12345',
            'host' => array(
                    '127.0.0.1',
                    '88.67.45.123',
                    '129.34.123.55'
            ),
            'port' => '3306'
    )
);

$value = & $this->getFromArray('type.conf.host');
$value = '-- changed ---';

// result
$conf = array(
    'type' => 'mysql',
    'conf' => array(
            'name' => 'mydatabase',
            'user' => 'root',
            'pass' => '12345',
            'host' => '-- changed ---'
            'port' => '3306'
    )
);

BUT, I can't destroy the that section:

// normally success
unset($conf['type']['conf']['host']);

// fail via reference
$value = & $this->getFromArray('type.conf.host');
unset($value);

Is there a solution?

eburhan
  • 123
  • 1
  • 5

4 Answers4

6

Ok, better answer I think. In order to unset , you should get a reference to the container array, then unset the element within the array;

i.e.

$value = & $this->getFromArray('type.conf');

unset  $value['host'];
Zak
  • 24,947
  • 11
  • 38
  • 68
  • Yes - if you take a reference to a value in an array, PHP allows you to update the value via that reference, but it doesn't allow you to delete the array entry by `unset`ing the reference. See e.g. http://3v4l.org/OsYck – Rich Jun 19 '13 at 10:55
2

References are not like hard-links. If you unset a reference, this will not unset the origin value.

<?php 
$a = 5;
xdebug_debug_zval('a'); // a: (refcount=1, is_ref=0), int 5

$b = &$a;
xdebug_debug_zval('a'); // a: (refcount=2, is_ref=1), int 5
xdebug_debug_zval('b'); // b: (refcount=2, is_ref=1), int 5

unset($b);
xdebug_debug_zval('a'); // a: (refcount=1, is_ref=0), int 5

Why not write a little Config class which abstracts the data (array)? Since objects are always passed by reference you won't need to handle this at your own.

class Config
{
    // ...
}

$config = new Config(array(
    'db' => array(
        'name' => 'mydatabase',
        'user' => 'root',
        'pass' => '12345',
    )
));

$config->get('db.user');
$config->set('db.user', 'newuser');
$config->unset('db.user');
//...
Philippe Gerber
  • 17,457
  • 6
  • 45
  • 40
  • 1
    References *are* like hard-links. If you change the second reference, it changes the value in the first. If you delete the second reference, it doesn't delete the first. This is pretty much exactly analogous to hard links in a file system. – Rich Jun 19 '13 at 10:54
1

Here is my function for unsetting nested keys

public function unsetKey(string $dotSeparatedKey)
{
    $keys = explode('.', $dotSeparatedKey);
    $pointer = &$this->data;
    $current = false; // just to make code sniffer happy
    // we traverse all but the last key
    while (($current = array_shift($keys)) && (count($keys) > 0)) {
        // if some key is missing all the subkeys will be already unset
        if (!array_key_exists($current, $pointer)) {
            // is already unset somewhere along the way
            return;
        }
        // set pointer to new, deeper level
        // called for all but last key
        $pointer = &$pointer[$current];
    }
    // handles empty input string
    if ($current) {
        // we finally unset what we wanted
        unset($pointer[$current]);
    }
}
Tomáš Fejfar
  • 11,129
  • 8
  • 54
  • 82
0

Creating some functions for my framework, think they halp you.

1. Function set value in array using reference for navigation
- if reference ending by name/key, this name/key will be equal setting value
- if reference ending by delimiter, last name/key will be array with setting value

function array_reference_set($input_arr=array(),$reference='',$delimiter='->',$set_var=''){
    switch ($reference){
        case (is_string($reference)):
            $reference = array_reverse(explode($delimiter, $reference),true);
            break;
        case (!is_array($reference)):
            return $input_arr;
    }
    $key = array_pop($reference);
    if (count($reference)<1){
        if($key!=''){
            $input_arr[$key] = $set_var;
        }elseif (!is_array($input_arr) && $key==''){
            $input_arr = array($set_var);
        }elseif ($key==''){
            $input_arr[] = $set_var;
        }
    }else{
        if (!is_array($input_arr)){
            $input_arr = array($key=>array());
        }
        if (isset($input_arr[$key])){
            $input_arr[$key] = $this->array_reference_set($input_arr[$key],$reference,$delimiter,$set_var);
        }else{
            $input_arr[$key] = $this->array_reference_set(array(),$reference,$delimiter,$set_var);
        }
    }
    return $input_arr;
}

$arr = array_reference_set(array(),'a->b->c','->','test');
//equal
$arr = array('a'=>array('b'=>array('c'=>'test')));//or
$arr['a']['b']['c'] = 'test';

$arr = array_reference_set(array(),'a->b->c->','->','test');
//equal
$arr = array('a'=>array('b'=>array('c'=>array('test'))));//or
$arr['a']['b']['c'][] = 'test';

2. Function set unset value from array using reference

- if reference ending is delimiter, then will be unset varible with name/key befor delimiter
- one moment of using this function: you need update array by returned result of function (in the end of code example)

function array_reference_unset($input_arr=array(),$reference='',$delimiter='->'){
    switch ($reference){
        case (is_string($reference)):
            $reference = array_reverse(explode($delimiter, $reference),true);
            break;
        case (!is_array($reference)):
            return $input_arr;
    }
    $key = array_pop($reference);
    if (count($reference)<1 && is_string($key)){
        if ($key!=''){
            unset($input_arr[$key]);
        }else{
            return false;
        }
    }else{
        if (isset($input_arr[$key])){
            $ret = $this->array_reference_unset($input_arr[$key],$reference,$delimiter);
            if ($ret!==false){
                $input_arr[$key] = $ret;
            }else{
                unset ($input_arr[$key]);
            }
        }
    }
    return $input_arr;
}

$arr = array('a'=>array('b'=>array('c'=>'test')));// test subject

$arr = array_reference_unset($arr,'a->b->c','->');//and
$arr = array_reference_unset($arr,'a->b->c->','->');
//equal
unset($arr['a']['b']['c']);

p.s. sorry for my pure English