PHP closures, array functions, and generators

Transform arrays with map, filter, and reduce, capture variables in closures, and produce values lazily with yield.

PHP
<?php
$numbers = [1, 2, 3, 4, 5, 6];

$squares = array_map(fn($n) => $n * $n, $numbers);
$evens = array_filter($squares, fn($n) => $n % 2 === 0);
$sum = array_reduce($evens, fn($carry, $n) => $carry + $n, 0);
echo implode(",", $squares), " | ", implode(",", $evens), " | ", $sum, "\n";

$rate = 3;
$multiply = function (int $n) use ($rate) {
    return $n * $rate;
};
echo $multiply(5), "\n";

$total = 0;
array_walk($numbers, function ($n) use (&$total) {
    $total += $n;
});
echo $total, "\n";

function fibonacci(): Generator
{
    [$a, $b] = [0, 1];
    while (true) {
        yield $a;
        [$a, $b] = [$b, $a + $b];
    }
}

$first = [];
foreach (fibonacci() as $value) {
    if (count($first) === 10) {
        break;
    }
    $first[] = $value;
}
echo implode(" ", $first), "\n";
Output
1,4,9,16,25,36 | 4,16,36 | 56
15
21
0 1 1 2 3 5 8 13 21 34

Explanation

PHP has first-class functions. An arrow function, fn($x) => $x * 2, is a short closure that captures variables from the surrounding scope automatically by value. A full closure, function () use ($rate) { ... }, lists the variables it captures, and use (&$total) captures by reference so the closure can change the outer variable. array_map, array_filter, and array_reduce apply a function across an array.

A generator is a function that uses yield to produce values one at a time. It does not build a whole array in memory, so it can describe very long or even infinite sequences. foreach pulls the next value on each pass. A generator that is only partly consumed does no extra work, which makes it ideal for large files and streams.

Explore the dry run
Guided walkthroughReady
1<?php
2$numbers = [1, 2, 3, 4, 5, 6];
3
4$squares = array_map(fn($n) => $n * $n, $numbers);
5$evens = array_filter($squares, fn($n) => $n % 2 === 0);
6$sum = array_reduce($evens, fn($carry, $n) => $carry + $n, 0);
7echo implode(",", $squares), " | ", implode(",", $evens), " | ", $sum, "\n";
8
9$rate = 3;
10$multiply = function (int $n) use ($rate) {
11 return $n * $rate;
12};
13echo $multiply(5), "\n";
14
15$total = 0;
16array_walk($numbers, function ($n) use (&$total) {
17 $total += $n;
18});
19echo $total, "\n";
20
21function fibonacci(): Generator
22{
23 [$a, $b] = [0, 1];
24 while (true) {
25 yield $a;
26 [$a, $b] = [$b, $a + $b];
27 }
28}
29
30$first = [];
31foreach (fibonacci() as $value) {
32 if (count($first) === 10) {
33 break;
34 }
35 $first[] = $value;
36}
37echo implode(" ", $first), "\n";

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
An arrow function squares every element.—
—
Keep only the even squares. array_filter keeps the original keys.—
—
Fold the remaining values into one number: 4 + 16 + 36.—
—
The three results of the pipeline.—
1,4,9,16,25,36 | 4,16,36 | 56
The closure remembered $rate = 3.—
1,4,9,16,25,36 | 4,16,36 | 56
15
use (&$total) captures by reference, so each call updates the outer variable.—
1,4,9,16,25,36 | 4,16,36 | 56
15
1 + 2 + ... + 6.—
1,4,9,16,25,36 | 4,16,36 | 56
15
21
Pause here and hand the next Fibonacci number to the foreach loop.—
1,4,9,16,25,36 | 4,16,36 | 56
15
21
The generator is infinite, but only ten values were requested before the loop stopped.—
1,4,9,16,25,36 | 4,16,36 | 56
15
21
0 1 1 2 3 5 8 13 21 34

Cost and common mistakes

array_map, array_filter, and array_reduce are O(n). Each generator step is O(1).

  • A plain function with use ($x) captures by value, so later changes to $x outside are not seen. Use & to share.
  • An infinite generator needs a break or a limit on the consumer side, or the loop never ends.
  • array_filter preserves keys, so the result of the example has keys 1, 3, 5. Use array_values to renumber them.

Try it yourself

How would you take the first 5 even squares lazily?

Reveal answer and reasoning

Write a generator that yields squares one at a time, filter inside the loop, and break after collecting 5 values.

Further reading