php - 对于这种 PHP 按值调用行为有合理的解释吗?还是 PHP 的错误?

标签 php arrays reference pass-by-reference pass-by-value

PHP 5.5.12。考虑一下:

<?php
$a = [ 'a', 'b', 'c' ];
foreach($a as &$x) {
    $x .= 'q';
}
print_r($a);

正如预期的那样,输出:

Array
(
    [0] => aq
    [1] => bq
    [2] => cq
)

现在考虑:

<?php
$a = [ 'a', 'b', 'c' ];
foreach(z($a) as &$x) {
    $x .= 'q';
}
print_r($a);

function z($a)
{
    return $a;
}

输出:

Array
(
    [0] => aq
    [1] => bq
    [2] => cq
)

(!)但是等一下。 $a 没有通过引用传递。这意味着我应该从 z() 获取一个副本,该副本将被修改,并且 $a 应该保持不变。

但是当我们强制 PHP 发挥其写时复制魔法时会发生什么:

$a = [ 'a', 'b', 'c' ];
foreach(z($a) as &$x) {
    $x .= 'q';
}
print_r($a);

function z($a)
{
    $a[0] .= 'x';
    return $a;
}

为此,我们得到了我所期望的:

Array
(
    [0] => a
    [1] => b
    [2] => c
)

编辑:再举一个例子...

$a = [ 'a', 'b', 'c' ];
$b = z($a);
foreach($b as &$x) {
    $x .= 'q';
}
print_r($a);

function z($a)
{
    return $a;
}

这按预期工作:

Array
(
    [0] => a
    [1] => b
    [2] => c
)

这有合理的解释吗?

最佳答案

更新

错误67633已开放以解决此问题。该行为已被 this commit 更改努力消除 foreach 的引用限制。

<小时/>

来自this 3v4l output您可以清楚地看到这种行为随着时间的推移而发生了变化:

更新2

已修复 this commit ;这将在 5.5.18 和 5.6.2 中提供。

PHP 5.4

在 PHP 5.5 之前,您的代码实际上会引发 fatal error :

Fatal error: Cannot create references to elements of a temporary array expression

PHP 5.5 - 5.6

当直接在 foreach block 内使用函数结果时,这些版本不会执行写时复制。因此,现在使用原始数组,并且对元素的更改是永久性的。

我个人认为这是一个bug;写时复制应该发生。

PHP > 5.6

phpng branch ,这很可能成为下一个主要版本的基础,常量数组是不可变的,因此只有在这种情况下才能正确执行写入时复制。像下面这样声明数组将会在 phpng 中出现同样的问题:

$foo = 'b';
$a = ['a', $foo, 'b'];

Proof

黑客攻击(HHVM)

只有 Hack 能够正确处理目前的情况。

正确的方法

documented通过引用使用函数结果的方法是这样的:

$a = [ 'a', 'b', 'c' ];
foreach(z($a) as &$x) {
    $x .= 'q';
}
print_r($a);

// indicate that this function returns by reference 
// and its argument must be a reference too
function &z(&$a)
{
    return $a;
}

Demo

其他修复

为了避免更改原始数组,目前您有以下选择:

  1. foreach之前将函数结果分配给临时变量;
  2. 不要使用引用文献;
  3. 切换到 Hack。

关于php - 对于这种 PHP 按值调用行为有合理的解释吗?还是 PHP 的错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24772958/

相关文章:

php - 使用 PHP 和新的 Twitter API

php - 如何在laravel中将css嵌入到HTML中?

php - MY SQL查询线程消息收件箱并发送

JavaScript:将 [a,b,c] 转换为 [a][b][c]

java - 如何使用流将 String 数组转换为 int 数组

php - foreach by reference 循环中 unset 的奇怪行为

c# - 找不到引用系统

php - PHP 生成表上的 jQuery 表搜索过滤器

c++ - 三元运算符在 return 语句中给出意想不到的结果

php - 使用 mysql 和 php 仅返回一次月份和日期