I have a question about PHP and the use of pointers and variables.
The following code produces something I wouldn't have expected:
<?php
$numbers = array('zero', 'one', 'two', 'three');
foreach($numbers as &$number)
{
$number = strtoupper($number);
}
print_r($numbers);
$texts = array();
foreach($numbers as $number)
{
$texts[] = $number;
}
print_r($texts);
?>
The output is the following
Array
(
[0] => ZERO
[1] => ONE
[2] => TWO
[3] => THREE
)
Array
(
[0] => ZERO
[1] => ONE
[2] => TWO
[3] => TWO
)
Notice the 'TWO' appearing twice in the second array.
It seems that there is a conflict between the two foreach loops, each declaring a $number variable (once by reference and the second by value).
But why ? And why does it affect only the last element in the second foreach ?
The key point is that PHP does not have pointers. It has references, which is a similar but different concept, and there are some subtle differences.
If you use var_dump() instead of print_r(), it's easier to spot:
$collection = array(
'First',
'Second',
'Third',
);
foreach($collection as &$item){
echo $item . PHP_EOL;
}
var_dump($collection);
foreach($collection as $item){
var_dump($collection);
echo $item . PHP_EOL;
}
... prints:
First
Second
Third
array(3) {
[0]=>
string(5) "First"
[1]=>
string(6) "Second"
[2]=>
&string(5) "Third"
}
array(3) {
[0]=>
string(5) "First"
[1]=>
string(6) "Second"
[2]=>
&string(5) "First"
}
First
array(3) {
[0]=>
string(5) "First"
[1]=>
string(6) "Second"
[2]=>
&string(6) "Second"
}
Second
array(3) {
[0]=>
string(5) "First"
[1]=>
string(6) "Second"
[2]=>
&string(6) "Second"
}
Second
Please note the &
symbol that's left in the last array item.
To sum up, whenever you use references in a loop, it's good practice to remove them at the end:
<?php
$collection = array(
'First',
'Second',
'Third',
);
foreach($collection as &$item){
echo $item . PHP_EOL;
}
unset($item);
var_dump($collection);
foreach($collection as $item){
var_dump($collection);
echo $item . PHP_EOL;
}
unset($item);
... prints the expected result every time.
You should break the reference after the first loop.
foreach($numbers as &$number)
{
$number = strtoupper($number);
}
unset($number);
as stated in documentation:
Warning Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().
Also, if you use var_dump() instead of print_r(), you'll notice that the last element of array after the first loop is a reference:
array(4) {
[0]=>
string(4) "ZERO"
[1]=>
string(3) "ONE"
[2]=>
string(3) "TWO"
[3]=>
&string(5) "THREE"
}
If you follow Stefan Gehrig's comments on question, there is a link that perfectly explains this behaviour: http://schlueters.de/blog/archives/141-References-and-foreach.html
variable $number
is initialized even after loop , you need to break the reference by unset
this code works properly:
<?php
$numbers = array('zero', 'one', 'two', 'three');
foreach($numbers as &$number)
{
$number = strtoupper($number);
}
print_r($numbers);
unset($number);
$texts = array();
foreach($numbers as $number)
{
$texts[] = $number;
}
print_r($texts);
?>
http://www.php.net/manual/en/language.references.unset.php
When you unset the reference, you just break the binding between variable name and variable content. This does not mean that variable content will be destroyed.
...think about this as analogous to the Unix unlink call.
http://uk.php.net/manual/en/control-structures.foreach.php
Warning about foreach
Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With