This article was published over 2 years ago. Some information may be outdated.
Iterating over a nested menu is not easy, especially when the menu has many nested levels.
The problem to solve here is building an unordered HTML list based on a multi-level nested menu for products:
$products = [
'Camera & Photo' => [
'Video Projectors' => [],
'Digital Cameras' => [
'Digital SLRs' => [
'Canon' => [
'Canon EOS 4000D', 'Canon EOS 2000D DSLR'
],
'Nikon' => [
'Nikon D7500',
'Nikon D5600'
],
'Fujifilm' => [
'Fujifilm X-A5 Mirrorless'
]
],
'Mirrorless Cameras' => [
'Sony' => [
'Sony α6400 E-mount compact'
],
'Panasonic' => [
'Panasonic LUMIX DC-GH5LEB-K'
]
],
'Lenses' => [
'Camera Lenses' => [],
'Camcorder Lenses' => [],
]
],
'Mobile Phones' => [
'Smart Phones' => [
'Apple' => [
'iPhone 11',
'iPhone 11 Pro',
'iPhone Xs Max'
],
'Samsung' => [],
'Huawei' => [],
'Google' => [],
]
]
]
];
The SPL library solves this problem cleanly.
SPL provides a set of iterators. This post uses RecursiveArrayIterator and RecursiveIteratorIterator.
As its name implies, RecursiveArrayIterator iterates over a given array, but it does not support nested arrays on its own. That is why you also need RecursiveIteratorIterator.
First, wrap the array:
$products = new RecursiveArrayIterator($products);
The RecursiveIteratorIterator class has four methods that fire when specific events occur: beginIteration, endIteration, beginChildren, and endChildren.
Here is a class that uses these four methods:
class NestedList extends RecursiveIteratorIterator
{
public function beginIteration()
{
echo "<ul>".PHP_EOL;
}
public function endIteration()
{
echo "</ul>".PHP_EOL;
}
public function beginChildren()
{
echo "<ul>".PHP_EOL;
}
public function endChildren()
{
echo "</ul></li>".PHP_EOL;
}
}
Now use the class:
$products = new NestedList($products, RecursiveIteratorIterator::SELF_FIRST);
foreach ($products as $category => $item) {
if ($products->hasChildren()) {
echo "<li>$category";
} else {
echo "<li>$item</li>\n";
}
}
The
RecursiveIteratorIterator::SELF_FIRSTflag shows the parent and its children. Without this flag, it falls back toLEAVES_ONLY(the default), which means only the leaf nodes are shown.
Here is the final result:

Summary
- RecursiveArrayIterator -- iterates over arrays but does not handle nesting on its own; it must be combined with
RecursiveIteratorIterator. - Event methods --
beginIteration,endIteration,beginChildren, andendChildrengive you precise control over the HTML output at each nesting level. - SELF_FIRST flag -- required to include parent nodes in the output; without it, only leaf nodes appear.
- SPL for nested structures -- a clean, built-in solution that handles arbitrary nesting depth without manual recursion.