An example of using anonymous functions with PHP

If you’re not familiar with them, anonymous functions seem to be a term of art that very advanced programmers throw around to make others feel dumb.  Until now, my closest experience has been in using them with jQuery and javascript, but tonight I found a use for them in PHP.   If you don’t read the wikipedia article linked before, let me pull out a quote that reflects the "Aha!" moment I had tonight. 

Anonymous functions can be used to contain functionality that need not be named and possibly for short-term use.

A sorting and filtering example

While true anonymous functions won’t be available until PHP 5.3, you can use create_function to generate functions on the fly by passing them a signature and string containing valid php code.  These functions are perfect for use with array sorting functions like usort or uasort as well as array_filter.

For this example, suppose we have the following data:

  1.  
  2. $sam = new StdClass;
  3. $sam->minutes_played = 1300;
  4. $sam->goals = 5;
  5. $sam->fouls = 10;
  6.  
  7. $max = new StdClass;
  8. $max->minutes_played = 1900;
  9. $max->goals = 6;
  10. $max->fouls = 10;
  11.  
  12. $joe = new StdClass;
  13. $joe->minutes_played = 1100;
  14. $joe->goals = 4;
  15. $joe->fouls = 8;
  16.  
  17. $team = array($sam, $max, $joe);

You’ve been tasked with reporting on the data, specifically, finding who has the most minutes, goals, and fouls. Traditionally, you’d be inclined to write three functions that sort the array by field you are interested, and return the elements with the highest values. It’s a bit of a brute force approach, but let’s see how we can do this with a single function. Below is that function.

  1.  
  2. /**
  3. * Returns an array of all elements in the array that have the highest value in a given field.
  4. * @param string field
  5. * @param array stats data
  6. * @return array
  7. */
  8. public function findMaxStat($field, $data)
  9. {
  10. // sorting comparision
  11. $sort_code = '
  12. if ($a->' . $field . ' == $b->' . $field . ') return 0;
  13. return ($a->' . $field . ' < $b->' . $field . ' ? -1 : 1);
  14. ';
  15.  
  16. // get the data sorted highest to lowest by the field we desire.
  17. usort($data, create_function('$a,$b', $sort_code));
  18. $data = array_reverse($data);
  19.  
  20. // remove all elements of the array that do not match the max value
  21. $max = $data[0]->$field;
  22. $filter_code = 'return ($a->' . $field . ' == ' . $max . ');';
  23. $values = array_filter($data, create_function('$a', $filter_code));
  24.  
  25. return $values;
  26. }

What’s going on here? 

  1. First, to use the function we simply pass it the field for which we want to get the maximum value and our data array. 
  2. Line 11, defines the body of a function that will be used to sort the array by that field.  You’ll have to follow closely the use of quotes to build this string.  If we’re looking for most minutes, then the code for this function would look like ‘if $a->minutes == $b->minutes) return 0’ and so on, where $field is replaced by the string ‘minutes’; 
  3. Lines 17 and 18 do the hard work, usort uses our anonymous function to sort the data and then reverse it so that it’s ordered from highest to lowest.  You could also achieve this by changing what th $sort_code function returns and eliminating the call to array_reverse altogether.
  4. In line 21, We get our maximum value for the field we care about from the first element in our sorted array.
  5. Lines 22-23 are here to ensure that we return all the elements that have the max value of our field.  This acocunts for situations where more than one element may be "tied for first place", so to speak.
  6. Finally, we return the values that we’ve found.

What’s the big deal here?  Well, with one function we quickly get the rows we care about.

  1.  
  2. // returns $sam and $joe
  3. $max_fouls = findMaxStat('fouls', $team);
  4. // returns $max
  5. $max_minutes = findMaxStat('minutes_played', $team);
  6. // returns $max
  7. $max_goals = findMaxStat('goals', $team);

More importantly, if we add more fields to our data, we don’t have to write more code!

Conclusion

Used correctly, anonymous functions provide very customized functionality that can save you from having to write more code. Also, by learning to use PHP’s built-in array manipulation functions, you’ll quickly learn how to slice and dice nested arrays and arrays of objects without resorting to complicated loops and if statements. I’m not sure what the performance implications might be of using the technique, but if it saves you from writing long, complicated code-blocks it’s worth the trade off.  There’s a lot of existing discussion about create_function, and its impact on memory usage and DANGER!