The drop is always movingYou know that saying about standing on the shoulders of giants? Drupal is standing on a huge pile of midgetsAll content management systems suck, Drupal just happens to suck less.Popular open source software is more secure than unpopular open source software, because insecure software becomes unpopular fast. [That doesn't happen for proprietary software.]Drupal makes sandwiches happen.There is a module for that

Closures with array_walk to avoid foreach &

Submitted by nk on Sun, 2013-11-17 07:34

Especially with Drupal 8 requiring PHP 5.4, closures (my brief intro) are going to much be much more useful and widespread. I would like to point out one advantage which already exists: array_walk($a, function (&$value, $key) {// some code here}) do not have any lingering after affects because both $value and $key are only visible in the scope of the closure. But foreach ($a as $key => &$value) { // same code here } leaves $value a reference to the last item and it's guaranteed to give you some nasty, nasty surprises down the line. More, the behavior of foreach itself might change compared to the non reference using foreach ($a as $key => $value), read how foreach actually works for much more (and a bonus headache).

Also, the header of the closure strictly separates the body of the loop from the rest of the function / method where array_walk is called, giving you a very clear overview of what might happen in the loop. For example

<?php
array_walk
($this->databaseContents[$this->table], function (&$row_array) use ($fields, $condition, &$affected) {
?>

We immediately know that the function might change the array values, will use the $fields and $condition variables and also change the $affected variable. We know that $fields and $condition can not change within this loop. This might not be visible even when reading the loop because it is possible that you have a function call:

<?php
array_walk
($this->databaseContents[$this->table], function (&$row_array) use ($fields, $condition, &$affected) {
       
$row = new DatabaseRow($row_array);
        if (
ConditionResolver::matchGroup($row, $condition)) {
?>

and without looking up matchGroup it is not possible to know whether it takes $condition by reference -- so just reading the code does not tell you what variables change and which ones are immutable. (And yes, objects passed around by handle and changing all the time is a bummer on this.)

Commenting on this Story is closed.

Submitted by nk on Mon, 2013-11-18 21:35.

Closures are indeed nice`n`warm`n`fuzzy, but everyone should remeber that they come with a performance penalty [1] [2], so be careful with unnecessary usage.

[1] http://willem.stuursma.name/2010/11/22/a-detailed-look-into-array_map-an...
[2] http://www.faieta.net/wp/performance-implications-of-closures-in-php/

amateescu

Submitted by Anonymous on Thu, 2013-11-21 18:48.

I'm not saying there's no performance penalty, as it makes sense that there would be (and the benchmarks suggest that, yes, there is) - but it's meaningless in a huge DB-driven, network-heavy application like most web apps are.

If it takes twice as long to do the closure, but makes your code safer or architecturally better, and "twice as long" equates to about 6ms vs 3ms per iteration, you take that penalty 95% of the time. If your app is doing intense processing, you may indeed need to opt for speed over flexibility or safety. But this is the rare case, and honestly you should be looking into building a C extension, not PHP, if you actually need every millisecond squeezed out of a particular algorithm.

Submitted by Anonymous on Sat, 2013-11-23 10:57.

I just did a benchmark about this a few days ago, and the closure results are definitely apalling. Specifically, for the same loop, I compared use of

  • a traditional for (i = ...)
  • a foreach (range)
  • array_map("callback_name")
  • array_map(array(class, static_method)
  • array_map(array(class_instance, method))
  • array_map(closure)

Results vary from machine to machine and even have some variation on the same machine, but using PHP 5.3.11, they consistently fall in three performance groups, in normalized units of time to accomplish the same task (so lower is better)

  • fast: for / foreach = 1,0
  • medium: array_map() method or static method: 1,8, array_map(function name): 2,0
  • slow: array_map() closure: 6,6

Yes, using closures as callbacks for array_map operations is more than 6 times slower than a plain old loop, and even more than three times slower than using array_map with a class/instance method.

To some degree, it makes sense since closures carry an environment along with the code while the other solutions don't. But still...

fgm