【问题标题】:php unset local reference affecting global scopephp取消设置影响全局范围的本地引用
【发布时间】:2012-12-28 04:17:19
【问题描述】:

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

<?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 =&amp; $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 =&amp; $in;),那么错误的行为就会消失。

更新

i have submitted a bug report

【问题讨论】:

  • 我看到分配给它的开发人员关闭了它。不过,他似乎没有正确解释这个问题。很好奇您是否已经撰写或考虑撰写某种后续行动?
  • 是的,我也看到了。我在历史上看到了你的评论。似乎开发人员不明白我想要展示的内容。也许他实际上并没有运行我的代码,因为他说注释掉该行没有任何区别,而且显然确实如此。

标签: php


【解决方案1】:

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

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

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

我会在 PHP 错误跟踪器上提交错误报告。值得一提的是,它仍然存在于 5.4.6 中。

【讨论】:

【解决方案2】:
$data = should_not_alter($data)

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

另外,当您创建引用 $in_ref =&amp; $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;
}

【讨论】:

  • $in_ref =&amp; $in['level1']; 肯定有效果。如果您将其注释掉,那么在测试 1 或测试 2 期间,“第一个值”不会在全局范围内取消设置。
  • 你是对的。但是,全局作用域只有在分配了$data 时才会生效。
  • 您是否有一些来源可以确认它确实符合设计并且这不是错误?
  • +1 到@eis。我怀疑真正发生的事情是 PHP 变成了“哦,废话,这很难修复。我们可以呃......只是把它称为一个功能?”对 what 正在发生的解释并不能让我相信 为什么 它正在发生。使用短语“奇怪,嗯?”并且“自动”指向我的错误。我也会与For most use cases this is desired and useful behavior. 争论,因为隐藏的副作用几乎不是我们想要的行为。但在这一点上,我被带走了。
  • +1 到@eis。该行为似乎与 php 处理除较低级别数组之外的所有其他引用变量的方式不一致。请注意,即使是数组的顶层(即$in_ref =&amp; $in;)也不会产生错误行为。
猜你喜欢
  • 1970-01-01
  • 2021-03-05
  • 1970-01-01
  • 2013-11-06
  • 1970-01-01
  • 1970-01-01
  • 2015-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多