按引用分配bug

时间:2014-09-28 02:41:01

标签: php opcodes

前几天我偶然发现了这个看似非常简单的问题How to changing value in $array2 without referring $array1?然而,我越是调查它就越奇怪,它似乎确实按预期发挥作用。在此之后,我开始研究从以下输出生成的操作码。

$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中获取只读值并将其回显。
  6. 即使是通过操作码,我也不确定这会出错。我觉得缺乏关于操作码的文档以及我与他们合作的经验不足可能使我无法弄清楚这出错的地方。

    编辑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引擎更好,更好。

1 个答案:

答案 0 :(得分: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在这个选择中生活了很多年,所以它只是其中一个"其中之一"。不幸的是!

相关问题