Table of Contents

PHP RFC: Error Backtraces v2

Introduction

PHP errors do not provide backtraces, which can make it difficult to ascertain their underlying cause. Enabling backtraces for fatal errors will provide vital information to developers debugging production applications.

Take, for example, a recursive infinite loop:

<?php
 
set_time_limit(1);
 
function recurse() {
    usleep(100000);
    recurse();
}
 
recurse();

Currently, this results in the following error:

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

While the above code is an obvious infinite loop, in large production codebases it may not be as straightforward. If this proposal is accepted, the error could instead look like:

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()
#3 example.php(7): recurse()
#4 example.php(7): recurse()
#5 example.php(7): recurse()
#6 example.php(7): recurse()
#7 example.php(7): recurse()
#8 example.php(7): recurse()
#9 example.php(7): recurse()
#10 example.php(10): recurse()
#11 {main}

Seeing this backtrace makes it far more clear that something is amiss with the recursion in this function, without looking at the source code.

Proposal

Add a fatal_error_backtraces INI setting, which potentially defaults to “1”.

When enabled, PHP will generate backtraces for errors with an error level matching E_FATAL_ERRORS. Generated backtraces will respect the existing setting for zend.exception_ignore_args, and parameters marked with the SensitiveParameter attribute.

error_get_last will return the backtrace in the array under the trace key, if it's available. As PHP will only generate backtraces for fatal errors, this will only be available in post-shutdown code, such as callbacks registered via register_shutdown_function.

Why enable backtraces only for fatal errors?

Backtraces that contain arguments will increment the refcount for those arguments, which means they will stay alive until the backtrace is destroyed. Since error_get_last will contain the backtrace for the most recent error, application developers may be surprised by objects lasting longer than they would expect - either until they called error_clear_last or caused another error.

By limiting this feature to only E_FATAL_ERRORS, we've also limited this side effect to abnormal situations only. PHP will only execute shutdown handlers after encountering a fatal error, and developers writing shutdown handling code should expect that the application is in an unusual or unreliable state.

In addition, modern PHP applications tend to promote PHP warnings and notices to exceptions via set_error_handler, which then provides a backtrace via the Error object. It is only E_FATAL_ERRORS which are not possible to generate traces for, since they cannot be handled in application code.

Backward Incompatible Changes

Proposed PHP Version(s)

Next PHP minor release.

RFC Impact

To SAPIs

There will be a new Zend global, error_backtrace, containing the backtrace of the most recent fatal error.

To Existing Extensions

None.

To Opcache

None.

New Constants

None.

php.ini Defaults

fatal_error_backtraces — Enable backtraces for errors matching E_FATAL_ERRORS.

Proposed Voting Choices

This vote requires a ⅔ majority:

Add a fatal_error_backtraces INI setting as described
Real name Yes No
Final result: 0 0
This poll has been closed.

This vote will require a simple majority:

Default value for fatal_error_backtraces
Real name '1' '0'
Final result: 0 0
This poll has been closed.

This vote determines if we need to update all phpt tests in the php-src repo that feature fatal errors. It depends on the default value, and will require a simple majority:

If fatal_error_backtraces defaults to 1, default value in run-tests.php
Real name '1' '0'
Final result: 0 0
This poll has been closed.

Patches and Tests

Implementation

References

Rejected Features