Recursion
A recursive function calls itself to solve a smaller version of the same problem, until the problem becomes simple enough to answer directly. Recursion is the natural tool for nested structures — menus, category trees, folders, comment threads, nested JSON — which appear constantly in web applications.
Base case and recursive case
Every recursive function needs a base case that stops the recursion and a recursive case that moves towards it. Factorial is the classic example: 5! = 5 × 4!, and 1! = 1.
<?php function factorial(int $n): int { if ($n <= 1) { return 1; // base case } return $n * factorial($n - 1); // recursive case } echo factorial(5), "\n"; echo factorial(20), "\n";
120 2432902008176640000
Watching the calls
<?php function sumTo(int $n, int $depth = 0): int { $indent = str_repeat(" ", $depth); echo "{$indent}sumTo($n)\n"; $result = $n === 0 ? 0 : $n + sumTo($n - 1, $depth + 1); echo "{$indent}= $result\n"; return $result; } sumTo(3);
sumTo(3)
sumTo(2)
sumTo(1)
sumTo(0)
= 0
= 1
= 3
= 6Each call waits for the one below it to finish; then the results flow back up.
Recursion on nested data
This is where recursion really earns its keep. A category tree can be any depth, and two short functions print and count it:
<?php $categories = [ "name" => "Shop", "children" => [ ["name" => "Books", "children" => [ ["name" => "Fiction", "children" => []], ["name" => "Science", "children" => [ ["name" => "Physics", "children" => []], ]], ]], ["name" => "Music", "children" => []], ], ]; function printTree(array $node, int $level = 0): void { echo str_repeat(" ", $level), $node["name"], "\n"; foreach ($node["children"] as $child) { printTree($child, $level + 1); } } function countNodes(array $node): int { return 1 + array_sum(array_map(countNodes(...), $node["children"])); } printTree($categories); echo "Total: ", countNodes($categories), "\n";
Shop
Books
Fiction
Science
Physics
Music
Total: 6Building a tree from flat rows
Databases usually store trees as flat rows with a parent_id. A recursive function turns them into a nested structure — for example a nested HTML menu:
<?php $rows = [ ["id" => 1, "parent_id" => null, "title" => "Home"], ["id" => 2, "parent_id" => null, "title" => "Products"], ["id" => 3, "parent_id" => 2, "title" => "Laptops"], ["id" => 4, "parent_id" => 2, "title" => "Phones"], ["id" => 5, "parent_id" => 4, "title" => "Android"], ]; function menuHtml(array $rows, ?int $parentId = null, int $level = 0): string { $children = array_filter($rows, fn($r) => $r["parent_id"] === $parentId); if ($children === []) { return ""; } $pad = str_repeat(" ", $level); $html = "$pad<ul>\n"; foreach ($children as $row) { $html .= "$pad <li>" . htmlspecialchars($row["title"]) . "\n"; $html .= menuHtml($rows, $row["id"], $level + 2); $html .= "$pad </li>\n"; } return $html . "$pad</ul>\n"; } echo menuHtml($rows);
<ul>
<li>Home
</li>
<li>Products
<ul>
<li>Laptops
</li>
<li>Phones
<ul>
<li>Android
</li>
</ul>
</li>
</ul>
</li>
</ul>Walking folders
File systems are trees too. Here a function totals the size of every file under a folder (it creates a few sample files first):
<?php @mkdir("demo/photos/2026", 0777, true); file_put_contents("demo/readme.txt", str_repeat("a", 100)); file_put_contents("demo/photos/cat.jpg", str_repeat("b", 2000)); file_put_contents("demo/photos/2026/dog.jpg", str_repeat("c", 3000)); function folderSize(string $dir): int { $total = 0; foreach (scandir($dir) as $entry) { if ($entry === "." || $entry === "..") { continue; } $path = "$dir/$entry"; $total += is_dir($path) ? folderSize($path) : filesize($path); } return $total; } echo folderSize("demo"), " bytes\n";
5100 bytes
PHP also offers RecursiveDirectoryIterator for this job (lesson 54), but writing it once by hand shows the pattern.
Limits
Each call uses memory on the call stack. Very deep recursion — hundreds of thousands of levels — can exhaust memory and crash the script, and naive recursion can repeat work exponentially (the Fibonacci function in lesson 30 needed a cache). Use recursion for naturally nested data and divide-and-conquer algorithms; use loops for simple repetition.
Try it yourself
Flatten a nested array of any depth into a simple list.
1, 2, 3, 4, 5, 6, 7, 8, 9