If a closure needs to change variables from the surrounding function, you have to list each one in use() with &. Forget one, and the closure quietly gets its own copy instead. This RFC from Bob Weinand and Volker Dusch adds scope functions, which are closures that share every variable with the function that defines them.
How it looks
The syntax reuses fn, but with a { } block instead of =>:
function example() { $x = 1; (fn() { $x++; $new = 'hi'; })(); var_dump($x, $new); // int(2), string(2) "hi" }
Reads, writes and new variables all land in the parent function. Here's the RFC's sorting example, before and after:
// Today usort($items, function ($a, $b) use ($priorities, &$comparisons) { $comparisons++; return $priorities->of($a) <=> $priorities->of($b); }); // With scope functions usort($items, fn($a, $b) { $comparisons++; return $priorities->of($a) <=> $priorities->of($b); });
The RFC also shows the same pattern with array_filter(), array_walk(), database transaction wrappers and async callbacks.
In other ways, a scope function acts like a normal closure. A return only leaves the scope function, not the parent, exceptions bubble up as usual, and $this is the same inside and out.
The rules
Because it borrows the parent's variables, a scope function can't outlive the parent, and returning one from a function throws an Error. A few other things aren't allowed either:
static fn() {}is an error.use()is a parse error, since every variable is already shared.- A scope function can't call itself.
- You can't
cloneone. Closure::bind()can change the class scope, but not$this.
When the same scope function line runs again, the new closure replaces the older one. In a loop, that means only the last closure still works, and calling an earlier one throws an Error. A scope function at the top level of an included file gets cleaned up when that file finishes.
The RFC also adds a ReflectionFunctionAbstract::isScopeFunction() method.
What it means for existing code
Nothing breaks, because fn(...) { ... } is a parse error today.
Where it stands
The RFC is under discussion, targets PHP 8.6 and has a draft pull request. The plan is a Yes/No vote that needs a two-thirds majority, but no vote has opened yet.