Closures and callables

PHP · Practical · 12 min

Callbacks are everywhere in PHP: array functions, routing, event listeners, collection pipelines.

An anonymous function is a value of class Closure:

$double = function (int $x): int { return $x * 2; };

It does not see outer variables automatically. List them with use, which copies the value at the moment the closure is created: function ($x) use ($rate) { ... }. Use use (&$total) to capture by reference when the closure must modify the outer variable.

Arrow functions (PHP 7.4) are short closures with a single expression: fn($x) => $x * $rate. They capture any outer variable they use, automatically and by value. They can't modify outer variables and can't contain statements.

A callable is anything PHP can call: a closure, a function name string 'strtoupper', an [$object, 'method'] or [ClassName::class, 'staticMethod'] array, or an object with __invoke. The first-class callable syntax (PHP 8.1) turns any of these into a proper Closure: strlen(...), $this->validate(...), Str::slug(...). It is checked by the engine and works with static analysis, unlike strings.

Closures created inside a class method automatically bind $this. Declare a closure static function or static fn when it doesn't need $this; that avoids keeping the object alive accidentally.

Example

<?php
$rate = 1.2;
$withTax = fn(float $p): float => round($p * $rate, 2);
echo $withTax(10), "\n";           // 12

$total = 0;
$add = function (int $n) use (&$total): void { $total += $n; };
array_map($add, [1, 2, 3]);
echo $total, "\n";                 // 6

$copy = function () use ($rate) { return $rate; };
$rate = 2.0;
echo $copy(), "\n";                // 1.2 (captured when created)

$len = strlen(...);                // first-class callable
print_r(array_map($len, ['a', 'bcd']));   // [1, 3]
echo get_class($len), "\n";        // Closure

Remember

  • Closures capture with use (by value) or use (&$x) (by reference)
  • fn() => captures automatically by value and holds one expression
  • strlen(...) and $obj->method(...) create real Closure objects

Watch out for

  • Expecting an arrow function to update an outer variable; it only gets a copy.

Check yourself

  1. $n = 1; $f = function () use ($n) { return $n; }; $n = 5; What does $f() return?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.php.net/manual/en/functions.anonymous.php