PHPClosures & use()

Closures & use()

A closure is an anonymous function that has "closed over" some variables from the scope where it was created, keeping access to them even after that outer scope has finished running. PHP does not do this automatically for regular anonymous functions — you have to opt in with a use() clause, listing exactly which outer variables the function should capture. That explicitness is a deliberate design choice: it makes it obvious, just by reading the function signature, which pieces of outer state a callback depends on.

Capturing by value with use ($var)

By default, use ($var) captures a copy of the variable's value at the moment the closure is created. Anything that happens to the original variable afterwards has no effect on the copy sitting inside the closure.

By-value capture: the closure freezes the value

PHP
<?php
$x = 10;

$printX = function () use ($x) {
    echo "Inside closure: {$x}\n";
};

$x = 99; // changed AFTER the closure was created

$printX();
echo "Outside closure: {$x}\n";
Inside closure: 10
Outside closure: 99

The closure printed 10, the value $x held when use ($x) ran — not 99, the value it holds now. The capture happened once, at creation time, and was never updated afterward.

Capturing by reference with use (&$var)

Prefixing the variable with & inside use() captures it by reference instead. The closure and the outer scope then share the exact same storage location, so a change on either side is visible on both.

By-reference capture: the closure sees later changes

PHP
<?php
$x = 10;

$printX = function () use (&$x) {
    echo "Inside closure: {$x}\n";
};

$x = 99; // changed AFTER the closure was created

$printX();
echo "Outside closure: {$x}\n";
Inside closure: 99
Outside closure: 99

Same code shape, one character different (&$x instead of $x), and the result flips completely. This is the single most important distinction to internalize about closures in PHP.

A closure factory: the counter generator

A common and genuinely useful pattern is a function that returns a closure, where that closure keeps its own private, persistent state via a by-reference capture. Each call to the outer function produces an independent counter.

A function that manufactures independent counters

PHP
<?php
function makeCounter() {
    $count = 0;

    return function () use (&$count) {
        $count++;
        return $count;
    };
}

$counterA = makeCounter();
$counterB = makeCounter();

echo $counterA(), "\n";
echo $counterA(), "\n";
echo $counterA(), "\n";
echo $counterB(), "\n"; // independent from $counterA
1
2
3
1

Each call to makeCounter() creates a brand new $count variable that only the returned closure can see and modify. $counterA and $counterB never interfere with each other because they closed over two separate $count instances.

A multiplier generator

The same shape works for building specialized functions out of a general one — a common technique sometimes called "partial application." Here, makeMultiplier bakes a fixed factor into a fresh closure each time it's called.

Generating specialized functions

PHP
<?php
function makeMultiplier($factor) {
    return function ($value) use ($factor) {
        return $value * $factor;
    };
}

$double = makeMultiplier(2);
$triple = makeMultiplier(3);

echo $double(21), "\n";
echo $triple(21), "\n";
42
63
Binding $this into a closure

Closures created inside an instance method automatically bind $this to that instance, but closures created outside a class — or detached ones — do not have a $this at all. Closure::bind() (a static method) and ->bindTo() (an instance method) let you attach or re-attach an object context to an existing closure, which is occasionally handy for building small dependency-free plugins or DSL-style helpers.

Attaching an object context with bindTo()

PHP
<?php
class Cart {
    private array $items = ['pen', 'notebook'];
}

$describe = function () {
    return 'Items: ' . implode(', ', $this->items);
};

$bound = Closure::bind($describe, new Cart(), Cart::class);

echo $bound();
Items: pen, notebook

Notice the closure could reach $this->items even though $items is private — the third argument to Closure::bind() tells PHP which class's scope to evaluate visibility rules against, letting the bound closure see private and protected members as if it were defined inside that class.

Reference capture keeps the variable alive and shared
A by-reference capture is powerful but can surprise you in loops. If you capture a loop variable by reference inside a closure stored in an array, every closure ends up sharing the *same* reference, which usually means they all report the loop's final value instead of the value at the time each closure was created.

A classic loop-and-reference-capture pitfall

PHP
<?php
$callbacks = [];

for ($i = 1; $i <= 3; $i++) {
    $callbacks[] = function () use (&$i) {
        return $i;
    };
}

foreach ($callbacks as $callback) {
    echo $callback(), "\n"; // NOT 1, 2, 3
}
4
4
4

All three closures share the same $i, which the for loop leaves at 4 after it exits. Capturing use ($i) by value instead would have frozen each closure's own copy at the moment it was created, producing 1, 2, 3 as most people expect.

  • use ($var) copies the value at closure-creation time; later changes to the outer variable are invisible to the closure.

  • use (&$var) shares the same storage location; changes on either side are visible on both.

  • A closure returned from a factory function keeps its captured variables alive for as long as the closure itself exists — this is how counter and multiplier generators work.

  • Closure::bind() and ->bindTo() attach an object (and optionally a class scope) to a closure that did not originally have $this.

Tip
Default to `use ($var)` (by value) unless you have a specific reason to share mutable state, such as a running total or a counter. By- reference capture is easy to reach for but easy to misuse, especially inside loops — reserve it for cases where sharing state is exactly the point.