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 useglobal
, 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
Is There a Function to Extract a 'Column' from an Array in PHP
How to Read a Large File Line by Line
Detect Encoding and Make Everything Utf-8
Property [Title] Does Not Exist on This Collection Instance
Call to a Member Function Bind_Param() on a Non-Object
How to Generate in PHP All Combinations of Items in Multiple Arrays
How to Remove Multiple Utf-8 Bom Sequences
How to Convert a Pdf Document to a Preview Image in PHP
Difference Between Public, Private, and Protected
Why Would $_Files Be Empty When Uploading Files to PHP
Send Email With PHP from HTML Form on Submit With the Same Script
Laravel 5 - Remove Public from Url
Use an Array in a MySQLi Prepared Statement: 'Where .. In(..)' Query
Gcm With PHP (Google Cloud Messaging)
Detecting Request Type in PHP (Get, Post, Put or Delete)
MySQL Connection Not Working: 2002 No Such File or Directory