Lesson 16 of 100
Functions12 min5 checksPHP 8.5

Arrow Functions for Small Transformations

Learn how PHP arrow functions turn one expression into a compact closure that captures outer variables by value, then use them to transform arrays.

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.

Example
<?php
$y = 1;
$add = fn(int $x): int => $x + $y;
echo $add(3), "\n";
Output
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:

Example
<?php
$count = 1;
$bump = fn() => $count++;
$bump();
echo $count, "\n";
Output
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.

Example
<?php
$rate = 10;
$prices = [100, 250, 40];
$withTax = array_map(fn(int $p): int => intdiv($p * (100 + $rate), 100), $prices);
echo json_encode($withTax), "\n";
Output
[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 to function.
  • 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() and array_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.

Before you start

Closures · Arrays

You’ll practice
  • Write a single-expression arrow function
  • Use captured values in an array transformation
Your task

Make scaleAtLeast() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.