====== PHP RFC: Error Backtraces v2 ====== * Version: 2.1 * Date: 2024-12-05 * Author: Eric Norris, erictnorris@gmail.com * Status: Under Discussion * First Published at: https://wiki.php.net/rfc/error_backtraces ===== 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: 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 ===== * If the sub-vote to default to "1" passes, messages for fatal errors will now contain backtraces and may not match the format existing code is expecting. ===== 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''. * php.ini-development: N/A * php.ini-production: N/A ===== Proposed Voting Choices ===== This vote requires a ⅔ majority: * Yes * No ---- This vote will require a simple majority: * '1' * '0' ---- 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: * '1' * '0' ===== Patches and Tests ===== * https://github.com/php/php-src/pull/17056 ===== Implementation ===== ===== References ===== * Previous RFC: https://wiki.php.net/rfc/error_backtraces * Previous discussion: https://externals.io/message/110302 ===== Rejected Features ===== * Modifying the ''zend_error_cb'' signature to take a backtrace as a nullable argument, instead of storing the backtrace in a Zend global. Unfortunately we would still need to store the backtrace as a global for ''error_get_last'', and so while modifying the signature is attractive from a correctness standpoint, it is likely not worth making as a breaking change. We could consider it for PHP 9.0, however. * Storing backtraces as a string. This would solve the lifetime issue, but it would limit both applications and extensions from choosing their own desired format for the exception, e.g. they would be unable to JSON-format the exception if they desired.