【问题标题】:PHP reference causes data corruption [duplicate]PHP引用导致数据损坏[重复]
【发布时间】:2013-11-04 19:22:46
【问题描述】:

我正在编写 PHP 代码来对数组中的每个值进行一些转换,然后从外部源(MySQL 游标或另一个数组)将一些值添加到数组中。如果我使用foreach 和转换数组值的引用

<?php
$data = array('a','b','c');

foreach( $data as &$x ) 
    $x = strtoupper($x);

$extradata = array('d','e','f'); 
// actually it was MySQL cursor

while( list($i,$x) = each($extradata) ) {
    $data[] = strtoupper($x);
}

print_r($data);
?>

(Here it is in PHPfiddle)

数据已损坏。所以我得到了

Array ( [0]=>A [1]=>B [2]=> [3]=>D [4]=>E [5] =>F )

而不是

Array ( [0]=>A [1]=>B [2]=>C [3]=>D [4]=>E [5] =>F )

当我不使用参考而写作时

foreach( $data as &$x ) 
    $x = strtoupper($x);

当然不会发生转换,但数据也没有损坏,所以我明白了

Array ( [0]=>a [1]=>b [2]=>c [3]=>D [4]=>E [5] =>F )

如果我写这样的代码

<?php
$result = array();

$data1 = array('a','b','c');

foreach( $data1 as $x ) 
    $result[] = strtoupper($x);

$data2 = array('d','e','f'); 
// actually it was MySQL cursor

while( list($i,$x) = each($data2) ) {
    $result[] = strtoupper($x);
}

print_r($result);
?>

一切都按预期进行。

Array ( [0]=>A [1]=>B [2]=>C [3]=>D [4]=>E [5] =>F )

当然,我复制数据解决了这个问题。但我想了解该参考有何奇怪的问题,以及如何避免此类问题。也许在代码中使用 PHP 引用通常是不好的(就像许多人所说的 C 指针)?

【问题讨论】:

  • 我已经在我的 PHP 安装 (win64-5.4.7) 上确认了相同的行为 - 我不知道为什么只有 C 消失了。如果您改用var_dump,您会看到C 已经来到&amp;NULL
  • 一个奇怪的代码。你想达到什么目的?为什么不先合并数组? (array_merge(), php.net/array_merge) 然后运行array_map() (php.net/array_map) 改变大小写。
  • > 为什么不先合并数组呢?最初我试图附加从 MySQL 游标中获取的值。后来我简化了代码,把它放到PHPfiddle中,制作了光标数组。
  • @deceze "可能重复" - 我已经尝试并看到随后的一对 foreach 循环可以正常工作!但是foreach 之后的While 会导致这种奇怪的行为。
  • 一步一步的解释应该解释发生了什么。您只需考虑分配给引用时会发生什么。无论是第二个foreachwhile 还是简单的=,都无关紧要。

标签: php reference


【解决方案1】:

PHP 语言的引用机制具有其他编程语言所不具备的特殊性。人们普遍认为,对象反映了通过对它的任何引用对其属性所做的所有更改。但是对引用本身的赋值要么被禁止,要么使引用指向另一个对象。取而代之的是,PHP 中的引用赋值将整个底层对象(引用指向的对象)替换为一个被赋值的对象。所以

$a = 1; $b = 2;
$r = &$a;
$r = $b;
echo $a; // will output '2'

这适用于赋值,但不适用于unset 调用,它不会破坏底层对象,但会破坏引用和指向对象之间的链接。

$a = 1; $b = 2;
$r = &$a;
unset($r); //!
$r = $b;
echo $a; // will output '1'

这种引用行为在某些情况下很有用,但它经常被误解,是什么导致了问题中所示的问题。

为了避免 PHP 引用出现问题,您应该:

  • 尽早取消设置所有引用(当它变得不必要时)。

所以,这段代码可以工作

<?php
$data = array('a','b','c');

foreach( $data as &$x ) 
    $x = strtoupper($x);
unset($x);

$extradata = array('d','e','f'); 
// actually it was MySQL cursor

while( list($i,$x) = each($extradata) ) {
    $data[] = strtoupper($x);
}

print_r($data);
?>
  • 通常认为在多个控制结构中重用局部变量名称是一种不好的风格。

所以下面的代码也可以工作

<?php
$data = array('a','b','c');

foreach( $data as &$x ) 
    $x = strtoupper($x);

$extradata = array('d','e','f'); 
// actually it was MySQL cursor

while( list($i,$y) = each($extradata) ) {
    $data[] = strtoupper($y);
}

【讨论】:

  • 谢谢! 奇怪的语言功能!它在哪里有用?
  • 它通过将变量引用传递给函数来使用,在那里它被分配并且变量值相应地改变。如果没有这样的引用分配规则,则必须将值包装到对象中才能通过引用传递它(就像在 JS 中发生的那样)。
  • 为什么'C'被替换为null?
  • 它因此被 each 中的每个值替换:'d'、'e'、'f',最后是 null(在每个 while 循环中断之后)。
  • PHP references you should 尽可能不要使用它们 ;)
【解决方案2】:

您在$extradata 的循环中再次使用$x,这会导致引用变得不稳定。

这行得通:

$data = array('a','b','c');

foreach( $data as &$x )
    $x = strtoupper($x);

$extradata = array('d','e','f');
// actually it was MySQL cursor

while( list($i,$anything_but_x) = each($extradata) ) {
    $data[] = strtoupper($anything_but_x);
}

print_r($data);

  • 不要重复使用变量
  • 避免引用

【讨论】:

    【解决方案3】:

    这对我有用....如果我知道您要做什么,也许我可以做得不同..但这应该可以。

    $data = array('a','b','c');
    
    foreach( $data as &$x ) 
        $x = strtoupper($x);
    
    $extradata = array('d','e','f'); 
    // actually it was MySQL cursor
    
    foreach ($extradata as &$x) {
        $data[] = strtoupper ($x);
    }
    

    【讨论】:

    • 与其说是 OP 试图完成什么,不如说 OP 看到了 OP 要求解释的奇怪行为。
    • @deceze,回答你的编辑......不,我只是出于个人原因语法不好......但我正在变得更好:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-17
    • 2020-10-11
    • 1970-01-01
    • 2012-02-17
    • 2011-01-12
    相关资源
    最近更新 更多