17

前几天我遇到了这个看似非常简单的问题如何在不引用 $array1 的情况下更改 $array2 中的值?然而,我对它的研究越多,它似乎就越奇怪,这确实按预期运行。在此之后,我开始研究从以下输出生成的操作码。

$array1 = array(2, 10);
$x = &$array1[1];
$array2 = $array1;
$array2[1] = 22;

echo $array1[1]; // Outputs 22

这对我来说似乎很疯狂,因为 array2 应该只是 array1 的副本,并且一个数组发生的任何事情都不应该影响另一个数组的内容。当然,如果您注释掉第二行,最后一行将像预期的那样回显 10。

看得更远,我可以看到一个很酷的网站,它向我展示了 PHP 使用 Vulcan Logic Dumper 生成的操作码。这是上述代码生成的操作码。

Finding entry points
Branch analysis from position: 0
Return found
filename:       /in/qO86H
function name:  (null)
number of ops:  11
compiled vars:  !0 = $array1, !1 = $x, !2 = $array2
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  >   INIT_ARRAY                                       ~0      2
         1      ADD_ARRAY_ELEMENT                                ~0      10
         2      ASSIGN                                                   !0, ~0
   4     3      FETCH_DIM_W                                      $2      !0, 1
         4      ASSIGN_REF                                               !1, $2
   5     5      ASSIGN                                                   !2, !0
   6     6      ASSIGN_DIM                                               !2, 1
         7      OP_DATA                                                  22, $6
   8     8      FETCH_DIM_R                                      $7      !0, 1
         9      ECHO                                                     $7
        10    > RETURN                                                   1

这些操作码在这里没有很好地记录http://php.net/manual/en/internals2.opcodes.php但我相信在英语中操作码正在执行以下操作。按行...可能对我来说比其他任何人都重要。

  1. 第 3 行:我们用它的第一个值初始化数组,然后在将它分配给 $array1 之前给它加 10。
  2. 第 4 行:获取只写?数组中的值并通过引用 $x 来分配它。
  3. 第 5 行:将 $array1 设置为 $array2。
  4. 第 6 行:获取 1 的数组索引。我猜 od_data 将其设置为 22,尽管 $6 从未返回。OD_DATA 绝对没有文档,并且在我看过的任何地方都没有列为操作码。
  5. 第 8 行:从 $array1 的索引 1 中获取一个只读值并将其回显。

即使通过操作码工作,我也不确定这是哪里出错了。我有一种感觉,缺乏关于操作码的文档,而且我没有使用它们的经验,这可能使我无法弄清楚哪里出了问题。

编辑1:

正如 Mike 在第一个评论数组中指出的那样,复制数组时会保留引用状态。这里可以看到文档以及它链接到http://php.net/manual/en/language.types.array.php#104064的数组文章中的一个位置。这很有趣,不被视为警告。如果这是真的,那么令我惊讶的是,没有像您期望的那样保留此代码的参考状态。

$array1 = array(2, 10);
$x = &$array1;
$array2 = $array1;
$array2[1] = 22;

echo $array1[1]; // Output is 10

因此,似乎只有当您尝试通过引用分配单个元素时才会发生这种情况,从而使此功能更加混乱。

为什么php只在单独分配数组索引时保留它们的状态?

编辑2:

我今天使用 HHVM 进行了一些测试,HHVM 以您认为的方式处理了第一个代码片段。我喜欢 PHP,但 HHVM 比 Zend 引擎看起来越来越好。

4

1 回答 1

10

这在 PHP 手册中有解释(即使你必须花费比你应该花费更多的时间才能找到它),特别是在http://php.net/manual/en/language.types.array。 php#104064

“共享”数据保持共享,初始分配仅充当别名。直到您开始使用独立操作来操作数组,...[] = ...解释器才开始将它们视为不同的列表,即使这样,共享数据仍然保持共享,因此您可以拥有两个具有共享前n 个元素但后续数据不同的数组。

对于一个数组到另一个数组的真正“按值复制”,你几乎最终会做类似的事情

$arr2 = array();
foreach($arr1 as $val) {
  $arr2[] = $val;
}

或者

$arr2 = array();
for($i=count($arr1)-1; $i>-1; $i--) {
  $arr2[$i] = $arr[$i];
}

(使用反向循环主要是因为没有足够的人记住这是您可以做的事情,并且比前向循环更有效=)

你会想象会有一个array_copy函数或其他东西来帮助处理数组复制怪癖,但似乎没有。这很奇怪,但却是“PHP 的状态”之一。过去做出了一个选择,因此 PHP 已经在这个选择中生活了好几年,所以它只是“其中之一”。很遗憾!

于 2014-09-28T03:43:45.593 回答