Skip to content
Theory · Functions as values · lesson 5 of 8

Arrow functions and strlen(...)

So far, a closure has been a recipe card with prices written on it through use. But some recipes are a single line, like "multiply by the rate". Nobody makes a whole card for those: a sticky note will do.

A sticky note is small: it holds one line. In return, it copies the prices mentioned in that line on its own, so you do not have to list them. In PHP, such a sticky note is an arrow function, fn.

Let's compare an anonymous function and an arrow function that do the same thing:

PHP
<?php

$rate = 2;
$long = function ($n) use ($rate) {
    return $n * $rate;
};
$short = fn($n) => $n * $rate;
echo $long(3), ' ', $short(3), "\n"; // 6 6

Let's go step by step:

  1. $long is an anonymous function with a use list, curly braces and return.
  2. $short does the same in one line. It has no use list, yet it still sees $rate.
  3. Both calls return 6, and echo prints 6 6.

The fn syntax

An arrow function is a short anonymous function whose body is a single expression. Let's take fn($n) => $n * $rate apart:

  • fn is the keyword; in the PER Coding Style, the parentheses with the parameters come right after it, with no space;
  • ($n) are the parameters, as in any function; you can add types too: fn(int $n): int => $n * 2;
  • => separates the parameters from the body;
  • $n * $rate is the body, one expression; the function returns its value by itself, so you do not write return.

An arrow function is a Closure object too. You put it in a variable, pass it to a callable parameter and call it with parentheses.

Capture without use

An arrow function captures the outer variables its expression mentions on its own. It captures them by value, at the moment it is created, as use ($rate) does:

PHP
$rate = 10;
echo $short(3), "\n"; // 6
$count = 0;
$add = fn() => ++$count;
echo $add(), ' ', $add(), ' ', $count, "\n"; // 1 1 0

Let's see what happens here:

  1. $rate = 10; changes the outer variable, but $short captured the copy 2 and still returns 6.
  2. $add increments $count, but only its own copy. Each call starts from 0 and returns 1.
  3. The outer $count stays 0.

This is where the sticky-note picture is off. The note copies prices on its own, but "see the board" does not fit on it: fn cannot capture a variable by reference. A counter needs an anonymous function with use (&$count).

How an arrow function differs from an anonymous one is a frequent interview question. The answer:

  • fn captures outer variables on its own, while function sees only those listed in use;
  • fn captures by value only, while function can also do use (&$x);
  • the body of fn is one expression without return, while function can hold as many statements as you like.

A closure from an existing function

Sometimes the function you need already exists, such as the built-in strlen. Since PHP 8.1, you can make a closure from it with strlen(...). The three dots in the parentheses stand where the arguments would be and mean "do not call it, make a closure".

PHP
$len = strlen(...);
$up = strtoupper(...);
echo $len('tea'), ' ', $up('tea'), "\n"; // 3 TEA
echo gettype($len), "\n"; // object

Let's go step by step:

  1. strlen(...) does not call strlen but returns a Closure object, which goes into $len.
  2. $len('tea') calls strlen('tea') and returns 3, and $up('tea') returns TEA.
  3. echo gettype($len) prints object, as for any anonymous function.

The string 'strlen' is a callable too, but strlen(...) has an advantage. If you misspell the name, PHP stops the program right away, on the line with (...). A misspelled string is checked only when it is called or passed to a callable parameter.

Common mistake

Someone tries to give an arrow function a body in curly braces:

PHP
<?php

$double = fn($n) => { // ✗
    return $n * 2;
};
echo $double(7), "\n";
TEXT
Parse error: syntax error, unexpected token "{"

After =>, PHP expects one expression, and statements in curly braces are never an expression. Because of the parse error, the file does not start at all. The fix is fn($n) => $n * 2, and for a body of several statements, an anonymous function with function.

Key points

  • fn($n) => expression is an arrow function: one expression, whose value it returns without return.
  • fn captures the outer variables of its expression on its own, by value, when it is created.
  • fn cannot capture a variable by reference: that needs function with use (&$x).
  • strlen(...) (PHP 8.1) makes a closure from an existing function without calling it.