Anonymous Functions
What Is an Anonymous Function?
In simple terms: An anonymous function is a function without a name. Instead of declaring it once and calling it by name, you create it as a value — store it in a variable or pass it directly to another function. Technically: An anonymous function is created with the function keyword in an expression context. The result is an object of the built-in Closure class, which can be called like a function, passed as an argument, returned from functions, and stored in arrays. Anonymous functions can capture variables from the surrounding scope with a use clause.
$greet = function (string $name): string {
return "Hello, $name!";
};
Why Anonymous Functions Exist
Many PHP functions accept a callback — a function that tells them how to do part of their work:
array_map()— how to transform each elementarray_filter()— which elements to keepusort()— how to compare two elements- Event handlers, route handlers, and middleware in frameworks Declaring a separate named function for every small callback would clutter the code. An anonymous function can be written exactly where it is needed.
Syntax
$variable = function (parameters) use (captured variables): ReturnType {
// body
};
Note: When an anonymous function is assigned to a variable, the statement ends with a semicolon after the closing brace — it is an assignment, not a function declaration.
Example
<?php $greet = function (string $name): string { return "Hello, $name!"; }; echo $greet('Asha') . "\n"; echo $greet('Bala') . "\n"; var_dump($greet instanceof Closure);Output
Hello, Asha! Hello, Bala! bool(true)How It Works
- The
function (...) { ... }expression creates aClosureobject.- The object is stored in
$greet.$greet('Asha')calls it, just like a named function.instanceof Closureconfirms that anonymous functions are objects.
Anonymous Functions as Callbacks
The most common use is passing an anonymous function directly to another function.
Transforming With
array_map()<?php $prices = [100, 250, 499]; $withGst = array_map(function (float $price): float { return round($price * 1.18, 2); }, $prices); print_r($withGst);Output
Array ( [0] => 118 [1] => 295 [2] => 588.82 )
array_map()calls the anonymous function once for each price and collects the results in a new array.Filtering With
array_filter()<?php $orders = [ ['id' => 1, 'total' => 450, 'paid' => true], ['id' => 2, 'total' => 1800, 'paid' => false], ['id' => 3, 'total' => 2400, 'paid' => true], ]; $largePaidOrders = array_filter($orders, function (array $order): bool { return $order['paid'] && $order['total'] >= 1000; }); foreach ($largePaidOrders as $order) { echo "Order {$order['id']}: ₹{$order['total']}\n"; }Output
Order 3: ₹2400The callback returns
truefor elements to keep.array_filter()preserves the original keys.Sorting With
usort()<?php $students = [ ['name' => 'Kavin', 'score' => 82], ['name' => 'Anu', 'score' => 95], ['name' => 'Bala', 'score' => 82], ]; usort($students, function (array $a, array $b): int { return [$b['score'], $a['name']] <=> [$a['score'], $b['name']]; }); foreach ($students as $student) { echo "{$student['name']}: {$student['score']}\n"; }Output
Anu: 95 Bala: 82 Kavin: 82The comparison sorts by score (descending), then by name (ascending) for equal scores. See Spaceship Operator.
Capturing Variables With
useAn anonymous function has its own scope, like any function. It cannot see variables from the surrounding code unless you import them with
use.Without
use<?php $taxRate = 18; $addTax = function (float $amount): float { return $amount * (1 + $taxRate / 100); // $taxRate is undefined here }; echo $addTax(100);Output
PHP Warning: Undefined variable $taxRate in /path/script.php on line 5 100Inside the function,
$taxRatedoes not exist, so PHP treats it asnull(with a warning), and the tax becomes zero.With
use<?php $taxRate = 18; $addTax = function (float $amount) use ($taxRate): float { return $amount * (1 + $taxRate / 100); }; echo $addTax(100) . "\n";Output
118
use ($taxRate)copies the variable into the anonymous function. A function that captures variables from its surroundings is called a closure; closures are covered in more depth in Closures.
useCaptures the Value at Creation Time<?php $taxRate = 18; $addTax = function (float $amount) use ($taxRate): float { return $amount * (1 + $taxRate / 100); }; $taxRate = 28; // changed after the closure was created echo $addTax(100) . "\n";Output
118The closure still uses
18, becauseusecopied the value when the function was created. Later changes to$taxRatedo not affect it.Capturing by Reference:
use (&$variable)To share a variable with the closure (so changes go both ways), capture it by reference:
<?php $count = 0; $increment = function () use (&$count): void { $count++; }; $increment(); $increment(); $increment(); echo "Count: $count\n";Output
Count: 3With
&, the closure works on the original$count. Use this sparingly — returning values is usually clearer.
Anonymous Functions vs Arrow Functions
PHP 7.4 introduced arrow functions, a shorter syntax for anonymous functions that contain a single expression:
<?php $taxRate = 18; $addTax1 = function (float $amount) use ($taxRate): float { return $amount * (1 + $taxRate / 100); }; $addTax2 = fn(float $amount): float => $amount * (1 + $taxRate / 100); echo $addTax1(100) . "\n"; echo $addTax2(100) . "\n";Output
118 118
Anonymous Function Arrow Function Syntax function (...) use (...) { ... }fn(...) => expressionBody Any number of statements A single expression Capturing outer variables Explicit, with useAutomatic, by value Capturing by reference Possible with use (&$x)Not possible Use arrow functions for short callbacks and anonymous functions when the body needs several statements. See Arrow Functions.
Recursive Anonymous Functions
An anonymous function has no name to call itself with. To make it recursive, capture the variable that holds it by reference:
<?php $factorial = function (int $n) use (&$factorial): int { return $n <= 1 ? 1 : $n * $factorial($n - 1); }; echo $factorial(5) . "\n";Output
120The reference is needed because
$factorialis assigned only after the function is created; capturing by value would capturenull. For most recursion, a named function is simpler.
Immediately Invoked Function Expressions
An anonymous function can be created and called at once. This is sometimes used to compute a value with temporary variables that should not leak into the surrounding scope:
<?php $config = (function (): array { $environment = 'production'; $isProduction = $environment === 'production'; return [ 'debug' => !$isProduction, 'cache' => $isProduction, ]; })(); var_dump($config['debug'], $config['cache']); var_dump(isset($environment));Output
bool(false) bool(true) bool(false)
$environmentand$isProductionexist only inside the function, so they do not pollute the outer scope. Configuration files in some frameworks use a similar pattern.
Static Anonymous Functions
Inside a class, an anonymous function automatically has access to
$this. Declaring itstaticprevents this binding:$formatter = static function (float $amount): string { return '₹' . number_format($amount, 2); };A
staticclosure cannot use$this, which avoids accidentally keeping the surrounding object alive in memory and makes the function's independence explicit. This matters mainly in object-oriented code (see Closures).
Practical Example: A Product Filter
<?php $products = [ ['name' => 'Desk Lamp', 'category' => 'Home', 'price' => 1299], ['name' => 'Notebook', 'category' => 'Stationery', 'price' => 60], ['name' => 'Wall Clock', 'category' => 'Home', 'price' => 899], ['name' => 'Gel Pen', 'category' => 'Stationery', 'price' => 25], ['name' => 'Cushion', 'category' => 'Home', 'price' => 499], ]; // Values that would normally come from the user's filter form $category = 'Home'; $maxPrice = 1000; $matches = array_filter($products, function (array $product) use ($category, $maxPrice): bool { return $product['category'] === $category && $product['price'] <= $maxPrice; }); usort($matches, function (array $a, array $b): int { return $a['price'] <=> $b['price']; }); $labels = array_map(function (array $product): string { return $product['name'] . ' — ₹' . number_format($product['price']); }, $matches); echo implode("\n", $labels) . "\n";Output
Cushion — ₹499 Wall Clock — ₹899Step-by-Step Explanation
- Filter: the first anonymous function captures
$categoryand$maxPricewithuseand keeps only matching products. The Desk Lamp is excluded because it costs more than ₹1000.- Sort: the second function orders the matches by price;
usort()also re-indexes the array.- Map: the third function turns each product into a display label.
- Each step is a small, focused function written exactly where it is used.
Common Mistakes
Forgetting the Semicolon After an Assigned Function
$fn = function () { return 1; } // parse error: missing ;Fix: End the assignment with
};.Forgetting
useEffect: "Undefined variable" warnings and wrong results inside the function. Fix: Import needed variables with
use, or use an arrow function.Expecting
useto See Later ChangesEffect: The closure keeps the value from when it was created. Fix: Pass the value as an argument, or capture by reference if sharing is truly intended.
Writing Long Logic Inline
Effect: Large anonymous functions inside
array_map()orusort()calls are hard to read and impossible to reuse. Fix: Move long logic into a named function or method, and pass that instead.
Best Practices
- Use anonymous functions for short callbacks that are used in one place.
- Import only the variables the function needs, by value.
- Use arrow functions for single-expression callbacks.
- Declare parameter and return types on anonymous functions as well.
- Use
staticclosures in classes when$thisis not needed. Official reference: https://www.php.net/manual/en/functions.anonymous.php