问题
I am writing PHP code to make some transformations of every value in an array, then to add some values to the array from external source (MySQL cursor or, say, another array). If I use foreach
and a reference to transform array values
<?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)
than data is beeing corrupted. So I get
Array ( [0]=>A [1]=>B [2]=> [3]=>D [4]=>E [5] =>F )
instead of
Array ( [0]=>A [1]=>B [2]=>C [3]=>D [4]=>E [5] =>F )
When I use no reference and write
foreach( $data as &$x )
$x = strtoupper($x);
transformation does not occur, of course, but data is not corrupted too, so I get
Array ( [0]=>a [1]=>b [2]=>c [3]=>D [4]=>E [5] =>F )
If I write code like this
<?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);
?>
everything works as expected.
Array ( [0]=>A [1]=>B [2]=>C [3]=>D [4]=>E [5] =>F )
Of course, I copying data solves the problem. But I would like to understand what is the strange trouble with that reference and how such troubles can be avoided. Maybe it is generally bad to use PHP references in code (like many people say about C-pointers)?
回答1:
References mechanism of PHP language has specific feature, that is not common to other programming languages. It is commonly accepted that object reflects all changes, made to its properties through any reference to it. But assignment to reference itself is either prohibited or makes the reference point to another object. Instead of this, assignment to reference in PHP substitutes the whole underlying object (object pointed by reference) with the one, beeing assigned. So
$a = 1; $b = 2;
$r = &$a;
$r = $b;
echo $a; // will output '2'
This is true to assigment, but not true to unset
call, which will not destroy underlying object, but break the link between the reference and pointed object.
$a = 1; $b = 2;
$r = &$a;
unset($r); //!
$r = $b;
echo $a; // will output '1'
This reference behaviour is useful in some cases, but it is often misunderstood, what leads to problems like shown in question.
To aviod problems with PHP references you should:
- Unset every reference as early as possible (at the point when it becomes unnecessary).
So, this code will work
<?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);
?>
- Generally it is considered a bad style to reuse local variable names in several control structures.
So the following code will work too
<?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);
}
回答2:
You're using $x
again in the loop over $extradata
, which causes the references to go wonky.
This works:
$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);
- Don't reuse variables
- Avoid references
回答3:
This works for me.... Maybe I could have done it differently if I knew what you were trying to do .. yet this should work.
$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);
}
来源:https://stackoverflow.com/questions/19593835/php-reference-causes-data-corruption