使用引用复制数组; 如何只获取值

I was messing with arrays and references, when I came across an interesting problem. Take this code, for example:

// Set everything up
$testArray = array(
    'a' => array(),
    'b' => array()
);

$saved = array();

$ref =& $testArray['b'];

// Set a value via the reference
$ref = array(1);

// Save the current array state
$saved[] = $testArray; // This shouldn't be a reference, right?

// Set another value via the reference
$ref = array(2);

// Save the current array state
$saved[] = $testArray; // This shouldn't be a reference, right?

Nothing too complicated. It makes an array, and a reference to a "deep value" in the array.

When I var_dump($saved), the result is not what I expect! I get:

array(2) {
  [0]=>
  array(2) {
    ["a"]=>
    array(0) {
    }
    ["b"]=>
    &array(1) {
      [0]=>
      int(2)
    }
  }
  [1]=>
  array(2) {
    ["a"]=>
    array(0) {
    }
    ["b"]=>
    &array(1) {
      [0]=>
      int(2)
    }
  }
}

I thought arrays were copied by value, not reference. Why is [b] equal to array(2) in both spots? How can I copy an array and break its references?

I want the result to be:

array(2) {
  [0]=>
  array(2) {
    ["a"]=>
    array(0) {
    }
    ["b"]=>
    array(1) {
      [0]=>
      int(1)
    }
  }
  [1]=>
  array(2) {
    ["a"]=>
    array(0) {
    }
    ["b"]=>
    array(1) {
      [0]=>
      int(2)
    }
  }
}

Why is $ref still linked to a copy of the $testArray array? I want to basically save the "state" of the array, and have the reference only update the original and not the copy.

Your array contains arrays' references, if you copy your array, the sub-arrays are just references... you need a deep copy. Make a recursive function:

function cloneArray($array){
    $newArray = array();

    foreach($array as $key => $value){
        if(is_array($value))
            $value = cloneArray($value);

        $newArray[$key] = $value;
    }

    return $newArray;
}

this should work for your needs

This is expected behaviour. You can use the function debug_zval_dump() to see that indeed the refcounter to the index b is 2.

// Set everything up
$testArray = array(
    'a' => array(),
    'b' => array()
);

$saved = array();

$ref =& $testArray['b'];

// Set a value via the reference
$ref = array(1);

// Save the current array state
$saved[] = $testArray; // This shouldn't be a reference, right?
debug_zval_dump($saved[0]);

// Set another value via the reference
$ref = array(2);

// Save the current array state
$saved[] = $testArray; // This shouldn't be a reference, right?

What gives you:

array(2) refcount(3){
  ["a"]=>
  array(0) refcount(1){
  }
  ["b"]=>
  &array(1) refcount(2){
    [0]=>
    long(1) refcount(1)
  }
}

If you want to circumvent this behaviour you need to unset the reference and get a new one before modifying it:

// Set everything up
$testArray = array(
    'a' => array(),
    'b' => array()
);

$saved = array();

$ref =& $testArray['b'];

// Set a value via the reference
$ref = array(1);
unset($ref);

// Save the current array state
$saved[] = $testArray; // This shouldn't be a reference, right?
debug_zval_dump($saved[0]);

$ref =& $testArray['b'];
// Set another value via the reference
$ref = array(2);
unset($ref);

// Save the current array state
$saved[] = $testArray; // This shouldn't be a reference, right?

var_dump($saved);

However, this is just theoretical stuff. You can of course do it easier without using references at all.