Real-life example of Closure usage with PHP5.3


One of the new improvements in new PHP5.3 version are the Closures. Here you are a real-life example where closures are really useful for me. Imagine the following scenario. A very common scenario at least in my daily work. We have a recordset from a SQL:

$recordset = array(
    array('code' => 1, 'quantity' => 20, 'amount' => 2),
    array('code' => 2, 'quantity' => 30, 'amount' => 3),
    array('code' => 3, 'quantity' => 10, 'amount' => 1),
    array('code' => 4, 'quantity' => 20, 'amount' => 5),
);

Imagine we want to show the recordset into a datagrid but we also want to calculate the price (quality / amount) of each row and add a total. We can perform the operation within the SQL but I prefer to keep the SQLs as simple as I can and let the logic to PHP.

We can use array_walk to calculate the price but with PHP<5.3 we needed to pass the callback function as a string. A dirty trick. Maybe a simple foerach is a better solution, isn’t it?. Now with PHP5.3 we can create anonymous functions (aka Closures) in the finest JavaScript style. They are really cool. We even can use the ‘use’ statement to use variables outside Closuse’s scope. This attribute allows us in our example to calculate our totals.

Maybe is difficult to explain but the code is very clear:

$total = array('quantity' => 0, 'amount' => 0, 'price' => null);
array_walk($recordset, function (&$value) use(&$total){
    $value['price'] = ($value['amount'] == 0) ? null : $value['quantity'] / $value['amount'];
    $total['quantity'] += $value['quantity'];
    $total['amount'] += $value['amount'];
});
$total['price'] = ($total['amount'] == 0) ? null : $total['quantity'] / $total['amount'];

And now our $recordset array will have an extra item called register and we have created a new row for our recordset with total values.

recordset:

Array(
 [0]=>Array(1=>1 [quantity]=>20 [amount]=>2 [price]=>10)
 [1]=>Array(1=>2 [quantity]=>30 [amount]=>3 [price]=>10)
 [2]=>Array(1=>3 [quantity]=>10 [amount]=>1 [price]=>10)
 [3]=>Array(1=>4 [quantity]=>20 [amount]=>5 [price]=>4)
)

total:

Array(
 [quantity] => 80 [amount]=>11 [price] => 7.2727272727273
)
About these ads

About Gonzalo Ayuso

Web Architect specialized in Open Source technologies. PHP, Python, JQuery, Dojo, PostgreSQL, CouchDB and node.js but always learning.

Posted on December 6, 2010, in php, Technology, tips. Bookmark the permalink. 11 Comments.

  1. I don’t consider this to be a good example for closures.

    Why use a closure and array_walk when a simple foreach loop would be better.

    Otherwise your adding (relatively) a lot of overhead with all the function calls that will be required.

    Also, on a side note, using += before you initialise your array elements will cause an E_NOTICE to occur.

    • Damn! you caught me. E_NOTICE warnings. I’m one of those who think all warnings and notices must be considered as errors. It’s a good way to avoid bugs. But I must admit I normally break my own principles here. I can use but array_key_exists to check the value or initialize the array to avoid the E_NOTICE in the first iteration. OK those are excuses. You are right so I’ve change the code to avoid the warning (Updated).

      The other point I’m not agree with you. Probably I’m wrong, but I’m not sure a foreach will get better performance. By the way I want to create a benchmark to compare both methods. For me array_walk with closure is more clear and readable (may be because I come from python and it’s lambda functions). But’s that’s only my point of view

      • I took the courtesy of writing a little test.benchmark myself.

        http://pastebin.com/D9hb8vV5

        The results where partially as I suspected and some what surprising.

        My setup was PHP 5.3.0, Windows 7, XAMPP, Core 2 2.4GHz, 2GB

        As I expected array_walk was slightly slower in all tests I through at it.

        It averaged about 10% slower (0.9secs -> 1.9secs) on a test size of 100,000

        The surprising part was the memory usage.

        On a test size of 100,000 the array_walk method averaged 2,000,000% higher memory usage (500b -> 8,000,000b). And I tried a few things, to no avail, to get the memory down.

        I hope the memory issue I am seeing is a bug and is not present in 5.3.3

      • Oh, and on a side note, after you fixed the E_NOTICE, you made line 1 redundant.

      • Redundant line deleted :).

        I’ve executed the benchmark here (PHP5.3.3 openSuse 2GB) and time is slight higher with array_walk than foreach, especially visible on huge recordsets.

        with 10000:
        Array walk: 0.019652
        foreach: 0.014731

        with 100000:
        Array walk: 0.204589
        foreach: 0.174850

        The memory usage is exactly the same with both methods. After changing the benchmark. There is a bug on line 77. $value must be used as reference (&$value) to have the same behaviour than the array_walk approach

  2. Any particular reasons that your example would be better than a foreach loop in terms of performance, readability, maintainability, etc.?

    • OK. I can’t say if array_walk is better than foreach in terms of performance. I’ve heard it’s slight better but I’m not sure. Probably they are similar (I want to create a small benchmark next week). Readability an maintainability are the same (at least for me). I prefer this syntax indeed. Looks more clear for me. Maybe it’s because it’s a new feature and I like to play with new things ;). But they are a new tool we can use. we don’t need foreach. We can do all what we want with a simple for. But foreach, for, while and now array_walk with closuses are a set of tools we have to meet our needs

  3. Closures are really handy for this kind of use cases. However I find difficult to reuse closures without some additional class; for this reason, I explored the use of classes implementing the __invoke() magic method, which are callable exactly like anonymous functions but have real flexibility when you have only one method and you wonder how to name it.

    • Fully agree with you. If you want to reuse code it’s better to use a class. But for punctual operations like this the use of a class (pick a name, create a file, …) can be a too complex. However I hadn’t ever thought the use of __invoke() in a callback. That’s cool!. I really don’t like the ugly syntax of array(‘foo’, ‘bar’). txs

  4. I read your blog (few postings)…
    Would like to learn basics of Web designing or architecture whatever you call. I have my own sub-domain website but am not able to know if ‘this’ happens how to remove by ‘that’.

    Please don’t mind but email me some of the most basic learnings required to know about web architecture.

    Regards
    Sushil

  1. Pingback: Gonzalo Ayuso’s Blog: Real-life example of Closure usage with PHP5.3 | PHP

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 952 other followers

%d bloggers like this: