【问题标题】:Can you explicitly copy a PHP array containing "referenced" elements by value?您可以按值显式复制包含“引用”元素的 PHP 数组吗?
【发布时间】:2015-03-07 03:48:32
【问题描述】:

PHP 数组元素引用的背景

假设您使用这样的嵌套 PHP 数组创建了一个复杂的数据结构:

$a1 = array(
    'b' => array('foo' => 1),
    'c' => array('bar' => 1)
);

想象数组嵌套得更深,元素更多,名称更长,更有意义。

如果需要大量访问 $a1 的子结构以进行读写,可能会想创建一个像这样的“别名”:

$b = &$a1['b'];

但是,由于“赋值”实际上改变了 $a1,这会导致大量混乱。

我认为许多没有经验的 PHP 开发人员(比如我)会在赋值后假设 $b 是对 $a1['b'] 的引用。真正发生的是 $b 和 $a1['b'] 都变成了对元素 array('foo' => 1) 的引用,产生了意想不到的后果。我觉得这很不直观。

假设你需要保留 $a1 的原样,但你还需要一个 $a1 的副本,我们将副本称为 $a2,并更改 $a2 的一些元素:

$a2 = $a1;           // Copy $a1 to $a2
$a2['b']['foo'] = 2; // GOTCHA! This will change $a1['b']['foo'] as well!
$a2['c']['bar'] = 2; // This will not change $a1['c']['bar'] however

因为我们之前创建了对 $a1['b'] 的引用,所以该元素将通过引用分配,而 $a1 的其余部分按值复制。我花了好几个小时才弄明白。

var_dump($a1);
var_dump($a2);

会输出

array (size=2)
  'b' => &
    array (size=1)
      'foo' => int 2
  'c' => 
    array (size=1)
      'bar' => int 1

array (size=2)
  'b' => &
    array (size=1)
      'foo' => int 2
  'c' => 
    array (size=1)
      'bar' => int 2

请注意'b'元素后面的&,表示它是一个引用。并且 $a1['b']['foo'] 的值从 1 变为 2。

我的问题

当源数组中的元素可能已被引用时,是否有一种万无一失的方法来按值复制数组

或者换句话说:有什么方法可以确保在将 $a1 复制到 $a2 时 $a1['b'] 按值复制,以便更改 $a2['b'] 不会破坏 $a1 ['b']?

我发现你可以这样做:

unset($b);      // Remove reference to $a1['b']
$a2 = $a1;      // Now all of $a1 will be copied to $a2 by value
$b = &$a1['b']; // Recreate reference

当然,如果有更多对 $a1['b'] 或 $a1 的其他元素的引用,您需要将它们全部取消设置。我希望有一种更聪明的方法,例如 =&(通过引用赋值)运算符的“逆”或按值递归复制深度嵌套的关联数组的函数。即使创建了对您不知道的数组元素的引用,它也始终有效。

这是迄今为止我找到的最接近解决方案的方法。它似乎有效,但它并不“漂亮”:

$a2 = json_decode(json_encode($a1), true);

研究完成

我的问题与 SO 上的其他几个问题有关。但是,我还没有找到我的具体问题的答案。甚至是一个答案,可以直观地理解这种奇怪和意想不到的行为背后的机制。例如,Array reference confusion in PHP 的答案引用了 PHP 手册:

但是请注意,数组内部的引用可能是 危险的。使用 右侧的参考不会将左侧变成 引用,但数组内的引用保留在这些正常的 任务。

我已阅读手册的部分,但发现它没有帮助。它没有提供更深入的理解,也没有解释如何安全地使用对数组元素的引用。 PHP 手册页What References Do 上的一条评论提到了这种奇怪的行为,但没有就如何处理它提出任何建议。

这只是你必须学会​​忍受的那些记录不充分的PHP“怪癖”之一吗?或者对主题有更深入的了解会有所帮助,在这种情况下,我最有可能在哪里找到启示?

【问题讨论】:

标签: php arrays reference


【解决方案1】:

你做了很好的研究。

只是不明白什么是确切的问题?

我想我找到了另一个非常接近您的 json 编码解码变体的解决方案:

有什么方法可以确保在将 $a1 复制到 $a2 时,$a1['b'] 按值复制,以便更改 $a2['b'] 不会破坏 $a1[' b']?

请检查:

$a1 = array(
    'b' => array('foo' => 1),
    'c' => array('bar' => 1)
);

$b = &$a1['b'];

$a2 = unserialize(serialize($a1));           // Copy $a1 to $a2
$a2['b']['foo'] = 2; // GOTCHA! 
$a2['c']['bar'] = 2; // 

var_dump($a1);
var_dump($a2);

为我输出:

array(2) {
  ["b"]=>
  &array(1) {
    ["foo"]=>
    int(1)
  }
  ["c"]=>
  array(1) {
    ["bar"]=>
    int(1)
  }
}
array(2) {
  ["b"]=>
  array(1) {
    ["foo"]=>
    int(2)
  }
  ["c"]=>
  array(1) {
    ["bar"]=>
    int(2)
  }
}

这是你要求的吗?

顺便说一下,这里有一个比较 json_encode 和 serialize 的链接,它可能会有所帮助

Preferred method to store PHP arrays (json_encode vs serialize)

【讨论】:

  • 感谢有趣的阅读。据我所知,与 json_encode/json_decode 相比,serialize/unserialize 替代方案要慢一些,但在某些特殊情况下还有其他额外的好处。也许这就是的答案。我想我只是希望有一些不那么“骇人听闻”的东西......我还更新了我的问题,以便 实际问题 希望能更好地脱颖而出。
猜你喜欢
  • 2011-10-10
  • 2021-08-02
  • 2021-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多