Anonymous Functions / Closures (PHP 5.3)
debt(d7/e1/b3/t7)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints list phpstan as the only tool, and automated is marked 'no'. A missing use() capture silently results in a null/undefined variable at runtime — phpstan can catch it in some cases but it's not a default lint rule and requires explicit type coverage. The bug often surfaces only during testing or in production when the closure executes.
Closest to 'one-line patch or single-call swap' (e1). The quick_fix is explicit: add use($var) to the closure signature, or upgrade to an arrow function. This is a single-line change at the point of the closure definition.
Closest to 'localised tax' (b3). Closures apply broadly (web, cli, queue-worker) but the burden of remembering explicit capture via use() is paid per closure, not across the whole codebase. It doesn't reshape architecture; it's a recurring local friction for developers writing closures.
Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception is direct: developers coming from JavaScript, Python, or Ruby expect automatic capture of outer scope variables. PHP 5.3 closures require explicit use() listing, which directly contradicts the behaviour of closures in almost every other mainstream language. This is a well-documented gotcha that reliably trips up experienced developers new to PHP.
TL;DR
Explanation
PHP 5.3 (2009) added: anonymous functions: $fn = function($x) { return $x * 2; }. Scope capture with use: function() use ($var) — captures by value. use (&$var) captures by reference. Closures are instances of the Closure class. PHP 7.4 added arrow functions: fn($x) => $x * 2 — automatically capture outer scope. Closures can be bound to different objects with Closure::bind(). In PHP 8.1, closures work with first-class callable syntax: strlen(...).
Common Misconception
Why It Matters
Common Mistakes
- Forgetting use() to capture outer variables — variable not available in closure.
- Using use(&$var) when use($var) suffices — reference capture causes subtle bugs.
- Not knowing arrow functions auto-capture — saves use() boilerplate.
Code Examples
$multiplier = 3;
$fn = function($x) {
return $x * $multiplier; // Error: $multiplier not defined (not captured)
};
$multiplier = 3;
// PHP 5.3+ — explicit capture:
$fn = function($x) use ($multiplier) {
return $x * $multiplier;
};
// PHP 7.4+ — auto-capture with arrow function:
$fn = fn($x) => $x * $multiplier;