Array question

Hi,
I am reading the php manual on php.net
I can see a sample code like this :




<?php
$colors = array('red', 'blue', 'green', 'yellow');
// PHP 5
foreach ($colors as &$color) {
    $color = strtoupper($color);
}
unset($color); /* ensure that following writes to
$color will not modify the last array element */

// Workaround for older versions
foreach ($colors as $key => $color) {
    $colors[$key] = strtoupper($color);
}

print_r($colors);
?>

The above example will output:

Array
(
    [0] => RED
    [1] => BLUE
    [2] => GREEN
    [3] => YELLOW
)



But I do not understand why the unset is needed,
indeed if I remove unset, I have this :

Array
(
    [0] => RED
    [1] => BLUE
    [2] => GREEN
    [3] => GREEN
)

and the last value YELLOW disappeaered.

Can someone help me understand why ?
(
the link to the manual , array part :
http://www.php.net/manual/en/language.types.array.php
)

LVL 9
matthew016Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Guy Hengel [angelIII / a3]Billing EngineerCommented:
the unset is needed because of the & in the following line:
>foreach ($colors as &$color) {

which make &$color a reference variable, and not only a variable having the value.
that way, the line:
$color = strtoupper($color);
will indeed change the array item.
if you remove the & in the above line, the array items will NOT get changed, and the output would be:

Array
(
    [0] => red
    [1] => blue
    [2] => green
    [3] => yellow
)
0
TomeeboyCommented:
The ampersand in front of the variable $color is making it a reference to the array value, which means that any changes to $color will effect the array value, and vice versa.  Since it loops through the array, the last reference set is to the last value of the array, which means if that value, or $color is modified afterwards, they both get changed.

Hope that helps a bit :)
0
matthew016Author Commented:
i know that  &$color is a reference

But Why without unset, the last value is lost in the second loop,
this will give new values to $color :
>>foreach ($colors as $key => $color) {
0
Cloud Class® Course: Certified Penetration Testing

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

TomeeboyCommented:
Yeah, what he said ;)

Here's more information on that, which I dug up from a quick Google search.  It explains it fairly well and gives some examples:
http://devzone.zend.com/manual/view/page/language.variables.html
0
matthew016Author Commented:
I know what a reference is,
and that if u change the reference, the referenced variable is altered as well,
but does that answer to my question here ?

I still cant see why the array has its last element altered.
0
Steve BinkCommented:
Interesting.  Here's some code that illustrates the example more clearly:

<?
$colors = array('red', 'blue', 'green', 'yellow');
// PHP 5
foreach ($colors as &$color) {
    $color = strtoupper($color);
}
print_r($colors);
echo "<br>";
// Workaround for older versions
foreach ($colors as $key => $color) {
print_r($colors);
echo "<br>";

      echo "$key=$color<br>\n";
    $colors[$key] = strtoupper($color);
}
echo "<br>";
print_r($colors);
?>

In the first part of the example, $color is set to $colors[0] through $colors[3] in turn, but the last reference assigned is $colors[3].  Without the unset(), the second loop starts with $color pointing to the 4th element in array $colors.

Now, first iteration:  
$colors[0] = "RED"
$key = 0
$color = $colors[3] = "RED"
$colors[$key] = $colors[0] = $color (or $colors[3], which equals "RED")

Next:
$colors[1] = "BLUE"
$key = 1
$color = $colors[3] = "BLUE"
$colors[$key] = $colors[1] = $color (or $colors[3], which equals "BLUE")

Next:
$colors[2] = "GREEN"
$key = 2
$color = $colors[3] = "GREEN"
$colors[$key] = $colors[2] = $color (or $colors[3], which equals "GREEN")

Now we go the last iteration, but you can see that $colors[3] now equals "GREEN", thus 'losing' the last element of the array.

Questions?

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
TomeeboyCommented:
Well, some consider it a bug in PHP, but others consider it normal behaviour (including PHP.net).  You be the judge:
http://bugs.php.net/bug.php?id=29992

The 3rd post down has an explaination for the behavior.  Hope this clears things up for you somewhat ;)
0
matthew016Author Commented:
Thank u routinet,
yes one thing still confuses me,

Since we have this in the second loop:
>> foreach ($colors as $key => $color) {

Why is $color (which is a reference to $color[3]) not replaced by the new $color in the loop.

To make my question more clear, u have put this in your comment :
$color = $colors[3] = "BLUE"

But that means that in the second loop we have :
- $color    (  from foreach ($colors as $key => $color)  )
- $color    (  which is a reference to $color[3]  )

Two  $color  ?
0
Steve BinkCommented:
They (the bug reporter, et al) make an interesting case.  The claim is that the foreach() construct should be responsible for clearing references used in the loop.  I refute that by saying PHP did not create the reference, so why should the engine clean it up?  

This issue is an example of why good programming practices are a must.  The last post on the report comments that this could lead to disastrous consequences if an included library does not clean up after itself.  I say that's a bug in the designer app, not in PHP, and that programmer should be cleaning up after himself or herself.

I would agree that they need to document this behavior better.  The docs don't say anything specifically regarding this.  I did see at least 5 user comments on the foreach() page for this problem, though.
0
Steve BinkCommented:
In the second loop, $color is simply a pointer to the location of $color[3].  Any time you assign something to $color, it will be stored in, and overwrite, the space reserved for $color[3].  As I tried to illustrate in my example, $color IS being assigned the color.  In the first iteration, "RED" goes into $color, which is EXACTLY the same as "RED" going in to $colors[3].  If you stopped the loop at that point, you would see this for print_r($colors):

Array
(
    [0] => RED
    [1] => BLUE
    [2] => GREEN
    [3] => RED
)

If you stopped it after the second iteration, you would see "BLUE" in $color[3].  "GREEN" for the third iteration.  The fourth iteration is different because the loop is essentially reading a variable and storing it into the same variable.  

The loss of the 4th value happens the moment the second loop begins, unless you use unset() to remove the previous reference.  As the loop progresses, $colors[3] will store every individual value in the array, excepting the original one because it was overwritten.
0
matthew016Author Commented:
Thank u,

it is perfectly clear now,

well, for me that will be :

- do not use foreach ($colors as &$color) {
- use foreach ($colors as $key => $color) {
0
Steve BinkCommented:
Using a reference in the foreach() construct is not the problem, and it can come in handy.  Don't discount it based on this issue.  Do remember to include the unset() if you use it.  

Good luck to you!
0
matthew016Author Commented:
I accepted the question,  I have one more question, if it doesnt bother u too much.

You explain me that using a reference in a loop can be handy,
but in this case, I don't see what u get more by using a reference,
since this :  foreach ($colors as $key => $color) {
will also change the array
0
Steve BinkCommented:
No bother, that's why we're here.  

In this context, the reference is simply to provide for a quick shorthand back to the original.  For example, say you have an array named $my_really_long_array_name.  As you iterate through each element of the array, you want to commit a change or otherwise manipulate the original value.  If you do not use the reference:

foreach ($my_really_long_array_name as $key=>$val) {
  // do whatever up here
  // then change the original value
  $my_really_long_array_name[$key] = $newval;
}

With the reference:

foreach ($my_really_long_array_name as $key=>&$val) {
  // do whatever up here
  // then change the original value
  $val = $newval;
}
unset($val);

That's sort of basic, but you get the idea.  Imagine having a 3-, 4-, or 5- dimensional array, nested foreach() loops, and a need to change original values at multiple levels.  Those shorthand references become handy in a mighty way.
0
matthew016Author Commented:
Thank u  i see now    :-)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
PHP

From novice to tech pro — start learning today.