#64108 closed defect (bug) (fixed)
Triggered errors are not displayed when applying template enhancement output buffer filters
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 6.9 | Priority: | normal |
| Severity: | normal | Version: | trunk |
| Component: | General | Keywords: | has-patch has-unit-tests |
| Focuses: | performance | Cc: |
Description (last modified by )
This is follow up to #43258.
Attempting to echo during a user output buffer callback does not add anything to the output, and this is now deprecated in PHP 8.5 as discovered by @swissspidy (see initial fix in [60945]), and was noted by @jorbin:
More info on the deprecation: https://github.com/php/php-src/commit/07f1cfd9b01ff0f3720c1a5580b9e263eec5fce1 and https://wiki.php.net/rfc/deprecations_php_8_4#:~:text=has%20been%20closed.-,Deprecate%20producing%20output%20in%20a%20user%20output%20handler,buffering%20function%20in%20an%20output%20handler%20will%20emit%20a%20Fatal%20Error.,-Deprecate%20producing%20output
Of note:
Deprecate producing output in a user output handler
Because an output handler should just be manipulating the incoming buffer, any output that it produces is discarded. As such, issues within an output handler can go unnoticed and be hard to debug.
Therefore, we propose deprecating producing any output in an output handler, in the same way as attempting to use an output buffering function in an output handler will emit a Fatal Error.
Currently when WP_DEBUG_DISPLAY is enabled (and thus display_errors is on), calls to _doing_it_wrong() and wp_trigger_error() in callbacks for the wp_template_enhancement_output_buffer filter are problematic. In reality, this has been a problem already for plugins that open their own output buffers and either trigger errors in their output buffer callback or provide extensibility for other plugins which can cause errors.
We should better handle errors triggered during a user output handler. We capture with an error handler and then when display_errors is on we can append the errors as HTML to the response.
Example plugin which emits errors before and during the output buffer callback:
<?php /** * Plugin Name: Emit Errors Before and During Output Buffer Callback * Requires at least: 6.9-alpha */ namespace EmitErrorsBeforeAndDuringOutputBufferCallback; function emit_notices() { _doing_it_wrong( __FUNCTION__, '✅ You are doing it wrong at ' . current_action(), '0.0.0' ); wp_trigger_error( __FUNCTION__, '✅ Notice ' . current_action(), E_USER_NOTICE ); wp_trigger_error( __FUNCTION__, '✅ Deprecated ' . current_action(), E_USER_DEPRECATED ); wp_trigger_error( __FUNCTION__, '✅ Warning ' . current_action(), E_USER_WARNING ); } add_action( 'wp_before_include_template', __NAMESPACE__ . '\emit_notices' ); add_filter( 'wp_template_enhancement_output_buffer', static function ( $buffer ) { emit_notices(); return $buffer; } );
With PHP 8.5 active, this results in the following errors being printed before the HTML document is served:
- Deprecated: ob_end_flush(): Producing output from user output handler wp_finalize_template_enhancement_output_buffer is deprecated in /var/www/src/wp-includes/functions.php on line 5481
- Notice: Function EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices was called incorrectly. ✅ You are doing it wrong at wp_before_include_template Please see Debugging in WordPress for more information. (This message was added in version 0.0.0.) in /var/www/src/wp-includes/functions.php on line 6131
- Notice: EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices(): ✅ Notice wp_before_include_template in /var/www/src/wp-includes/functions.php on line 6131
- Deprecated: EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices(): ✅ Deprecated wp_before_include_template in /var/www/src/wp-includes/functions.php on line 6131
- Warning: EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices(): ✅ Warning wp_before_include_template in /var/www/src/wp-includes/functions.php on line 6131
Notice only the four errors printed during the wp_before_include_template action are printed. None of the errors during the wp_template_enhancement_output_buffer filter are printed. They all appear in the error log, however:
[16-Oct-2025 19:23:00 UTC] PHP Notice: Function EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices was called <strong>incorrectly</strong>. ✅ You are doing it wrong at wp_before_include_template Please see <a href="https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/">Debugging in WordPress</a> for more information. (This message was added in version 0.0.0.) in /var/www/src/wp-includes/functions.php on line 6131
[16-Oct-2025 19:23:00 UTC] PHP Notice: EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices(): ✅ Notice wp_before_include_template in /var/www/src/wp-includes/functions.php on line 6131
[16-Oct-2025 19:23:00 UTC] PHP Deprecated: EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices(): ✅ Deprecated wp_before_include_template in /var/www/src/wp-includes/functions.php on line 6131
[16-Oct-2025 19:23:00 UTC] PHP Warning: EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices(): ✅ Warning wp_before_include_template in /var/www/src/wp-includes/functions.php on line 6131
[16-Oct-2025 19:23:00 UTC] PHP Notice: Function EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices was called <strong>incorrectly</strong>. ✅ You are doing it wrong at wp_template_enhancement_output_buffer Please see <a href="https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/">Debugging in WordPress</a> for more information. (This message was added in version 0.0.0.) in /var/www/src/wp-includes/functions.php on line 6131
[16-Oct-2025 19:23:00 UTC] PHP Notice: EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices(): ✅ Notice wp_template_enhancement_output_buffer in /var/www/src/wp-includes/functions.php on line 6131
[16-Oct-2025 19:23:00 UTC] PHP Deprecated: EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices(): ✅ Deprecated wp_template_enhancement_output_buffer in /var/www/src/wp-includes/functions.php on line 6131
[16-Oct-2025 19:23:00 UTC] PHP Warning: EmitErrorsBeforeAndDuringOutputBufferCallback\emit_notices(): ✅ Warning wp_template_enhancement_output_buffer in /var/www/src/wp-includes/functions.php on line 6131
[16-Oct-2025 19:23:00 UTC] PHP Deprecated: ob_end_flush(): Producing output from user output handler wp_finalize_template_enhancement_output_buffer is deprecated in /var/www/src/wp-includes/functions.php on line 5481
[16-Oct-2025 19:23:00 UTC] PHP Deprecated: ob_end_flush(): Producing output from user output handler {closure:od_buffer_output():54} is deprecated in /var/www/src/wp-includes/functions.php on line 548
In PHP 8.4, the deprecation notice for ob_end_flush() does not appear, and neither to any of the errors emitted during the output buffer callback.
Change History (11)
This ticket was mentioned in PR #10310 on WordPress/wordpress-develop by @westonruter.
5 weeks ago
#2
- Keywords has-patch added
#4
@
4 weeks ago
Related: #64126
We'll need to capture errors emitted during any new action as well.
This ticket was mentioned in Slack in #core-performance by westonruter. View the logs.
3 weeks ago
#6
@
2 weeks ago
- Keywords has-unit-tests added
Ready for review: https://github.com/WordPress/wordpress-develop/pull/10310
@westonruter commented on PR #10310:
2 weeks ago
#7
Here's example plugin code to test this:
add_filter(
'wp_template_enhancement_output_buffer',
static function ( $buffer ) {
trigger_error( 'Oh no, a notice during filter!', E_USER_NOTICE );
trigger_error( 'Oh no, a deprecation during filter!', E_USER_DEPRECATED );
trigger_error( 'Oh no, a warning during filter!', E_USER_WARNING );
_doing_it_wrong( __FUNCTION__, 'Why did you do this in a filter??', '0.1' );
$buffer .= "<p>Filtered Successfully!</p>";
return $buffer;
},
10
);
add_action(
'wp_finalized_template_enhancement_output_buffer',
static function () {
trigger_error( 'Oh no, a notice during action!', E_USER_NOTICE );
trigger_error( 'Oh no, a deprecation during action!', E_USER_DEPRECATED );
trigger_error( 'Oh no, a warning during action!', E_USER_WARNING );
_doing_it_wrong( __FUNCTION__, 'Why did you do this in an action??', '0.1' );
},
10
);
if ( isset( $_GET['emit_error_during_filter'] ) ) {
add_filter(
'wp_template_enhancement_output_buffer',
static function ( $buffer ) {
@trigger_error( 'Oh no, an error during filter!', E_USER_ERROR );
},
100
);
}
if ( isset( $_GET['emit_exception_during_filter'] ) ) {
add_filter(
'wp_template_enhancement_output_buffer',
static function () {
throw new Exception( 'Oh no, an exception during filter!' );
},
100
);
}
if ( isset( $_GET['emit_error_during_action'] ) ) {
add_action(
'wp_finalized_template_enhancement_output_buffer',
static function () {
@trigger_error( 'Oh no, an error during action!', E_USER_ERROR );
},
100
);
}
if ( isset( $_GET['emit_exception_during_action'] ) ) {
add_action(
'wp_finalized_template_enhancement_output_buffer',
static function () {
throw new Exception( 'Oh no, an exception during action!' );
},
100
);
}
When the plugin is active, accessing the site on trunk ends simply with:
Filtered Successfully!
No errors are displayed, unexpectedly. However, the error log does include:
[01-Nov-2025 21:03:58 UTC] PHP Notice: Oh no, a notice during filter! in /var/www/src/wp-content/plugins/emit-errors-during-template-enhancement-output-buffer-callback.php on line 9
[01-Nov-2025 21:03:58 UTC] PHP Deprecated: Oh no, a deprecation during filter! in /var/www/src/wp-content/plugins/emit-errors-during-template-enhancement-output-buffer-callback.php on line 10
[01-Nov-2025 21:03:58 UTC] PHP Warning: Oh no, a warning during filter! in /var/www/src/wp-content/plugins/emit-errors-during-template-enhancement-output-buffer-callback.php on line 11
[01-Nov-2025 21:03:58 UTC] PHP Notice: Function {closure:/var/www/src/wp-content/plugins/emit-errors-during-template-enhancement-output-buffer-callback.php:8} was called <strong>incorrectly</strong>. Why did you do this in a filter?? Please see <a href="https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/">Debugging in WordPress</a> for more information. (This message was added in version 0.1.) in /var/www/src/wp-includes/functions.php on line 6131
[01-Nov-2025 21:03:58 UTC] PHP Notice: Oh no, a notice during action! in /var/www/src/wp-content/plugins/emit-errors-during-template-enhancement-output-buffer-callback.php on line 21
[01-Nov-2025 21:03:58 UTC] PHP Deprecated: Oh no, a deprecation during action! in /var/www/src/wp-content/plugins/emit-errors-during-template-enhancement-output-buffer-callback.php on line 22
[01-Nov-2025 21:03:58 UTC] PHP Warning: Oh no, a warning during action! in /var/www/src/wp-content/plugins/emit-errors-during-template-enhancement-output-buffer-callback.php on line 23
[01-Nov-2025 21:03:58 UTC] PHP Notice: Function {closure:/var/www/src/wp-content/plugins/emit-errors-during-template-enhancement-output-buffer-callback.php:20} was called <strong>incorrectly</strong>. Why did you do this in an action?? Please see <a href="https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/">Debugging in WordPress</a> for more information. (This message was added in version 0.1.) in /var/www/src/wp-includes/functions.php on line 6131
When checking out this branch, the error log entries remain unchanged, but the errors are now displayed (when display_errors is enabled):
When adding ?emit_exception_during_filter=1 to the URL, then you can see the modification added by the filter no longer applies ("Filtered Successfully!" is absent), but an error message is displayed:
If debug_display is off, then the no errors are printed.
Given that this is the template _enhancement_ output buffer, it should gracefully degrade when an issue occurs in filter/action callbacks that fire during the execution of the output buffer display handler.
@westonruter commented on PR #10310:
2 weeks ago
#8
@dmsnell Are you happy to approve the changes here?
I had Gemini CLI do a review, and it gave me the green light:
The changes introduce robust error handling for the wp_template_enhancement_output_buffer filter and wp_finalized_template_enhancement_output_buffer action in src/wp-includes/template.php. This is a significant improvement for the stability of WordPress, preventing fatal errors from crashing the site due to misbehaving callbacks.
src/wp-includes/template.php:
- Error Handling: The implementation of
try...catchblocks around the filter and action calls is a solid approach to gracefully handle exceptions and errors. - Custom Error Handler: The custom error handler correctly captures various error types and allows for their display or logging without interrupting the execution flow, which is crucial for a robust system.
- Docblock Updates: The docblocks for the filter and action have been updated to accurately reflect the new error handling behavior, including notes about output suppression and deprecation notices in newer PHP versions.
- INI Configuration Management: The temporary modification and restoration of
display_errorsand the custom error handler are handled correctly, ensuring that the changes are localized to this function. -
@phpstan-ignore: The@phpstan-ignore if.alwaysFalsecomment is present. While the logic appears sound (the variabledid_just_catchis indeed set in thecatchblock), it's worth noting that such ignores should be used judiciously. In this case, it seems to be a pragmatic solution for a specific static analysis warning.
tests/phpunit/tests/template.php:
- Comprehensive Testing: The new test method
test_wp_finalize_template_enhancement_output_buffer_with_errors_while_processingand its data provider demonstrate thorough testing of various error scenarios, including deprecated notices, warnings, and exceptions. - Adherence to Standards: The use of
@tickettags for referencing Trac tickets and@coversfor indicating the covered function aligns with WordPress testing standards. - INI Configuration for Tests: The tests correctly manipulate INI settings to create controlled environments for testing error reporting, which is essential for reliable results.
- Error Log Capture: The use of
ini_set( 'error_log', $this->temp_filename() )is an excellent practice for isolating and verifying error log output during tests. -
@phpcs:ignore: The@phpcs:ignorecomments are appropriately used in the test file to allow for specific code patterns necessary for testing error conditions.
Conclusion:
The changes are well-implemented, adhere to WordPress coding standards, and significantly improve the robustness of the template enhancement output buffering mechanism. The accompanying tests provide good coverage for the new error handling logic.
@westonruter commented on PR #10310:
2 weeks ago
#9
Since I believe all concerns have been addressed, I'm going ahead and committing this for beta3. Any additional feedback I can address over the next week.
When
display_errorsis enabled, this temporarily turns off that setting and then adds an error handler to capture errors while applyingwp_template_enhancement_output_bufferfilters. After the filters have applied, it then appends any captured errors to the buffer. This fixes an issue where errors emitted during an output buffer callback are suppressed from the output which even though a site may haveWP_DEBUG_DISPLAYenabled. In PHP 8.5, a deprecation notice is shown but the emitted errors are not displayed.Trac ticket: https://core.trac.wordpress.org/ticket/64108