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
$greet = function (string $name): string {
return "Hello, $name!";
};
echo $greet("Asha") . "\n";
echo $greet("Liam") . "\n";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
$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";
}Water Bottle: 12
Ceramic Mug Set: 18.75
Mechanical Keyboard: 89.5usort() 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
$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";118
105.61use captures a copy of the value at the moment the closure is created. Changing the outside variable later does not affect it:
<?php
$rate = 0.18;
$withTax = function (float $price) use ($rate): float {
return $price * (1 + $rate);
};
$rate = 0.05;
echo $withTax(100) . "\n";118If 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
$count = 0;
$increment = function () use (&$count): void {
$count++;
};
$increment();
$increment();
echo "Count: $count\n";Count: 2Arrow 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
$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";118
20, 40, 60No 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
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));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
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";180
100Each 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
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";89.5, 24.99, 199.99Key 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(...).