9

How can I get the current recursion level in a PHP function?

I mean, is there any "magical" (or eventually normal) function like the following?

function doSomething($things) {
    if (is_array($things)) {
        foreach ($things as $thing) {
            doSomething($thing);
        }
    } else {
        // This is what I want:
        echo current_recursion_level();
    }
}

I know I can use another function argument ($level in this example):

function doSomething($things, $level = 0) {
    if (is_array($things)) {
        foreach ($things as $thing) {
            $level++;
            doSomething($thing, $level);
        }
    } else {
        echo $level;
    }
}

But I want to know if there is a built-in function (or trick) to do that. Maybe something with debug_backtrace(), but it does not seem to be a simple or quick solution.

I did not found this information. Maybe it simply does not exists...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
rap-2-h
  • 30,204
  • 37
  • 167
  • 263

5 Answers5

9

If you are just looking to avoid hitting PHP's 100 level recursion limit then

count(debug_backtrace()); 

should be sufficient. Otherwise you've no choice to pass a depth argument, though the precrement operator makes it somewhat cleaner as seen in the example below.

function recursable ( $depth = 0 ) {
  if ($depth > 3) {
    debug_print_backtrace();
    return true;
  } else {
    return recursable( ++$depth );
  }
}
Michael Morris
  • 531
  • 5
  • 7
  • 2
    FYI the recursion limit isn't from PHP but comes from Xdebug extension https://xdebug.org/docs/all_settings#max_nesting_level – Mrskman Nov 20 '18 at 15:05
  • Can you qualify the 100 level recursion limit in your answer? (But ***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today.) – Peter Mortensen Oct 26 '21 at 16:20
1

You need to count it yourself. The only alternative would be something like Xdebug which profiles your complete software. But this is highly inefficient.

<?php
    function my_recursive_fn($param) {
        static $counter = 0;
        if (is_array($param)) {
            ++$counter;
            foreach ($param as $k => $v) {

            }
        }
        else {
            echo $counter;
            --$counter;

            // If we're returning (only PHP 5.5+)
            try {
                return $counter;
            }
            finally {
                --$counter;
            }
        }
    }
?>

This allows you to count without a second public parameter.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fleshgrinder
  • 15,703
  • 4
  • 47
  • 56
  • Using `static` is okay, but you're counting the total number of array elements instead of the recursion level. To do the latter, just increment the variable at the start of the function and decrease it before returning. – GolezTrol Nov 12 '13 at 10:01
  • Don't you need to decrement `$counter` before returning? – Barmar Nov 12 '13 at 10:13
  • Of course, corrected it to really count the recursion. The OP has the incrementing in the loop. – Fleshgrinder Nov 12 '13 at 10:13
  • Why decrement? The function was actually called. Depends highly on the use-case. – Fleshgrinder Nov 12 '13 at 10:14
  • Because when you return, you're no longer at the higher recursion level. And the next time you start the recursion from the beginning, recursion level should start at 0 again. – Barmar Nov 12 '13 at 10:15
0

In Java you can inspect the call stack. I think you can do the same in PHP:

debug-backtrace may be the one you are looking for.

Since PHP does not optimize recursion with tail calls this should tell you the depth of the recursion.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Adam Arold
  • 29,285
  • 22
  • 112
  • 207
0
private function select($modules, $level = 0)
{
    $return_html = '';
    $level_html = '';
    $new_level = 0;

    foreach($modules as $module)
    {
        $repeat = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;', $level);

        if(!empty($module['children']))
        {
            $return_html .= '<option>' . $repeat . ' ' . $module['title'] . '</option>';

            $new_level = $level + 1;

            $return_html .= $this->select($module['children'], $new_level);
        }
        else
        {
            $return_html .= '<option>' . $repeat . ' ' . $module['title'] . '</option>';
        }
    }
    return $return_html;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Crustamet
  • 104
  • 1
  • 11
  • An explanation would be in order. E.g., what is the idea/gist? Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/63901677/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Oct 26 '21 at 16:21
-1
function doSomething($things) {
    static $level = 0;
    if (is_array($things)) {
        foreach ($things as $thing) {
            $level++;
            doSomething($thing);
        }
    }
    else {
        // This is what I want:
        echo $level
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
litechip
  • 326
  • 2
  • 9
  • Imho you need to decrement $level after the call to your function. – Paolo Gibellini Oct 20 '14 at 17:07
  • An explanation would be in order. E.g., what is the idea/gist? Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/19926118/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Oct 26 '21 at 15:45