zen of coding

PHP5 and tiny foreach() improvement

While this issue is certainly not specific to CakePHP, I felt it was worthwhile to point out since we are often dealing with data arrays and foreach() loops to modify that data.

Consider the following example:

function afterFind($results, $primary=false) {
      if($primary == true) {
        foreach($results as $key => $value) {
            $results[$key][$this->alias]['my_field'] = $this->modifyField($value[$this->alias]['my_field']);
        }
      }

      return $results;
}

While this works just fine for most cases, the main issue is that $value in the above example is actually a copy of the array’s content.

In PHP5 we have the option to reference the array elements of $results, by using: “$results as &$value”.

So the modified code for PHP5 would look like:

function afterFind($results, $primary=false) {
      if($primary == true) {
        foreach($results as &$value) {
            $value[$this->alias]['my_field'] = $this->modifyField($value[$this->alias]['my_field']);
        }
      }
      unset($value);

        return $results;
}

The obvious improvement is that we are not creating any extra copies of our data. Sure, it may not cause any serious issues, but if making additional copies of the data can be easily avoided it probably should be. Secondly, it makes the code cleaner and more concise, which is always a good thing.

P.S. It is always a good idea to unset() the reference in case any further processing might accidentally change the value of our $results array.

  • w.

    Is there a way to get the key out as well using this method? (if the key were, for instance, a string rather than just an incremented numeric value)

  • teknoid

    @w.

    Yep, same as always: $array as $key => &$value

  • Ooooh, awesome tip teknoid, thanks!

  • teknoid

    @Brendon Kozlowski

    Nice, glad you liked ;)

  • dooltaz

    Overall I like this concept, and I plan to start using it. So thanks for the tip.

    Since the traditional $key and $value get replaced at each iteration, the memory you save will only become significant as the size of the $value increases. It would work great for multiple nested foreach() statements or if an element of your array has a large amount of data in it. It may also shave off the processing time it takes to copy each set of data.

    I wouldn’t go back through my code and change every foreach statement, but next time I need to optimize, this is a great strategy.

  • Adz

    Actually, PHP only makes a copy of the data when you *change* it.

    So doing this won’t actually copy any data, references are used internally …

    foreach ($results as $key => $value){
    $value2 = $value;
    $value3 = $value2;
    }

    But if you do this the data is actually duplicated.

    foreach ($results as $key => $value){
    $value2 = $value;
    $value3 = $value2;
    $value3[‘something’] = ‘something’; // $value3 is now copied & modified
    }

  • teknoid

    @Adz

    I’m pretty sure in your first example $value contains a copy of the $results data. Since regardless of the modification to the $value, the $results array will remain intact.

  • teknoid

    @dooltaz

    Thanks. I agree that in reality the impact on the performance for most real-world purposes is negligible, but like you say it wouldn’t be a bad habit to start using for any PHP5 app.

  • I’ve just discovered it yesterday (for beforeSave use) after one hour of searching… Next time please post your tips earlier ;)

  • teknoid

    @snowdog

    Alright, I guess I should also account for the time difference ;)

  • There is some major confusion here. Let me quote somebody:

    “PHP uses copy-on-write semantics, which means that there isn’t any data copied unless really necessary. It takes more time to create/use the reference than a simple assignment.

    Use references only when you really need them, not for “optimization”. The engine is smart enough to handle cases like above efficiently.” – Micha

    In short, there is no way to optimize your php code with “references”. I used to do this myself and now I know I was just wasting my time.

  • @Jonah

    I’m not quite sure, how that would be possible… or even if I’m understanding the point of that quote.

    If the above code was not to use a reference and the $value (second code snippet) is modified, the $results array data remains intact. In that case if $value is not technically a copy of the data in the $results, then what is it?

  • Apologies. I did not read your code correctly. But what I was saying can better be told through an example

    $foo = “A very long string”;
    $bar = $foo; //technically bar is not a copy of foo but instead references it (this is transparent to php programmers).

    $bar .= “more text”; //now that I modified $bar php internally copies $foo into $bar and then modifies it. This is for optimization purposes

    So, in a real life example:
    foreach($array as $value) {
    echo $value;
    }

    will not be more efficient than:

    foreach($array as &$value) {
    echo $value;
    }

  • @Jonah

    Thanks for clarification.

%d bloggers like this: