480

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 one of the examples on anonymous functions.

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

Andrey Belykh
  • 2,578
  • 4
  • 32
  • 46
SeanDowney
  • 17,368
  • 20
  • 81
  • 90

6 Answers6

559

A simpler answer.

function ($quantity) use ($tax, &$total) { .. };

  1. The closure is a function assigned to a variable, so you can pass it around
  2. A closure is a separate namespace, normally, you can not access variables defined outside of this namespace. There comes the use keyword:
  3. use allows you to access (use) the succeeding variables inside the closure.
  4. use is early binding. That means the variable values are COPIED upon DEFINING the closure. So modifying $tax inside the closure has no external effect, unless it is a pointer, like an object is.
  5. You can pass in variables as pointers like in case of &$total. This way, modifying the value of $total DOES HAVE an external effect, the original variable's value changes.
  6. Variables defined inside the closure are not accessible from outside the closure either.
  7. Closures and functions have the same speed. Yes, you can use them all over your scripts.

As @Mytskine pointed out probably the best in-depth explanation is the RFC for closures. (Upvote him for this.)

Community
  • 1
  • 1
zupa
  • 12,809
  • 5
  • 40
  • 39
  • 4
    The as keyword in the use statement is giving me a syntax error in php 5.5: `$closure = function ($value) use ($localVar as $alias) { //stuff};` Error given is: `Parse: syntax error, unexpected 'as' (T_AS), expecting ',' or ')'` – Kal Zekdor Jul 29 '14 at 20:33
  • 1
    @KalZekdor, confirmed with php5.3 as well, seems deprecated. I updated the answer, thanks for you effort. – zupa Jul 30 '14 at 07:52
  • 4
    I would add to point #5 that this way, modifying the value a pointer like `&$total` has an internal effect as well. In other words, if you change the value of `$total` *outside* of the closure *after* it is defined, the new value only gets passed in if it is a pointer. – But those new buttons though.. Nov 19 '15 at 13:10
  • 2
    This line stopped my two hours vain search `You can pass in variables as pointers like in case of &$total. This way, modifying the value of $total DOES HAVE an external effect, the original variable's value changes.` – BlackPearl Apr 10 '18 at 02:29
  • Please scroll down and check out this answer: https://stackoverflow.com/a/30547499/529187 – Devs love ZenUML Jan 13 '22 at 08:50
400

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
  • 344,730
  • 71
  • 640
  • 635
  • 1
    So it's ONLY used for closures? Thanks for you explanation, I did not know the difference between anonymous function and a closure – SeanDowney Jun 30 '09 at 19:01
  • 153
    The `use` keyword is also used for [aliasing namespaces](http://php.net/manual/en/language.namespaces.importing.php). It's amazing that, more than 3 years after the release of PHP 5.3.0, the syntax `function ... use` is still officially undocumented, which makes closures an undocumented feature. The doc even [confuses anonymous functions and closures](http://php.net/manual/en/functions.anonymous.php). The only (beta and unofficial) documentation on `use ()` I could find on php.net was the [RFC for closures](https://wiki.php.net/rfc/closures). –  Sep 03 '12 at 21:49
  • 2
    So [When was function use closures implemented in PHP?](http://stackoverflow.com/questions/20411631/when-was-function-use-closures-implemented-in-php) I guess then it was in PHP 5.3? Is it documented in the PHP manual now somehow? – rubo77 Dec 05 '13 at 22:00
  • @Mytskine Well, according to the doc, anonymous functions use the Closure class – Manny Fleurmond Nov 16 '16 at 02:24
  • See also: https://github.com/php/php-langspec/blob/master/spec/10-expressions.md#anonymous-function-creation – Mikko Rantalainen Mar 26 '18 at 13:12
  • 1
    Now `use` is also used for including a `trait` into a `class`! – CJ Dennis Oct 30 '19 at 06:03
141

The function () use () {} is like closure for PHP.

Without use, function cannot access parent scope variable

$s = "hello";
$f = function () {
    echo $s;
};

$f(); // Notice: Undefined variable: s
$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$f(); // hello

The use variable's value is from when the function is defined, not when called

$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$s = "how are you?";
$f(); // hello

use variable by-reference with &

$s = "hello";
$f = function () use (&$s) {
    echo $s;
};

$s = "how are you?";
$f(); // how are you?
Steely Wing
  • 16,239
  • 8
  • 58
  • 54
57

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 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.

Timm
  • 2,488
  • 2
  • 22
  • 25
stefs
  • 18,341
  • 6
  • 40
  • 47
  • 5
    ohhhhhhhh, so the Uses is used to pass in *extra* variables, I thought it was some funny assignment. Thanks! – SeanDowney Jul 01 '09 at 20:31
  • 43
    be careful. parameters are used to pass values when the function is CALLED. closures are used to "pass" values when the function is DEFINED. – stefs Jul 02 '09 at 06:19
  • 1
    In Javascript, one can use [bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) to specify initial arguments to functions - see [Partially applied functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Partially_applied_functions). – Sᴀᴍ Onᴇᴌᴀ Aug 18 '17 at 22:11
19

Zupa did a great job explaining closures with 'use' and the difference between EarlyBinding and Referencing the variables that are 'used'.

So I made a code example with early binding of a variable (= copying):

<?php

$a = 1;
$b = 2;

$closureExampleEarlyBinding = function() use ($a, $b){
    $a++;
    $b++;
    echo "Inside \$closureExampleEarlyBinding() \$a = ".$a."<br />";
    echo "Inside \$closureExampleEarlyBinding() \$b = ".$b."<br />";    
};

echo "Before executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "Before executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";  

$closureExampleEarlyBinding();

echo "After executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "After executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";

/* this will output:
Before executing $closureExampleEarlyBinding() $a = 1
Before executing $closureExampleEarlyBinding() $b = 2
Inside $closureExampleEarlyBinding() $a = 2
Inside $closureExampleEarlyBinding() $b = 3
After executing $closureExampleEarlyBinding() $a = 1
After executing $closureExampleEarlyBinding() $b = 2
*/

?>

Example with referencing a variable (notice the '&' character before variable);

<?php

$a = 1;
$b = 2;

$closureExampleReferencing = function() use (&$a, &$b){
    $a++;
    $b++;
    echo "Inside \$closureExampleReferencing() \$a = ".$a."<br />";
    echo "Inside \$closureExampleReferencing() \$b = ".$b."<br />"; 
};

echo "Before executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "Before executing \$closureExampleReferencing() \$b = ".$b."<br />";   

$closureExampleReferencing();

echo "After executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "After executing \$closureExampleReferencing() \$b = ".$b."<br />";    

/* this will output:
Before executing $closureExampleReferencing() $a = 1
Before executing $closureExampleReferencing() $b = 2
Inside $closureExampleReferencing() $a = 2
Inside $closureExampleReferencing() $b = 3
After executing $closureExampleReferencing() $a = 2
After executing $closureExampleReferencing() $b = 3
*/

?>
joronimo
  • 563
  • 5
  • 8
3

Until very recent years, PHP has defined its AST and PHP interpreter has isolated the parser from the evaluation part. During the time when the closure is introduced, PHP's parser is highly coupled with the evaluation.

Therefore when the closure was firstly introduced to PHP, the interpreter has no method to know which which variables will be used in the closure, because it is not parsed yet. So user has to pleased the zend engine by explicit import, doing the homework that zend should do.

This is the so-called simple way in PHP.

Zhu Jinxuan
  • 406
  • 2
  • 8