In PHP, What Is a Closure and Why Does It Use the "Use" Identifier

In PHP, what is a closure and why does it use the use identifier?

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.

Confusion with 'use' identifier in PHP closures

$apples will take on the value that is passed to the function when it is called, e.g.

function my_method($callback) {
// inside the callback, $apples will have the value "foo"
$callback('foo');
}

$oranges will refer to the value of the variable $oranges which exists in the scope where you defined the closure. E.g.:

$oranges = 'bar';

my_method(function($apples) use ($oranges) {
// $oranges will be "bar"
// $apples will be "foo" (assuming the previous example)
});

The differences is that $oranges is bound when the function is defined and $apples is bound when the function is called.


Closures let you access variables defined outside of the function, but you have to explicitly tell PHP which variables should be accessible. This is similar (but not equivalent!) to using the global keyword if the variable is defined in global scope:

$oranges = 'bar';

my_method(function($apples) {
global $oranges;
// $oranges will be "bar"
// $apples will be "foo" (assuming the previous example)
});

The differences between using closures and global:

  • You can bind local variables to closures, global only works with global variables.
  • Closures bind the value of the variable at the time the closure was defined. Changes to the variables after the function was defined does not effect it.

    On the other hand, if you use global, you will receive the value the variable has at the moment when the function is called.

    Example:

    $foo = 'bar';
    $closure = function() use ($foo) {
    echo $foo;
    };
    $global = function() {
    global $foo;
    echo $foo;
    };

    $foo = 42;
    $closure(); // echos "bar"
    $global(); // echos 42

Closures in PHP... what, precisely, are they and when would you need to use them?

PHP will support closures natively in 5.3. A closure is good when you want a local function that's only used for some small, specific purpose. The RFC for closures gives a good example:

function replace_spaces ($text) {
$replacement = function ($matches) {
return str_replace ($matches[1], ' ', ' ').' ';
};
return preg_replace_callback ('/( +) /', $replacement, $text);
}

This lets you define the replacement function locally inside replace_spaces(), so that it's not:

1) cluttering up the global namespace

2) making people three years down the line wonder why there's a function defined globally that's only used inside one other function

It keeps things organized. Notice how the function itself has no name, it's simply defined and assigned as a reference to $replacement.

But remember, you have to wait for PHP 5.3 :)

What's the difference between closure parameters and the 'use' keyword?

A closure is a function that is evaluated in its own environment, which has one or more bound variables that can be accessed when the function is called. They come from the functional programming world, where there are a number of concepts in play. Closures are like lambda functions, but smarter in the sense that they have the ability to interact with variables from the outside environment of where the closure is defined.

The use() keyword let's you import variables from outside the function environment, inside the function. Variables to be imported from the outside environment are specified in the use clause of the closure function definition. By default, they are passed by value. So let's say the function has no parameters, but you wan't it to use a variable you already have.

$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

This is useful when you need to create a function what must be used as callback somewhere else, and can only have defined parameters. The use() keyword let's you use other variables in addition to the ones you pass as function arguements. For example on the php.net example: http://php.net/manual/en/functions.anonymous.php

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

$callback =
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);
}

$callback must only have two parameters, because array_walk will only allow that much:

Typically, funcname takes on two parameters. The array parameter's
value being the first, and the key/index second.

So what can we do? We call use() to add other variables that are not the $callback's scope, but in scope of the environment it is being called in.

Use keyword in functions - PHP

The use of "use" is correct in this case too.

With closures, to access variables that are outside of the context of the function you need to explicitly grant permission to the function using the use function. What it means in this case is that you're granting the function access to the $tax and $total variables.

You'll noticed that $tax was passed as a parameter of the getTotal function while $total was set just above the line where the closure is defined.

Another thing to point out is that $tax is passed as a copy while $total is passed by reference (by appending the & sign in front). Passing by reference allows the closure to modify the value of the variable. Any changes to the value of $tax in this case will only be effective within the closure while the real value of $total.

PHP closure in other closure : scope of use

Internally, each Closure object in PHP contains a hash table. This table stores the values that are copied into the closure's scope with the use keyword. Arrays in PHP are also implemented using a hash table.

When you use a set of variables in a closure, it is as though you've created an array containing each variable being used. Each closure contains it's own unique "array" of values that is initialized when it is created. Unlike a normal array, the table of variables used in a closure cannot be modified.

$var1 = 1;
$var2 = 2;

$closure = function () use ($var1, $var2) {
return $var1 . ", " . $var2 . "\n";
};

$array = [$var1, $var2];

$var1 = 3;
$var2 = 4;

echo $closure(); // echoes 1, 2
echo $array[0] . ", " . $array[1] . "\n"; // echoes 1, 2

Changing the value of $var1 will not affect the value at $array[0], nor would it change the value of $var1 within $closure.

When you use an object in a closure, that object may be changed outside of the closure and those changes will be reflected in the closure. Objects are not cloned when used in a closure. However, because you cannot modify the variable itself, you cannot change the variable to point at a different object.

Variables may also be used in a closure by reference. This allows a variable value to be modified outside of a closure and those changes to be reflected within the closure itself.

$var1 = 1;
$var2 = 2;

$closure = function () use (&$var1, $var2) {
return $var1 . ", " . $var2 . "\n";
};

$array = [&$var1, $var2];

$var1 = 3;
$var2 = 4;

echo $closure(); // echoes 3, 2
echo $array[0] . ", " . $array[1] . "\n"; // echoes 3, 2

When the closure above is created, a reference to $var1 is created in the closure's table of values, but only the value of $var2 is copied into the table. When the values of $var1 and $var2 were changed, only the value of $var1 was changed within the closure because only that variable was used by reference. This again is similar to creating an array where $var1 is added to the array by reference, but the value of $var2 is copied to the array.

When a closure is created within a closure, the internal closure is copying the value of the variables at the time the closure is created. It does not matter that it is being created within another closure.

$value = 1;

$closure = function ($arg) use ($value) {
return function () use ($arg, $value) {
return $value + $arg;
};
};

$value = 10;

$callback1 = $closure(1);
$callback2 = $closure(2);

echo $callback1() . "\n"; // Echoes 2
echo $callback2() . "\n"; // Echoes 3

TL;DR: The value of a variable is copied into a closure when the closure is created. To be able to modify the value outside of the closure, the value must be used by reference (e.g., function () use (&$value) { ... }).



Related Topics



Leave a reply



Submit