Himanshu SharmaSoftware engineer
Let’s talk

PHP · Lesson 21 of 54

Closures and arrow functions

Store functions in variables, pass them to other functions as callbacks, capture outside values with use, and write short arrow functions with fn.

12 minFunctionsUpdated 22 October 2026

Awaiting technical review. This lesson is readable but not yet listed in search engines.

By the end of this lessonWrite anonymous functions and arrow functions and pass them as callbacks to functions such as usort and array_map.

Functions without names

The functions in the previous lessons all had names. PHP also lets you create a function without a name and store it in a variable, exactly like a number or a string. It is called an anonymous function:

php
<?php
$greet = function (string $name): string {
    return "Hello, $name!";
};

echo $greet("Asha") . "\n";
echo $greet("Liam") . "\n";
result
Hello, Asha!
Hello, Liam!

Note the semicolon after the closing brace: the whole thing is an assignment statement. You call the function through the variable, $greet(...).

Passing functions to functions

The main reason to write anonymous functions is to hand them to other functions. A function that you pass in, and that the receiver calls for you, is called a callback. Sorting is the classic example: usort() knows how to sort, but you tell it how to compare two elements:

php
<?php
$products = [
    ["name" => "Water Bottle", "price" => 12.0],
    ["name" => "Mechanical Keyboard", "price" => 89.5],
    ["name" => "Ceramic Mug Set", "price" => 18.75],
];

usort($products, function (array $a, array $b): int {
    return $a["price"] <=> $b["price"];
});

foreach ($products as $product) {
    echo "{$product['name']}: {$product['price']}\n";
}
result
Water Bottle: 12
Ceramic Mug Set: 18.75
Mechanical Keyboard: 89.5

usort() calls the callback many times, each time with two products, and uses the answer from the spaceship operator to decide their order. The Sorting arrays and array_map, array_filter and array_reduce lessons rely on callbacks throughout.

Closures and use

An anonymous function has its own scope, like any function, so it cannot see the variables around it. To bring an outside value in, list it after use. A function that captures variables this way is called a closure:

php
<?php
$rate = 0.18;

$withTax = function (float $price) use ($rate): float {
    return round($price * (1 + $rate), 2);
};

echo $withTax(100) . "\n";
echo $withTax(89.5) . "\n";
result
118
105.61

use captures a copy of the value at the moment the closure is created. Changing the outside variable later does not affect it:

php
<?php
$rate = 0.18;
$withTax = function (float $price) use ($rate): float {
    return $price * (1 + $rate);
};

$rate = 0.05;
echo $withTax(100) . "\n";
result
118

If you really want the closure to see later changes, or to change the outside variable, capture it by reference with use (&$rate). Use that sparingly, for the same reasons as references elsewhere.

php
<?php
$count = 0;
$increment = function () use (&$count): void {
    $count++;
};

$increment();
$increment();
echo "Count: $count\n";
result
Count: 2

Arrow functions

Many callbacks are a single expression. Arrow functions, added in PHP 7.4, are a short form for those. They start with fn, return the expression after => automatically, and capture any outside variables they use automatically, by value:

php
<?php
$rate = 0.18;
$withTax = fn(float $price): float => round($price * (1 + $rate), 2);

echo $withTax(100) . "\n";

$prices = [10, 20, 30];
$doubled = array_map(fn($p) => $p * 2, $prices);
echo implode(", ", $doubled) . "\n";
result
118
20, 40, 60

No use, no return, no braces. The limits: the body must be one expression, and arrow functions cannot change outside variables. For anything longer, use a normal anonymous function.

Other kinds of callables

PHP accepts several things wherever a callback is expected. The type for them is callable:

php
<?php
function shout(string $text): string
{
    return strtoupper($text) . "!";
}

$words = ["sale", "today"];

print_r(array_map('shout', $words));
print_r(array_map(shout(...), $words));
print_r(array_map(strlen(...), $words));
result
Array
(
    [0] => SALE!
    [1] => TODAY!
)
Array
(
    [0] => SALE!
    [1] => TODAY!
)
Array
(
    [0] => 4
    [1] => 5
)

A function name as a string, 'shout', is the old way. First-class callable syntax, shout(...) and strlen(...), added in PHP 8.1, creates a closure from any function and is checked by editors, so typos are caught early.

Functions that return functions

Because functions are values, a function can build and return one. This is a neat way to make families of similar functions:

php
<?php
function discount(int $percent): Closure
{
    return fn(float $price): float => $price * (100 - $percent) / 100;
}

$tenOff = discount(10);
$halfPrice = discount(50);

echo $tenOff(200) . "\n";
echo $halfPrice(200) . "\n";
result
180
100

Each returned arrow function remembers its own $percent. Anonymous functions are objects of the built-in class Closure, which is why that is the return type.

Common mistakes

  • Forgetting the semicolon after an anonymous function assigned to a variable.
  • Forgetting use, so the closure reads an undefined variable.
  • Expecting a closure to see later changes to a variable captured by value.
  • Writing several statements in an arrow function. Use function () { ... }.

Exercise

Write a function minimumPrice(float $min): Closure that returns an arrow function for array_filter(), keeping only prices of at least $min. Use it to keep the prices of 20 or more from [12.0, 89.5, 18.75, 24.99, 199.99].

Show one possible answer
php
<?php
function minimumPrice(float $min): Closure
{
    return fn(float $price): bool => $price >= $min;
}

$prices = [12.0, 89.5, 18.75, 24.99, 199.99];
$expensive = array_filter($prices, minimumPrice(20));

echo implode(", ", $expensive) . "\n";
result
89.5, 24.99, 199.99

Key points

  • An anonymous function has no name and can be stored in a variable or passed as an argument.
  • A closure captures outside variables with use ($var); by default it captures a copy of the value at that moment.
  • Arrow functions, fn($x) => $x * 2, return one expression and capture outside variables automatically.
  • A callable can be a closure, a function name as a string, or first-class callable syntax such as strlen(...).

Check yourself

1. What does use ($rate) do in function ($price) use ($rate) { ... }?
2. What does this print? $n = 1; $f = fn() => $n; $n = 2; echo $f();
3. Can an arrow function contain several statements?
4. What is a callback?