PHP closures, array functions, and generators
Transform arrays with map, filter, and reduce, capture variables in closures, and produce values lazily with yield.
On this page
PHP functions and arrays.
- Transform arrays with map, filter, and reduce, capture variables in closures, and produce values lazily with yield.
- Predict the sample output and explain the state changes.
- Complete the practice task and check the boundary cases.
The concept
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.
Code example
<?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";1,4,9,16,25,36 | 4,16,36 | 56 15 21 0 1 1 2 3 5 8 13 21 34
Follow the code
Open the walkthrough to inspect the example one step at a time.
Show guided walkthrough
<?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";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
| Step / line | What happens | State after step | Output 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 |
Behind the scenes
How PHP processes this code
The PHP runtime executes the script. echo writes to the response or terminal output.
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.