我遇到了一些非常奇怪的PHP行为(ubuntu 10.04的5.3.2)。本应在局部作用域中发生的取消设置影响了调用方函数的作用域。下面的代码片段是对我的代码的简化,我只能假设它显示了一个bug:
<?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
,则不会发生这种情况。
i只能假设PHP不喜欢引用数组中的元素。在我的代码,我需要改变$in_ref
-因此$in_ref =& $in['level1'];
行在上面的代码的原因。我意识到删除这一行将解决'first value'
在全局作用域中未设置的问题,但这不是一个选项。
谁能确认这是否是php的预期行为?
我怀疑这是一个bug,而不是一个特性,因为这种行为与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;
),那么错误的行为就消失了。
我已经提交了一个bug报告
是啊,看起来像个bug。
正如函数名所暗示的那样,should_not_alter
不应该改变数组,因为它是按值传递的。(当然,我不是仅仅基于名字——它也不应该根据它的定义改变任何东西。)
注释$in_ref =& $in['level1'];
使它离开$in
的事实似乎进一步证明它是一个bug。这是一个相当奇怪的小怪癖。不知道内部发生了什么会导致这种情况。
我会在PHP bug跟踪器上提交一个bug报告。不管怎样,它在5.4.6中仍然存在。
$data = should_not_alter($data)
这一行用should_not_alter
的返回值$in
覆盖$data
数组。这是正常的行为。
同样,当你创建一个引用$in_ref =& $in['level1'];
,但你没有做任何事情。对程序输出没有影响
短答:
在调用should_only_unset_locally()
函数之前通过unset($in_ref)
删除引用变量
长答:
创建对数组元素的引用时,将数组元素替换为引用。这种行为很奇怪,但它不是一个bug——这是语言的一个特性,也是设计出来的。
考虑以下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;
}