PHP:如何将引用插入到数组中?

7

我正在尝试在数组的特定位置插入值:

$hi = "test";
$var2 = "next";
$arr = array(&$hi);
$arr[] = &$var2; // this works
array_splice($arr, 1, 0, &$var2); // this doesn't

为什么使用splice插入数组失败,而使用第一种方法不会失败?
4个回答

8

这是一个快速粗略的答案,但请注意,使用引用调用此函数已被弃用,并且可能(根据您的PHP配置)会生成警告:

array_splice($arr, 1, 0, array(&$var2));

答案是:发生的事情非常微妙。当您进行splice操作时,由于在该位置插入了一个引用,$var2实际上正在被重新赋值。可以使用以下代码进行验证:
<?php
  $hi = "test";
  $var2 = "next";
  $arr = array(&$hi);
  $arr[] = &$var2; // this works
  printf("=== var2 before splice:\n%s\n", var_export($var2, TRUE));
  array_splice($arr, 1, 0, &$var2); // this doesn't
  printf("=== var2 after splice:\n%s\n", var_export($var2, TRUE));
?>

你将会得到以下结果:
=== var2 before splice:
'next'
=== var2 after splice:
array (
  0 => 'next',
)

请注意,在splice之前,$var2是一个字符串,正如您所期望的那样('next')。然而,在splice之后,$var2被替换为一个包含一个元素(字符串“next”)的数组。
我认为导致这种情况的原因是文档中所说的:“如果replacement不是数组,则会将其类型转换为数组(即(array)$parameter)”。所以正在发生的事情是这样的:
- 您将&$var2作为replacement传递到数组中。 - 在内部,php将&$var2转换为array(&$var2)。它实际上可能正在执行与$param = array($param)等效的操作,这意味着&$var2将设置为array(&$var2),并且由于它是引用而不是像通常一样的$var2的副本,因此这会影响通常超出调用范围的变量。 - 现在将$var2的新值移动到最后位置,并在第二个位置插入$var2的副本。
我不确定内部发生的所有巫术,但是$var在splice期间肯定被重新分配了。请注意,如果使用第三个变量,由于它没有将某些东西分配给已经存在的引用,因此它按预期工作:
<?php
  $hi = "test";
  $var2 = "next";
  $var3 = "last";
  $arr = array(&$hi);
  $arr[] = &$var2; // this works
  array_splice($arr, 1, 0, &$var3);
  printf("=== arr is now\n%s\n", var_export($arr, TRUE));
?>

生成结果:
=== arr is now
array (
  0 => 'test',
  1 => 'last',
  2 => 'next',
)

我认为调用array(&$var2)的原因是因为在内部,splice不需要通过自赋值将&$var2转换为array(&$var2),从而改变了$var2。你明确地创建了一个新的符号array(&$var2),它在内存中是物理上分离的,不需要自赋值。 - King Skippus
P.P.S. 这种意外的副作用正是为什么调用时传递引用已被弃用的原因。 ;) - King Skippus
@AndyLobel 你应该把采纳的答案改成这个。 - Hamish
@Hamish 这个回答确实解释了问题,但我不认为他实际提到了如何解决它哈哈(如果我错了请纠正)。 - Andy Lobel
@AndyLobel:好的,我在顶部放了一个快速而简单的解决方案。 - King Skippus
显示剩余5条评论

2

你可能需要将最后一个参数改为数组,否则根据手册中的说明,它将会进行类型转换。

array_splice( $arr, 1, 0, array( &$var2 ) ); 

解决方案是正确的,但是出于@Travesty3的原因。类型转换不是问题 - 调用时间传递引用失败。 - Hamish
如果替换只是一个元素,则不必将array()放在其周围,除非该元素本身是一个数组、对象或NULL。 - Nir Alfasi

2
当我尝试像您的示例一样的示例时,我会收到一个警告,提示“已弃用调用时间传递引用”。根据这个答案
“您可以在php.ini文件中将allow_call_time_pass_reference设置为true。 但这是一种hack方法。”

0

这里我正在遍历一个数组($values_arr),并将每个变量的引用存储在另一个数组($params)中。根据您的需求,您可以更改它的用法。

$params = array();
$values_arr = array('a', 'b', 123);
foreach ($values_arr as $key=>&$val) {
    $params[$key] = &$val;
}

结果

array (size=3)
  0 => &string 'a' (length=1)
  1 => &string 'b' (length=1)
  2 => &int 123

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接