rfc:error_backtraces_v2

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
rfc:error_backtraces_v2 [2024/12/05 20:47] – created enorrisrfc:error_backtraces_v2 [2025/01/29 15:46] (current) – add link to implementation enorris
Line 1: Line 1:
 ====== PHP RFC: Error Backtraces v2 ====== ====== PHP RFC: Error Backtraces v2 ======
-  * Version: 1.0+  * Version: 2.1
   * Date: 2024-12-05   * Date: 2024-12-05
   * Author: Eric Norris, erictnorris@gmail.com   * Author: Eric Norris, erictnorris@gmail.com
-  * Status: Under Discussion+  * Status: Implemented 
   * First Published at: https://wiki.php.net/rfc/error_backtraces   * First Published at: https://wiki.php.net/rfc/error_backtraces
 +  * Implementation: https://github.com/php/php-src/commit/0a14ab18d2b3bcf1358aa9f25efc1ad72c18d000
  
 ===== Introduction ===== ===== Introduction =====
-PHP errors do not provide backtraces, which can make it difficult to ascertain their underlying cause. Enabling backtraces for certain errors will provide vital information to developers debugging production applications.+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: Take, for example, a recursive infinite loop:
Line 50: Line 51:
  
 ===== Proposal ===== ===== Proposal =====
-Add configuration to enable backtraces for specific PHP error levels, defaulting to ''E_FATAL_ERRORS''. 
  
-Generated backtraces must follow the existing setting for ''zend.exception_ignore_args'', and any parameters marked with the ''SensitiveParameter'' attribute.+Add a ''fatal_error_backtraces'' INI settingwhich potentially defaults to "1".
  
-The pull request associated with this RFC currently implements this via a new INI setting''error_backtrace_recording'', which a user may set to an error level mask, similar to ''error_reporting''.+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.
  
-==== Why an error mask? ====+''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''.
  
-The first version of this RFC proposed creating backtraces for **all** errors. In the resulting discussion, [[https://externals.io/message/110302#110339|@nikic had the following feedback:]]:+==== Why enable backtraces only for fatal errors? ====
  
-<blockquote> +Backtraces that contain arguments will increment the refcount for those argumentswhich means they will stay alive until the backtrace is destroyedSince ''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.
-SecondPHP has plenty of functions (especially I/O functions) where +
-ignoring notices and warnings is a requirementYou are not interested in +
-back traces for errors your ignore. Keep in mind that error_get_last() data +
-is populated independently of whether errors are suppressed or not.+
  
-Similarlyif your code promoted all warnings to exceptions using custom +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 fatal error, and developers writing shutdown handling code should expect that the application is in an unusual or unreliable state.
-error handler, then you'll perform a duplicate backtrace calculation, first +
-when the warning is originally through, and then again if you construct the +
-exception. (This is less bad than other cases, because it's "just" twice as +
-slow.)+
  
-Finallyeven if we completely disregard the question of performance, back +In additionmodern 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 forsince they cannot be handled in application code.
-traces are very noisy. You get one fatal error per request, but you might +
-easily get 1k warnings if something happens to trigger in a loop. With back +
-traces, those 1k warnings will be 100k lines. +
-</blockquote> +
- +
-[[https://externals.io/message/110302#113264|and:]] +
- +
-<blockquote> +
-To put my feedback into more actionable form: Rather than adding an ini +
-setting that enables error backtraces for all diagnosticsI'd make it a +
-mask of error types for which a backtrace should be captured, and default +
-it to fatal errors onlySo error_reporting=E_ALL, +
-error_backtrace=E_ERROR|E_CORE_ERROR|E_COMPILE_ERROR|E_USER_ERROR|E_RECOVERABLE_ERROR|E_PARSE. +
-It might be handy to expose the internal E_FATAL_ERRORS constant we have +
-for that. +
- +
-I think this should give a very sensible default behavior, and people who +
-want to capture backtraces for all errors still have an option to do so. +
-</blockquote> +
- +
-In short, I agree with this suggestion. Defaulting to ''E_FATAL_ERRORS'' is sensiblebut allowing a mask allows users the freedom to pick the setting that best suits their environment.+
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-  * If the proposal to default to ''E_FATAL_ERRORS'' passes, messages for fatal errors will now contain backtraces and may not match the format existing code is expecting.+  * 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) ===== ===== Proposed PHP Version(s) =====
Line 107: Line 78:
 ==== To SAPIs ==== ==== To SAPIs ====
  
-As currently implemented, there will be a new Zend global, ''error_backtrace'', containing the backtrace of the most recent error.+There will be a new Zend global, ''error_backtrace'', containing the backtrace of the most recent fatal error. 
  
 ==== To Existing Extensions ==== ==== To Existing Extensions ====
Line 119: Line 90:
 ==== New Constants ==== ==== New Constants ====
  
-  * ''E_FATAL_ERRORS'' — Like, ''E_ALL'', but for fatal errors only.+None.
  
 ==== php.ini Defaults ==== ==== php.ini Defaults ====
  
-''error_backtrace_recording'' — Enable backtraces for errors matching this level. Defaults to ''E_FATAL_ERRORS''.+''fatal_error_backtraces'' — Enable backtraces for errors matching ''E_FATAL_ERRORS''.
  
   * php.ini-development: N/A   * php.ini-development: N/A
Line 132: Line 103:
 This vote requires a ⅔ majority: This vote requires a ⅔ majority:
  
-<doodle title="Add error mask INI setting to enable backtraces for PHP errors" auth="enorris" voteType="single" closed="true">+<doodle title="Add a fatal_error_backtraces INI setting as described" auth="enorris" voteType="single" closed="true" closeon="2025-01-10T00:00:00Z">
    * Yes    * Yes
    * No    * No
Line 139: Line 110:
 ---- ----
  
-This vote will require a simple majority. A vote of 'No' means the default will be 0, and thus will not produce backtraces for any error level:+This vote will require a simple majority:
  
-<doodle title="Default to E_FATAL_ERRORS" auth="enorris" voteType="single" closed="true"> +<doodle title="Default value for fatal_error_backtraces" auth="enorris" voteType="single" closed="true" closeon="2025-01-10T00:00:00Z"> 
-   Yes +   * '1' 
-   No+   * '0' 
 +</doodle> 
 + 
 +---- 
 + 
 +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: 
 + 
 +<doodle title="If fatal_error_backtraces defaults to 1, default value in run-tests.php" auth="enorris" voteType="single" closed="true" closeon="2025-01-10T00:00:00Z"> 
 +   '1' 
 +   '0'
 </doodle> </doodle>
  
Line 158: Line 138:
  
 ===== Rejected Features ===== ===== 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.
  
rfc/error_backtraces_v2.1733431638.txt.gz · Last modified: 2024/12/05 20:47 by enorris