Lee
Lee

Reputation: 5936

PHP foreach and references

I have trying to modify values using pointers within nested foreach loops in PHP.. The following line however does not seem work:

// Assign a the attribs value to the array
$link_row['value'] = $args[ $u_value ];

The variable $args[ $u_value ]; is populated and can be output without issue, but when i added it to the $link_row reference it just doesnt seem to set..

  foreach ($unique_links as $link_id => &$link_attr)
  {
     foreach($link_attr as &$link_row)
     {
        foreach($link_row as $u_attr => &$u_value)
        {
           if ($u_attr == 'attribute_name') 
           {               

              // Assign a the attribs value to the array
              $link_row['value'] = $args[ $u_value ];

              // If one of the values for the unique key is blank,  
              // we can remove the entire 
              // set from being checked
              if ( !isset($args[ $u_value ]) ) 
              {
                 unset($unique_links[$link_id] );
              }
           }
        }
     }
  }

Upvotes: 3

Views: 3607

Answers (3)

f.ardelian
f.ardelian

Reputation: 6956

You must always unset variables after using them in a foreach. Even if you have two foreach(..) statements one right after the other. Even if you have a foreach(..) inside another foreach(..). No exceptions!

foreach ($unique_links as $link_id => &$link_attr)
{
 foreach($link_attr as &$link_row)
 {
    foreach($link_row as $u_attr => &$u_value)
    {
       if ($u_attr == 'attribute_name') 
       {               

          // Assign a the attribs value to the array
          $link_row['value'] = $args[ $u_value ];

          // If one of the values for the unique key is blank,  
          // we can remove the entire 
          // set from being checked
          if ( !isset($args[ $u_value ]) ) 
          {
             unset($unique_links[$link_id] );
          }
       }
    }
    unset($u_value);  // <- this is important
 }
 unset($link_row);  // <- so is this
}
unset($lnk_attr);  // <- and so is this, even if you reached the end of your program or the end of a function or a method and even if your foreach is so deeply indented or on such a long line that you're not sure what code might follow it, because another developer (maybe even you) will come back and read the code and he might not see that you used a reference in a foreach

Here's another fun piece of code that messed up a big project not long ago:

foreach ($data as $id => &$line) {
    echo "This is line {$id}: '{$line}'\n";
    $line .= "\n";
}

echo "And here is the output, one line of data per line of screen:\n";

foreach ($data as $id => &$line) {
    echo $line;
}

The fact that someone didn't unset($line) right after the first foreach(..) really messed up the data in the array, because &$line was a reference and the second foreach(..) assigned it a different value as it looped through the data and it kept overwriting the last line of data.

Upvotes: 6

jon_darkstar
jon_darkstar

Reputation: 16768

Since I can't see your arrays I don't know which are integer, which associate, etc.

As far I can see there is no reason for the refernece of $u_value. It doesn't do any harm, but makes no difference either way.

More importantly, any time your second if condition would be true you will have an error before you reach it on the line

 $link_row['value'] = $args[ $u_value ];

maybe you'd like to use

$link_row['value'] = isset($args[$u_value]) ? $args[ $u_value ] : "NOT PRESENT";

The line you mention seems to work just fine.

My code:

    $args = array(100,200,300,400,500);
    $unique_links = array (array(   'a' => array('attribute_name' => 1,'x' => 2, 'y' => 3, 'z' =>4), 
                                    'b' => array('attribute_name' => 3,'x' => 2, 'y' => 3, 'z' =>4),
                                    'c' => array('attribute_name' => 0,'x' => 2, 'y' => 3, 'z' =>4),
                                    'd' => array('attribute_name' => 7,'x' => 2, 'y' => 3, 'z' =>4),
                                    'e' => array('attribute_name' => 1,'x' => 2, 'y' => 3, 'z' =>4)

                                    ));                                 
    echo_r($unique_links);
    foreach ($unique_links as $link_id => &$link_attr)
    {
        foreach($link_attr as &$link_row)
        {
            foreach($link_row as $u_attr => $u_value)
            {
                echo "&nbsp&nbsp&nbsp&nbsp $u_attr is $u_value <br />";
                if ($u_attr == 'attribute_name')
                {

                    // Assign a the attribs value to the array
                    $link_row['value'] = isset($args[$u_value]) ? $args[ $u_value ] : "NOT PRESENT";

                    // If one of the values for the unique key is blank, we can remove the entire
                    // set from being checked
                    if ( !isset($args[ $u_value ]) )
                    {
                        //echo "want to kill: $link_id <br />";
                        //unset($unique_links[$link_id] );
                    }
                }
            }
            echo "<br />";
        }
    }
    echo_r($unique_links);

My output:

Array
(
[0] => Array
    (
        [a] => Array
            (
                [attribute_name] => 1
                [x] => 2
                [y] => 3
                [z] => 4
            )

        [b] => Array
            (
                [attribute_name] => 3
                [x] => 2
                [y] => 3
                [z] => 4
            )

        [c] => Array
            (
                [attribute_name] => 0
                [x] => 2
                [y] => 3
                [z] => 4
            )

        [d] => Array
            (
                [attribute_name] => 7
                [x] => 2
                [y] => 3
                [z] => 4
            )

        [e] => Array
            (
                [attribute_name] => 1
                [x] => 2
                [y] => 3
                [z] => 4
            )

    )

)

 attribute_name is 1
 x is 2
 y is 3
 z is 4
 value is 200

 attribute_name is 3
 x is 2
 y is 3
 z is 4
 value is 400

 attribute_name is 0
 x is 2
 y is 3
 z is 4
 value is 100

 attribute_name is 7
 x is 2
 y is 3
 z is 4
 value is NOT PRESENT

 attribute_name is 1
 x is 2
 y is 3
 z is 4
 value is 200

Array
(
[0] => Array
    (
        [a] => Array
            (
                [attribute_name] => 1
                [x] => 2
                [y] => 3
                [z] => 4
                [value] => 200
            )

        [b] => Array
            (
                [attribute_name] => 3
                [x] => 2
                [y] => 3
                [z] => 4
                [value] => 400
            )

        [c] => Array
            (
                [attribute_name] => 0
                [x] => 2
                [y] => 3
                [z] => 4
                [value] => 100
            )

        [d] => Array
            (
                [attribute_name] => 7
                [x] => 2
                [y] => 3
                [z] => 4
                [value] => NOT PRESENT
            )

        [e] => Array
            (
                [attribute_name] => 1
                [x] => 2
                [y] => 3
                [z] => 4
                [value] => 200
            )

    )

)

I comment out the unnset because it seems to kill the whole array, not just the part you intend. I guess its some weird behavior due to killing the part you are currently iterating.

Upvotes: 0

Phill Pafford
Phill Pafford

Reputation: 85318

I think you might be overwriting the value in the loop. To test this you could do something like this

$link_row['value'] = $args[ $u_value ];

Change this to

$link_row[] = $args[ $u_value ];

Then outside of the loops add this

echo "Link Row Value(s):<pre>".print_r($link_row,true)."</pre><br />\n";

This will show all of the values that are being cast/set to $link_row['value'], if you see more than one index the values are being overwritten

Upvotes: 0

Related Questions