01 · THE IDEAREAD → TRY → LEARN
Why arrow functions exist
Many callbacks do one tiny job: double a number, test a threshold, or build a label. A full anonymous function needs the function keyword, a use list, braces and a return statement, so the ceremony can outweigh the logic. Arrow functions, available since PHP 7.4, shorten this. Their basic shape is fn (parameters) => expression.
An arrow function is still a Closure object, just like an anonymous function. You can pass it to array_map(), store it in a variable, or return it from another function.
One expression, implicit return
The right-hand side of => must be a single expression. Its value becomes the return value automatically, so you never write return. You also cannot put several statements inside. When a callback needs loops, multiple steps or temporary variables, use a regular anonymous function or a named function instead.
Parameters are as flexible as in other functions. You can add type declarations, a return type, default values, variadics and by-reference parameters. For example, fn(int $n): int => $n * 2 declares both an input and an output type.
Automatic capture by value
The feature that makes arrow functions pleasant is automatic capture. If the expression uses a variable that exists in the parent scope, PHP binds it for you. This works like writing use ($variable) by hand, with no list to maintain.
$y = 1;
$add = fn(int $x): int => $x + $y;
echo $add(3), "\n";4
Here $y is not a parameter, yet the function can read it. The value is bound when the arrow function is created, and the closure keeps its own copy.
Because the capture is by value, an arrow function cannot change variables in the outer scope. An increment inside the function affects only its private copy:
$count = 1;
$bump = fn() => $count++;
$bump();
echo $count, "\n";1
The outer $count stays 1. If you really need to modify an outer variable, use an anonymous function with use (&$variable). Often the better design is to return the new value and assign it yourself.
Nesting works too. An inner arrow function can use variables from the outer arrow function and from the scope around it, because capture happens at each level.
Worked example: adding tax to prices
Suppose prices are stored as whole numbers and you want to add a percentage rate held in a variable. The mapping function needs two things: the current price, which is its parameter, and the rate, which comes from outside. An arrow function covers both without ceremony.
$rate = 10;
$prices = [100, 250, 40];
$withTax = array_map(fn(int $p): int => intdiv($p * (100 + $rate), 100), $prices);
echo json_encode($withTax), "\n";[110,275,44]
Read it in three parts. array_map() calls the closure once per element. The parameter $p receives each price. The variable $rate is captured automatically from the surrounding code. Because array_map() returns a new array, the original $prices is untouched.
Combining map and filter
Two arrow functions often work together: one transforms values and one decides which results to keep. Remember that array_filter() preserves the original keys of the elements it keeps. After filtering a list, the keys may have gaps, and an array with gaps encodes to JSON as an object. Wrap the result in array_values() when you want a clean, zero-based list again.
Common pitfalls
- Expecting statements.
fn($x) => { ... }is not valid. Use one expression, or switch tofunction. - Expecting write access. Assigning to a captured variable changes only the closure's copy.
- Hiding complexity. If the expression needs a comment to be understood, a named function is usually clearer.
- Forgetting keys.
array_map()andarray_filter()keep keys in common cases, so reindex when order and numbering matter.
Your exercise
Write scaleAtLeast(array $numbers, int $factor, int $min): array. It must multiply every number by $factor, keep only the results that are greater than or equal to $min, and return a zero-based list in the original order. Use an arrow function for each step. The $factor and $min values should be captured from the function's scope rather than passed to the callbacks. The tests cover an empty list, a result exactly equal to the minimum, a negative factor, and a case where nothing survives the filter.
Recap
An arrow function is fn (args) => expression. It returns the expression's value, captures used outer variables by value, and cannot modify them. That makes it ideal for short array callbacks, and a poor fit for multi-step logic.
Closures · Arrays
- Write a single-expression arrow function
- Use captured values in an array transformation