This is an old revision of the document!
PHP RFC: debug_backtrace_depth(int $limit=0): int
- Version: 0.1
- Date: 2021-03-13
- Author: Tyson Andre, tandre@php.net
- Status: Under Discussion
- First Published at: https://wiki.php.net/rfc/debug_backtrace_depth
- Implementation: https://github.com/php/php-src/pull/6653
Introduction
Inspecting the current stack trace depth is occasionally useful for manually debugging, checking for potential infinite recursion, or for checking if code would hit stack frame limits in extensions such as XDebug (xdebug.max_nesting_limit
). It is currently possible to compute the depth through count(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $limit=0))
, but this is verbose and inefficient compared to returning the depth directly.
Proposal
Add a new function debug_backtrace_depth(int $limit=0): int
that will compute the depth directly rather than build an array with the frame data.
This can be polyfilled with
min($limit, count(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)))
.
or less accurately as count(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $limit))
- For
debug_backtrace_depth
, the limit is on the returned depth. If that depth is exceeded,$limit
is returned instead) - For
debug_backtrace
, the preexisting behavior is that the$limit
is on the number of frames scanned, not the size of the returned array, and this may differ. Changing this to be a limit on the returned depth may be an improvement, but is out of scope for this RFC.
When $limit <= 0
, there is no limit.
Internally, PHP's stack frames are represented like a singly linked list (i.e. to count them, you need to iterate through the frames), and this RFC does not change that representation. The amount of time needed to compute the depth is proportional to the depth, which is why this RFC supports the optional $limit
. Counting the frames is still much faster than constructing an array with the file
, line
, class
, etc. with debug_backtrace()
.
Example use cases
While it is possible to do these things already with count(debug_backtrace(...))
, having a dedicated function to do this makes it more concise, efficient, and readable.
Tracking down potentially infinite recursion
function fibonacci(int $n): int|float { if (debug_backtrace_depth(1000) >= 1000) { // temporary debugging code throw new RuntimeException("Possibly infinite recursion at n=$n - was a base case forgotten?"); } if ($n === 1) { return 1; } return fibonacci($n - 2) + fibonacci($n - 1); } echo fibonacci(10); /* Warning: Uncaught RuntimeException: Possibly infinite recursion at n=-1988 - was a base case forgotten? in php shell code:3 Stack trace: #0 php shell code(7): fibonacci(-1988) #1 php shell code(7): fibonacci(-1986) ... */
Checking for compatibility with extensions with stack trace limits
For example, in XDebug https://xdebug.org/docs/all_settings#max_nesting_level has a default value of 256 - if you are developing an application or library which cannot override max_nesting_level you may want to ensure your library does not use deep recursion.
// e.g. a logging function function my_common_function() { if (is_running_as_unit_test() && debug_backtrace_depth(250) >= 250) { throw new TestFrameworkError("Refactor this code, it's likely to hit the default xdebug.max_nesting_level in other environments"); } // body of my_common_function() }
Debugging
This makes it more convenient to efficiently visualize how deep recursion is when adding temporary debugging statements, or where recursive calls are made relative to other calls.
<?php function fibonacci_debug(int $n) { if ($n <= 1) { $result = 1; } else { $result = fibonacci_debug($n - 2) + fibonacci_debug($n - 1); } printf("%sfib(%d) = %d\n", str_repeat('|', debug_backtrace_depth() - 1), $n, $result); return $result; } fibonacci_debug(4); /* ||fib(0) = 1 ||fib(1) = 1 |fib(2) = 2 ||fib(1) = 1 |||fib(0) = 1 |||fib(1) = 1 ||fib(2) = 2 |fib(3) = 3 fib(4) = 5 */
RFC Impact
To SAPIs
None, other than creating the new global function debug_backtrace_depth
Open Issues
Make sure there are no open issues when the vote starts!
Unaffected PHP Functionality
debug_backtrace
and debug_print_backtrace
are unchanged.
Future Scope
The behavior of debug_backtrace
and debug_print_backtrace
could be changed to make $limit be consistent with the documentation in a separate RFC. https://www.php.net/manual/en/function.debug-backtrace.php#refsect1-function.debug-backtrace-parameters.
Proposed Voting Choices
Yes/No, requiring a 2/3 majority