Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP reference causes data corruption [duplicate]

Tags:

php

reference

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

like image 943
user2920383 Avatar asked Oct 25 '13 15:10

user2920383


1 Answers

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);
}
like image 191
mas.morozov Avatar answered Oct 29 '22 09:10

mas.morozov