Lesson 67 of 100
Functions15 min5 checksPHP 8.5

Capture Values in Closures

Learn how a PHP closure's use clause copies a variable by value or shares it by reference, and test the difference in a single call.

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.

Example
<?php
$message = 'hello';
$show = function () use ($message): string {
    return $message;
};
$message = 'world';
echo $show(), "\n";
echo $message, "\n";
Output
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.

Example
<?php
$count = 0;
$increment = function () use (&$count): void {
    $count++;
};
$increment();
$increment();
echo $count, "\n";
Output
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.

Example
<?php
$total = 10;
$addFive = function () use ($total): int {
    $total += 5;
    return $total;
};
echo json_encode([$addFive(), $addFive(), $total]), "\n";
Output
[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 $this or superglobals through use.

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:

  1. the outer by-value variable after all calls,
  2. the outer by-reference variable after all calls,
  3. the value returned by the by-value closure on its last call, or $start if $amounts is empty.

The tests compare the returned list, so do not print anything.

Before you start

Functions · Arrays · Types

You’ll practice
  • Distinguish use-by-value from use-by-reference within a single invocation
  • Implement and test a deterministic PHP 8.4 function
Your task

Make captureTotals() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.