2012-12-28 38 views
6

我遇到了一些非常奇怪的PHP行为(5.3.2在Ubuntu 10.04上)。在本地范围内发生的未设置会影响呼叫者功能的范围。下面的代码片段是我的代码的简化,显示什么,我只能认为是一个错误:php未设置影响全局范围的本地参考

<?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

+0

我看到它被分配给它的开发关闭它。尽管如此,他似乎没有正确解释问题。我很好奇,如果你已经写或考虑写一些跟进? – Corbin

+0

是的,我也看到了。我在历史中看到你的评论。似乎开发人员不明白我想表现出什么。也许他实际上没有运行我的代码,因为他说注释掉这条线没有什么区别,显然它确实如此。 – mulllhausen

回答

0
$data = should_not_alter($data) 

这条线被重写$data阵列的should_not_alter返回值,这是$in。这是正常的行为。

此外,虽然你正在创建一个参考$in_ref =& $in['level1'];,但你没有做任何事情。它对程序输出没有影响。

简短的回答:

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

龙答案:

当创建一个数组元素的引用,所述阵列元件被替换为参考。这种行为很奇怪,但它不是一个错误 - 它是语言的一个特征,并且是设计的。

考虑下面的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; 
} 
+0

'$ in_ref =&$ in ['level1'];'肯定有效果。如果你将它评论出来,那么在测试1或测试2期间,'第一个值'在全局范围内不会被取消设置。 – mulllhausen

+0

你是对的。但是,全局范围仅在分配了'$ data'时才会生效。 – leepowers

+2

你会有一些来源确认它确实是设计的,这不是一个错误? – eis

1

是啊,看起来像一个错误。

正如函数的名字暗示的那样,should_not_alter不应该因数值传递而改变数组。 (我当然不会仅仅基于它的名字 - 它也不应该基于它的定义来改变任何东西)。

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

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

+0

会做,谢谢。 – mulllhausen

+0

这不是一个错误。这是设计,请参阅我的修订答案:http://stackoverflow.com/a/14064555/212700 – leepowers