01 · THE IDEAREAD → TRY → LEARN
Why closures need use
A normal PHP function cannot see the variables of the code that surrounds it. An anonymous function behaves the same way: inside its body, only its own parameters and local variables exist. If you want it to work with something from the parent scope, you must name that variable in a use clause. The parent scope is the function (or script) where the closure was created, not where it is later called.
There are two ways to list a variable, and they behave very differently.
Capture by value
use ($name) copies the value of $name at the moment the closure is defined. Later changes to the outer variable are invisible to the closure, and changes made to the copy inside the closure never reach the outside.
$message = 'hello';
$show = function () use ($message): string {
return $message;
};
$message = 'world';
echo $show(), "\n";
echo $message, "\n";hello world
The closure still returns hello because the snapshot was taken at definition time. Note that the return type goes after the use clause.
Capture by reference
use (&$name) binds the closure to the original variable instead of a copy. Both sides now see and modify the same storage, so the closure can update state in the parent scope.
$count = 0;
$increment = function () use (&$count): void {
$count++;
};
$increment();
$increment();
echo $count, "\n";2
This is the standard way to accumulate a result from a callback, for example while walking an array.
The copy is reset on every call
A subtle point: when you capture by value and then modify the variable inside the closure, the modification only lives for that one call. The next call starts again from the originally captured value.
$total = 10;
$addFive = function () use ($total): int {
$total += 5;
return $total;
};
echo json_encode([$addFive(), $addFive(), $total]), "\n";[15,15,10]
Both calls return 15, and the outer $total stays 10. If you expected 15 and then 20, you needed use (&$total).
Choosing between them
- Use by-value capture for configuration-like data: a tax rate, a prefix, a limit. It is predictable because nothing can change it behind your back.
- Use by-reference capture only when the closure is deliberately meant to write back to the outer scope, such as collecting a sum or a list.
- Capturing a variable is not the same as using a global. The closure remembers variables from the function where it was declared.
- A captured variable cannot share its name with one of the closure's parameters, and you cannot capture
$thisor superglobals throughuse.
Worked reasoning
Suppose $x is 1 and you create two closures, one with use ($x) and one with use (&$x). You then set $x = 7 and call both. The first still sees 1. The second sees 7. If each closure also tries $x++ internally, the first returns 2 on every call without touching the outer $x, while the second raises the outer $x to 8 and then 9 on later calls. Keeping this table in your head solves most closure-capture puzzles.
Exercise contract
Write captureTotals(int $start, array $amounts): array. Create two closures that each take an integer $n, add it to a captured variable that begins at $start, and return the new value. The first closure captures its variable by value, the second by reference. Call both closures once for every amount, in order. Return a list of three integers:
- the outer by-value variable after all calls,
- the outer by-reference variable after all calls,
- the value returned by the by-value closure on its last call, or
$startif$amountsis empty.
The tests compare the returned list, so do not print anything.
Functions · Arrays · Types
- Distinguish use-by-value from use-by-reference within a single invocation
- Implement and test a deterministic PHP 8.4 function