-1

I have a nested menu (a sidebar) like this:

$menu = [
    'dashboard' => [
        'type' => 'item',
        'name' => 'bacheca',
        'url' => 'home.php',
    ],
    'administration' => [
        'type' => 'header',
        'name' => 'amministrazione',
        'items' => [
            'configuration' => [
                'type' => 'item',
                'name' => 'configurazione',
                'url' => 'configuration.php',
            ],
            'admins' => [
                'type' => 'item',
                'name' => 'amministratori',
                'url' => 'admins.php',
            ],
            'activities' => [
                'type' => 'item',
                'name' => 'attività',
                'url' => 'activities.php',
            ],
        ],
    ],
    'intranet' => [
        'type' => 'header',
        'name' => 'intranet',
        'items' => [
            'chiefs' => [
                'type' => 'item',
                'name' => 'capiarea',
                'url' => 'chiefs.php',
            ],
            'agents' => [
                'type' => 'item',
                'name' => 'informatori',
                'url' => 'agents.php',
            ],
            'samples' => [
                'type' => 'tree',
                'name' => 'campioni',
                'url' => '#',
                'submenu' => array_merge(
                    [
                        'dummy' => [
                            'type' => 'item',
                            'name' => 'dummy',
                            'url' => 'dummy.php',
                        ],
                    ],
                    [
                        'temperature' => [
                            'type' => 'item',
                            'name' => 'temperature',
                            'url' => 'temperature.php',
                        ],
                    ]),
            ],
            'files' => [
                'type' => 'item',
                'name' => 'riferimenti normativi',
                'url' => 'files.php',
            ],
            'documents' => [
                'type' => 'item',
                'name' => 'documenti',
                'url' => 'documents.php',
            ],
        ],
    ],
];

With a recursive function I try to find the matching 'item' type in the menu and return it, for future elaboration (types 'header' and 'tree' are not targeted). Here is the recursive function I wrote:

function item($needle, $haystack, $result = []){
    foreach ($haystack as $key => $value){
        switch ($value['type']) {
            case 'header':
                if (isset($value['items'])){
                    item($needle, $value['items'], $result);
                }
                break;
            case 'tree':
                if (isset($value['submenu'])){
                    item($needle, $value['submenu'], $result);
                }
                break;
            default:
                if ($needle == $key){
                    $result['name'] = $value['name'];
                    $result['url'] = $value['url'];
                    return $result;
                }
        }
    }
}

I don't know why, this function returns nothing: if I type var_dump(item('chiefs')); I expect it to return the item with chief key, but I get nothing

The problem lies in the return statement: in the if ($needle == $key){ .. } condition I can echo or var_dump() the matching array, but when I use return $result, it prints NULL

Here is a "live" script to play with: where am I wrong?

Ivan
  • 2,463
  • 6
  • 39
  • 51

1 Answers1

3

https://www.tehplayground.com/Ix0ODne19Acsjvyx

The problem is that

case 'header':
        if (isset($value['items'])){
            item($needle, $value['items'], $result);
        }
        break;
case 'tree':
        if (isset($value['submenu'])){
            item($needle, $value['submenu'], $result);
        }
        break;

If it goes into either of these and find what you are looking for, you don't return anything.

You need to save the return value of the recursion, if it's not null, then return it, otherwise, continue.

case 'header':
        if (isset($value['items'])){
            $v = item($needle, $value['items'], $result);
            if ($v) return $v;
        }
        break;
case 'tree':
        if (isset($value['submenu'])){
            $v = item($needle, $value['submenu'], $result);
            if ($v) return $v;
        }
        break;
dave
  • 62,300
  • 5
  • 72
  • 93
  • Imagine you have a recursive function `get_to_0($x) { if ($x == 0) return $x; else get_to_0($x-1); }` and then you do `get_to_0(1)`, it will first check if `$x == 0`, and since it's not, it will then call `get_to_0(0)`, so you have two function calls on the stack `f(1) => f(0)`, `f(0)` returns `0` to `f(1)`, but `f(1)` doesn't return anything to it's caller, because in the `else` part of `f(1)` there is no `return`. If instead we had `get_to_0($x) { if ($x == 0) return $x; else return get_to_0($x-1); }` then `f(0)` returns 0, and `f(1)` *also* returns 0, because it passes along `f(0)` return – dave May 07 '18 at 18:09
  • Your code is say 5 calls deep, and you find what you are looking for, so you return the value. Now your code is 4 calls deep, and you are in `case: 'header'`. From here, your code then breaks out of the for loop and does not return anything. The item you found does not propogate back through the call tree – lbenedetto May 07 '18 at 18:12