php取消设置影响全局范围的本地引用

时间:2012-12-28 04:17:19

标签: php

我遇到了一些非常奇怪的php行为(ubuntu 10.04上的5.3.2)。应该在本地范围内发生的未设置会影响调用者函数的范围。以下代码片段是我的代码的简化,它显示了我只能假设的错误:

<?php
function should_not_alter($in)
{
    $in_ref =& $in['level1'];
    should_only_unset_locally($in);
    return $in;
}
function should_only_unset_locally($in)
{
    unset($in['level1']['level2_0']);
}
$data = array('level1' => array('level2_0' => 'first value', 'level2_1' => 'second value'));
$data = should_not_alter($data); //test 1
//should_only_unset_locally($data); //test 2
print_r($data);
?>

如果运行上述内容,您将看到全局范围内的'first value'数组中未设置值$data。但是,如果您注释掉test 1并运行test 2,则不会发生这种情况。

我只能假设php不喜欢引用数组元素。在我的代码中,我需要更改$in_ref - 因此上面代码中$in_ref =& $in['level1'];行的原因。我意识到删除这一行将解决'first value'在全局范围内未设置的问题,但这不是一个选项。

任何人都可以确认这是否是php的预期行为?

我怀疑这是一个错误,而不是一个功能,因为这种行为与php处理具有普通(非数组)变量的作用域和引用的方式不一致。例如,使用字符串而不是数组函数should_only_unset_locally()对全局范围没有影响:

<?php

function should_not_alter($in)
{
    $in_ref =& $in;
    should_only_unset_locally($in);
    return $in;
}
function should_only_unset_locally($in)
{
    unset($in);
}
$data = 'original';
$data = should_not_alter($data); //test 1
//should_only_unset_locally($data); //test 2
print_r($data);

?>

test1或test2输出original正如预期的那样。实际上,即使$data是一个数组但$in_ref被引用到整个数组(即$in_ref =& $in;),那么错误的行为也会消失。

更新

i have submitted a bug report

2 个答案:

答案 0 :(得分:1)

是的,看起来像个臭虫。

正如函数的名称所暗示的那样,should_not_alter不应该改变数组,因为它是按值传递的。 (我当然不是仅基于名称 - 它也不应该根据其定义改变任何东西。)

评论$in_ref =& $in['level1'];使$in单独离开这一事实似乎进一步证明了这是一个错误。这是一个奇怪的小怪癖。不知道内部会发生什么导致这种情况。

我会在PHP bug跟踪器上提交错误报告。对于它的价值,它仍然存在于5.4.6。

答案 1 :(得分:0)

$data = should_not_alter($data)

此行覆盖$data数组,返回值为should_not_alter,即$in。这是正常行为。

此外,在您创建引用$in_ref =& $in['level1'];时,但您没有对其进行任何操作。它对程序输出没有影响。

简答:

在调用unset($in_ref)函数之前,通过should_only_unset_locally()删除引用变量。

答案很长:

创建对数组元素的引用时,数组元素将替换为引用。这种行为很奇怪,但它不是一个错误 - 它是语言的一个特征,而且是设计的。

考虑以下PHP程序:

<?php
$a = array(
    'key1' => 'value1',
    'key2' => 'value2',
);
$r = &$a['key1'];
$a['key1'] = 'value3';
var_dump($a['key1']);
var_dump($r);
var_dump($a['key1'] === $r);

Output:
string(6) "value3"
string(6) "value3"
bool(true)

$a['key1']分配值会更改$r的值,因为它们都引用相同的值。相反,更新$r将更新数组元素:

$r = 'value4';
var_dump($a['key1']);
var_dump($r);

Output:
string(6) "value4"
string(6) "value4"

该值不在$r$a['key']中 - 这些只是引用。这就像他们都引用了一些幽灵般隐藏的价值。很奇怪,是吗?

对于大多数用例,这是期望和有用的行为。

现在将此应用于您的程序。以下行修改本地$in数组,并使用引用替换'level1'元素:

$in_ref = &$in['level1'];

$in_ref不是$in['level1']的引用 - 而是它们都引用相同的怪异值。所以当这条线出现时:

unset($in['level1']['level2_0']);

PHP将$in['level1']视为对怪异值的引用,并删除'level2_0'元素。由于它是一个参考,因此在should_not_alter()函数的范围内也可以感受到删除。

解决您的特定问题的方法是销毁引用变量,该变量会自动将$in['level1']恢复为正常行为:

function should_not_alter($in) {
    $in_ref =& $in['level1'];
    // Do some stuff with $in_ref
    // After you're done with it delete the reference to restore $in['level1']
    unset($in_ref);
    should_only_unset_locally($in);
    return $in;
}