Arrow Functions

PHP 5.3 has introduced anonymous functions and closures, allowing us to define a function in-place, and thus write code that has no external dependencies. This comes in handy with comparison functions, for example.

Especially when they only perform a simple operation, anonymous functions in PHP can be perceived as rather verbose when compared to other programming languages. Consider the following example:

function array_values_from_keys(array $array, array $keys): array
{
    return array_map(
        function (string $x) use ($array)
        {
            return $array[$x];
        },
        $keys
    );
}

$array = ['a' => 1, 'b' => 2, 'c' => 3];
$keys  = ['a', 'c'];

var_dump(array_values_from_keys($array, $keys));

Executing the code shown above will print the output shown below:

array(2) {
  [0]=>
  int(1)
  [1]=>
  int(3)
}

The closure’s actual operation, return $array[$x], gets lost between the required boilerplate code. The example shown below accomplishes the same as the one shown above – but with less code:

function array_values_from_keys(array $array, array $keys): array
{
    return array_map(
        fn(string $x) => $array[$x],
        $keys
    );
}

The new fn keyword is used to declare a so-called arrow function. These provide a more concise syntax to express closures.

fn(parameter list) => expression

When a variable used in the expression is defined in the parent scope it will be implicitly captured by-value. Implicitly captured variables follow the same scoping rules as regular function arguments. This means that you can safely modify them locally.

$y = 1;
$f = fn(int $x): int => $x + $y;

The arrow function shown above is equivalent to the closure shown below:

$y = 1;
$f = function (int $x) use ($y) { return $x + $y; };

If you look closely, you will notice that fn(int $x): int has a return type declaration and function (int $x) use ($y) does not. This is another advantage of arrow functions over regular closures as function (int $x): int use ($y) is not possible and results in a compiler error.

Arrow functions may be nested:

$z  = 1;
$fn = fn(int $x) => fn(int $y): int => $x * $y + $z;

In the example shown above, the outer arrow function captures $z from the parent scope. The inner arrow function then also captures $z from the outer function’s scope. Thus the value of $z from the parent scope becomes available in the inner arrow function.

The syntax for arrow function allows type declarations for parameters and return values, default values, variadics, as well as by-reference passing and returning. The examples shown below are all valid:

fn(int $x) => $x;
fn(): int => $x;
fn(int $x = 42) => $x;
fn(&$x) => $x;
fn&($x) => $x;
fn($x, ...$rest) => $rest;

$this is automatically bound when an arrow function is created inside a class method. Just like with regular closures, the static keyword can be used to prevent this:

class C
{
    public function m(): void
    {
        $f = fn() => var_dump($this);
        $f();

        $f = static fn() => var_dump($this);
        $f();
    }
}

Executing the code shown above will print the output shown below:

object(C)#1 (0) {
}
PHP Fatal error: Uncaught Error: Using $this when not in object context in ...

Because the first arrow function was declared with just fn the $this variable is bound and can be accessed. The second arrow function is declared with static fn, the $this variable is therefore not bound and not available. This is why accessing it leads to an error.