Closures and the use Keyword
Anonymous functions that capture variables from the surrounding scope with use — by value, or by reference for shared mutable state.
A closure is an anonymous function you can store in a variable, pass around, and return. Unlike a named function, it can close over variables from where it was defined — but you must list them explicitly with use.
By value vs by reference
use ($x) captures a snapshot of $x at definition time. use (&$x) captures a reference, so changes inside and outside the closure stay in sync.
<?php
$greeting = 'Hi';
$sayHi = function(string $name) use ($greeting): string {
return "$greeting, $name!";
};
echo $sayHi('Sam'); // Hi, Sam!You can also rebind a closure's $this with Closure::bind(), which is how some frameworks add methods to objects on the fly.
Example
When to use it
- Capture a $taxRate variable from the outer scope with use to create a reusable price-formatting closure.
- Pass a closure to usort() that captures an outer $sortField to sort records by a runtime-chosen column.
- Store an event handler as a closure in an array and invoke it later when the event fires.
More examples
Closure capturing with use
use($taxRate) copies $taxRate into the closure's scope; changing $taxRate outside does not affect the closure.
<?php
$taxRate = 0.08;
$applyTax = function (float $price) use ($taxRate): float {
return round($price * (1 + $taxRate), 2);
};
echo $applyTax(100.00); // 108.0
echo $applyTax(49.99); // 53.99Capture by reference with use &
use (&$log) captures by reference so each call appends to the same $log array in the outer scope.
<?php
$log = [];
$logger = function (string $message) use (&$log): void {
$log[] = date('H:i:s') . " $message";
};
$logger('Start');
$logger('Processing');
$logger('Done');
print_r($log);Closure bound to an object
Closure::bind() attaches a closure to an object so $this refers to that object, enabling white-box testing of private state.
<?php
class Counter {
private int $count = 0;
}
$increment = Closure::bind(
function (int $by = 1): void { $this->count += $by; },
new Counter(),
Counter::class
);
$increment();
$increment(5);
$get = Closure::bind(
function (): int { return $this->count; },
(function () use ($increment) {
// grab the bound object
return null;
})(),
Counter::class
);
// Simpler demo:
$c = new Counter();
$fn = Closure::bind(fn() => $this->count, $c, Counter::class);
echo $fn(); // 0
Discussion