PHP Arrow Functions

Write compact single-expression callbacks and understand by-value capture, objects and static closures.

Lesson 40 of 86Intermediate10 min read

A PHP arrow function writes a small anonymous function as fn($value) => expression. It returns that expression automatically. Arrow functions are useful for short transformations and predicates, especially with array operations. They became available in PHP 7.4 and are ordinary Closure objects, so you can store and pass them like other anonymous functions.

Their main convenience is automatic by-value capture of outside variables used in the expression. Their main limit is a single expression instead of a block of statements. This guide shows when that trade-off helps, what capture really means, and when an ordinary closure or named function communicates the work more clearly.

Basic syntax and automatic return

The keyword is fn, followed by a parameter list, an optional return type, =>, and the result expression. Unlike an ordinary anonymous function, there is no explicit return statement and no body surrounded by braces. The expression can span several source lines, but it still represents one expression.

Use a type declaration when it makes the contract clearer, such as fn(int $n): int => $n * 2. The same general parameter features are available, including defaults and variadics. Type declarations do not validate a complete business rule; an integer quantity can still be outside the permitted range.

Arrow syntax is well suited to a direct field extraction, a small numeric formula, or a boolean test. If the operation needs several named intermediate results, logging, loops or multiple early returns, an ordinary closure is normally easier to read. Do not hide a multi-step procedure inside a deeply nested expression just to retain fn syntax.

Capture records the surrounding value

An arrow function automatically captures the outer variables that its expression references. You do not write a use clause. For scalar variables, capture is by value at function creation, so reassigning the outer variable afterward does not update the closure's captured value.

For example, fn(int $price): int => $price - $discount captures the current discount. That can be useful for creating a formatter or calculation configured for one operation. If the discount is meant to vary on every call, make it a parameter instead. A parameter makes that changing dependency explicit.

An arrow function cannot request by-reference capture of an outer local using a use clause. Incrementing a captured scalar does not update the outer variable. However, reference parameters are a separate feature, and objects captured by value still refer to the same instances. Avoid claiming that arrow functions can never produce side effects; method calls and object mutation can still affect external state.

Object context and static arrow functions

An arrow function created in an object context can use $this, just like a bound nonstatic closure. That is convenient for short operations that intentionally depend on the object. It also means the closure can retain the object, which matters when callbacks are stored longer than the immediate operation.

Writing static fn(...) => ... creates an unbound arrow function without $this. Use it when the callback does not need the current object and you want that independence visible. Static does not disable automatic capture of other referenced local values, and it does not make the result immutable.

Keep object dependencies small and visible. A short arrow expression calling a large service method is still executing that method's work. In a sorting comparator, repeated service calls can become expensive. Compute needed data once before sorting rather than using elegant syntax around repeated database or network operations.

Composition and readability

Several arrow callbacks can form a pipeline, such as map, filter and sort. A pipeline is useful when each step has a distinct purpose and the intermediate shape stays obvious. Name intermediate arrays when that helps explain the transformation. One long nested call can force readers to understand everything from the inside out.

An arrow expression can contain a ternary or match expression, but complexity grows quickly. If a predicate handles several exceptional states, move it into a named function with explicit branches. The shorter source form is not automatically the clearer explanation of a domain rule.

The PHP arrow functions manual documents the PHP 7.4 introduction and automatic by-value capture. For projects supporting older versions, use an ordinary closure with a use clause. Avoid copying JavaScript arrow syntax directly: PHP uses fn and has its own capture and object-binding rules.

PHP Arrow Functions Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Transform and filter a list

The mapper returns a number, while the predicate returns a boolean. Array_filter preserves keys, so array_values deliberately creates a list for the retained values. The short functions are readable because each performs one clear calculation or test.

PHP
<?php
$numbers = [1, 2, 3, 4];
$doubled = array_map(fn(int $n): int => $n * 2, $numbers);
$large = array_values(array_filter($doubled, fn(int $n): bool => $n >= 6));
echo implode(',', $doubled) . PHP_EOL;
echo implode(',', $large) . PHP_EOL;
Output
2,4,6,8
6,8
Example 2

See a captured configuration value

The closure uses the captured two, while the outer variable now stores five. If you want each call to use a supplied discount, define fn(int $price, int $discount): int => $price - $discount instead. Choosing capture versus a parameter is a decision about the behavior's lifetime.

PHP
<?php
$discount = 2;
$applyDiscount = fn(int $price): int => $price - $discount;
$discount = 5;
echo $applyDiscount(10) . PHP_EOL;
echo $discount . PHP_EOL;
Output
8
5

Common PHP Arrow Functions Mistakes

Expecting outer scalar mutation or block syntax

Writing fn() => $count++ does not create a shared outer counter merely because the expression mentions count. Use an ordinary closure with deliberate reference capture, an object that owns the state, or a returned result. Also do not write a brace-delimited multi-statement body after the arrow; PHP arrow functions accept one expression.

Avoid a void return type for an arrow function that necessarily returns its expression. When you need a procedure with no returned value, an ordinary closure with a statement body can describe that intent directly. Syntax should reflect the operation's contract rather than force a value-returning form onto every action.

PHP Arrow Functions in Real Projects

Practical task: keep products above a threshold

Filter trusted product records by a minimum stock count supplied outside the callback. Keep records with stock at least three, then extract their names into a display list. The threshold is stable for this filter operation, making automatic capture appropriate. Reindex after filtering because the result is intended to be a list.

PHP
<?php
$minimum = 3;
$products = [
    ['name' => 'Pen', 'stock' => 4],
    ['name' => 'Book', 'stock' => 1],
    ['name' => 'Bag', 'stock' => 3],
];
$kept = array_values(array_filter(
    $products,
    fn(array $product): bool => $product['stock'] >= $minimum
));
$names = array_map(fn(array $product): string => $product['name'], $kept);
echo implode(',', $names) . PHP_EOL;
Output
Pen,Bag

The first callback answers whether to keep a record; the second extracts one field. Try a threshold of five and verify that the empty result is valid. If products are externally supplied, validate stock as a nonnegative integer and name as a string before the pipeline. The arrow functions assume that contract rather than replacing its validation.

PHP Arrow Functions FAQ

Do arrow functions need use?

No. Referenced outer variables are automatically captured by value. Ordinary anonymous functions use an explicit use clause.

Can the expression span several lines?

Yes. The limit is one expression, not one visual line. Break long calls into readable lines without inventing a statement block.

Are arrow functions pure?

Not automatically. They can call methods with side effects or mutate captured objects. Keep pure transformations pure by design.

When should I choose a closure instead?

When you need several statements, reference capture or a clearer sequence of named intermediate steps.

Can I add types?

Yes. Parameter and return declarations work, subject to the language's usual type rules and the result actually returned.

PHP Arrow Functions Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

A callback needs logging, a loop and two early returns. Which form is usually clearest?

PHP Arrow Functions Quiz

2 questions to check what you learned.

  1. Which keyword starts a PHP arrow function?

    • fn
    • function=>
    • lambda
  2. An outer scalar changes after an arrow function is created. Which value does its capture normally use?

    • Always the latest outer assignment
    • The value captured at creation
    • No value can ever be captured