When PHP hits a fatal error, it tells you the file and line but not how the code got there. Exceptions come with a stack trace, the list of function calls that led to the problem, but fatal errors don't. This RFC from Eric Norris adds a fatal_error_backtraces INI setting that gives fatal errors a stack trace too.

Before and after

Take this endless recursion:

set_time_limit(1);

function recurse() {
    usleep(100000);
    recurse();
}

recurse();

Today you only get:

Fatal error: Maximum execution time of 1 second exceeded in example.php on line 7

With the setting on, you get the full trail:

Fatal error: Maximum execution time of 1 second exceeded in example.php on line 6
Stack trace:
#0 example.php(6): usleep(100000)
#1 example.php(7): recurse()
#2 example.php(7): recurse()
...
#10 example.php(10): recurse()
#11 {main}

Now it's clear the problem is the recursion, without even opening the code.

Details

  • It respects zend.exception_ignore_args, and it hides parameters marked with #[\SensitiveParameter].
  • error_get_last() returns the trace under a trace key. You'd read it in a shutdown function, since that's the only code that runs after a fatal error.

Why only fatal errors

A backtrace keeps its arguments alive in memory for as long as the trace exists. If every warning kept a trace, objects could stick around longer than you'd expect. Fatal errors only happen when things have already gone wrong, so that cost matters less. Many applications also convert warnings into exceptions with set_error_handler(), and those already carry a trace. Fatal errors are the one kind your code can't catch.

What it means for existing code

The setting defaults to on, so fatal error messages now include a stack trace. Code or tools that parse those messages and expect the old format may need changes.

The vote

There were three votes. The main vote, to add the setting, passed 19 to 1 against a two-thirds requirement.

The second vote chose the default and needed a simple majority, and '1' (on) won 12 to 6. The third vote chose the value for PHP's own test runner, where '0' (off) won 11 to 4, so the test suite doesn't show traces.

All three closed on January 10, 2025, and the page lists the RFC as implemented.