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

对于这种PHP按值调用的行为有合理的解释吗?还是PHP bug?,php,arrays,reference,pass-by-reference,pass-by-value,Php,Arrays,Reference,Pass By Reference,Pass By Value,PHP5.5.12。考虑这一点: <?php $a = [ 'a', 'b', 'c' ]; foreach($a as &$x) { $x .= 'q'; } print_r($a); 现在考虑: <?php $a = [ 'a', 'b', 'c' ]; foreach(z($a) as &$x) { $x .= 'q'; } print_r($a); function z($a) { return $a; } 等一下$未通过引用传递

PHP5.5.12。考虑这一点:

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

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

function z($a)
{
    return $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
)

对此有合理的解释吗?

在本例中,函数z什么都不做。它不会复制或克隆任何内容,因此z()的响应与根本不调用相同。您只是返回传入的对象,因此响应与预期一致

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

function z($a)
{
    return $a;
}
两个对象的ID均为“1”,表明它们不是副本或克隆。

Update 已打开Bug以解决此问题。为了从foreach中删除引用限制,该行为已被更改


从中可以清楚地看到,这种行为随着时间的推移而发生了变化:

更新2 固定的;这将在5.5.18和5.6.2中提供

PHP5.4 在PHP 5.5之前,您的代码实际上会引发一个致命错误:

致命错误:无法创建对临时数组表达式元素的引用
PHP5.5-5.6 当函数结果直接在
foreach
块内使用时,这些版本不执行写时复制。因此,现在使用原始数组,对元素的更改是永久性的

我个人觉得这是一个bug;书面抄袭应该已经发生了

PHP>5.6 在可能成为下一个主要版本的基础的中,常量数组是不可变的,因此只有在这种情况下才能正确执行写时复制。如下所示声明数组将出现与phpng相同的问题:

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

黑客(HHVM) 只有黑客才能正确处理当前的情况

正确的方法 通过引用使用函数结果的方法如下:

$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;
}

其他修复 为了避免更改原始阵列,目前,您有以下选项:

  • 将函数结果分配到
    foreach
    之前的临时变量中
  • 不要使用参考资料
  • 换成黑客

  • 在我看来,在示例2中,
    $a
    是通过引用传递的。这与PHP5.4~中的情况确实不同。可能与5.5中引入的数组解引用更改有关上一个示例无效<代码>$b=z($a)一次应用于整个数组。从中可以明显看出,您实际上不应该依赖这种行为;事实上,这可能是一个bug。为您准备了另一个古怪的示例~@Nairebis刚刚与另一位php src开发人员进行了一次快速聊天,他同意这是一个bug。但在php中,对象是通过引用隐式传递的,而数组不是(应该是这样的),因此您的示例不准确。不过,您可能会发现“函数z不做任何事情”。这可能只是一次编译器优化work@Phil我同意Phil的观点,观察到的行为可能是优化的结果,imho,不应该以这种方式工作。你传递和返回的是对对象的引用,而不是对象本身。参考确实是复制的。事实上,根据手册,这也不应该起作用~我已经确认这在5.5.19中是固定的(ubuntu 14.04 64位通过ppa:ondrej/php5)
    <?php
    $obj = new stdClass();
    $obj->name = 'foo';
    
    function z($a)
    {
        $a->name = 'bar';
        return $a;
    }
    
    var_dump($obj);
    var_dump(z($obj));
    
    object(stdClass)#1 (1) {
      ["name"]=>
      string(3) "foo"
    }
    object(stdClass)#1 (1) {
      ["name"]=>
      string(3) "bar"
    }
    
    $foo = 'b';
    $a = ['a', $foo, 'b'];
    
    $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;
    }