对于这种PHP按值调用的行为有合理的解释吗?还是PHP bug?
PHP5.5.12。考虑这一点:对于这种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; } 等一下$未通过引用传递
<?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;
}