In order to use variables from the scope that the anonymous function came from, you have to explicitly import them into the anonymous function with the use[1] block:
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);
}
In other languages that have true closures, the anonymous function "closes over" the lexical scope in which it was declared.
I for one don't find it particularly shocking. While I agree it's not common, having things more explicit in PHP rather than less is not necessarily a bad thing. Of course, this being PHP, this is probably by accident. I've been bitten in the past in Perl and Javascript by thoughtless capture of variables from the outer scope.
Wrong. You only need to use the free variables of the closure, you don't need to copy the entire environment from the function you're defining your closure in. By the definition of a free variable you actually use it, otherwise it wouldn't be a free variable of the function! Python definitely does not work the same way, but in Python "x = 3" can inadvertently shadow (make a local name) something defined in the outer scope, but that's a completely different problem.
In modern versions of Python you can declare "nonlocal x", but that's only necessary if you want to assign to x. Since assignment in Python creates a local variable by default, there needs to be a way to tell the byte compiler not to shadow the nonlocal variable.
If you don't assign to x, the compiler will do the right thing without help.
It is an algorithm used in compiling languages like Scheme where you calculate the set of free variables in a function and then create a closure (a function pointer and an environment, which is basically a way to lookup free variables). PHP requires you to do this by hand with the "use" keyword, e.g. use($a, $b, $c) where $a, $b, and $c are free variables (that is they are not bound in the current scope). I see no reason why PHP couldn't be doing this for you other than that the devs are lazy.
AFAIK, `use` were required because PHP has variable-variables:
<?php
$x = "b";
$b = 42;
echo $$x; // equivalent to echo $b;
// Here the compiler can't determine all the variables
// used in the function.
// Has a result, there were two choices:
// - capture the whole environment
// - capture explicitly named variables
function () {
return $$x;
}
Hopefully, nobody uses variable-variables these days, however this is the reason behind the introduction of `use` for closures.
Yep that occurred to me after I posted this, but I believe it should be possible to exclude variable variables from this at the cost of a runtime error if you try to use them in a closure. Someone else pointed out to me that PHP has other ways of messing with variables as well and that it would require full evaluation to determine all of them, so maybe it "makes sense", but only because of the extremely dynamic nature of variables in PHP.
Can you give an example? I googled "closure conversion" but didn't understand.