tags:

views:

727

answers:

3

I'm checking out some php 5.3.0 features and ran across some code on the site that looks quite funny:

public function getTotal($tax)
{
    $total = 0.00;

    $callback =
        /* This line here: */
        function ($quantity, $product) use ($tax, &$total)
        {
            $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                strtoupper($product));
            $total += ($pricePerItem * $quantity) * ($tax + 1.0);
        };

    array_walk($this->products, $callback);
    return round($total, 2);;
}

as on of the examples on : http://www.php.net/manual/en/functions.anonymous.php

Does anybody know about this ? Any documentation? And it looks evil, Should it ever be used?

+8  A: 

This is how PHP expresses a closure. This is not evil at all and in fact it is quite powerful and useful.

Basically what this means is that you are allowing the anonymous function to "capture" local variables (in this case, $tax and a reference to $total) outside of it scope and preserve their values (or in the case of $total the reference to $total itself) as state within the anonymous function itself.

Andrew Hare
So it's ONLY used for closures?Thanks for you explanation, I did not know the difference between anonymous function and a closure
SeanDowney
+3  A: 

closures are beautiful! they solve a lot of problems that come with anonymous functions, and make really elegant code possible (at least as long as we talk about php).

javascript programmers use closures all the time, sometimes even without knowing it, because bound variables aren't explicitly defined - that's what "use" is for in php.

there are better real-world examples than the above one. lets say you have to sort an multidimensional array by a sub-value, but the key changes.

<?php
 function generateComparisonFunctionForKey($key) {
  return function ($left, $right) use ($key) {
   if ($left[$key] == $right[$key])
    return 0;
   else
    return ($left[$key] < $right[$key]) ? -1 : 1;
  }
 }

 $myArray = array(
  array('name' => 'Alex', 'age' => 70),
  array('name' => 'Enrico', 'age' => 25)
 );

 $sortByName = generateComparisonFunctionForKey('name');
 $sortByAge  = generateComparisonFunctionForKey('age');

 usort($myArray, $sortByName);

 usort($myArray, $sortByAge);
?>

warning: untested code (i don't have php5.3 installed atm), but it should look like something like that.

there's one downside: a lot of php developers may be a bit helpless if you confront them with closures.

to understand the nice-ty of closures more, i'll give you another example - this time in javascript. one of the problems is the scoping and the browser inherent asynchronity. especially, if it comes to window.setTimeout(); (or -interval). so, you pass a function to setTimeout, but you can't really give any parameters, because providing parameters executes the code!

function getFunctionTextInASecond(value) {
    return function () {
        document.getElementsByName('body')[0].innerHTML = value; // "value" is the bound variable!
    }
}

var textToDisplay = prompt('text to show in a second', 'foo bar');

// this returns a function that sets the bodys innerHTML to the prompted value
var myFunction = getFunctionTextInASecond(textToDisplay);

window.setTimeout(myFunction, 1000);

myFunction is a returns a function with a kind-of predefined parameter!

to be honest, i like php a lot more since 5.3 and anonymous functions/closures. namespaces may be more important, but they're a lot less sexy.

Schnalle
ohhhhhhhh, so the Uses is used to pass in *extra* variables, I thought it was some funny assignment. Thanks!
SeanDowney
be careful. parameters are used to pass values when the function is CALLED. closures are used to "pass" values when the function is DEFINED.
Schnalle
A: 

The code works except that you need to add semicolon after the closure:

function generateComparisonFunctionForKey($key) { return function ($left, $right) use ($key) { ... }; }

Michael