Skip to content

Commit 3b7373b

Browse files
committed
General: Rename wp_send_late_headers action to wp_finalized_template_enhancement_output_buffer.
Also update docs for `wp_finalized_template_enhancement_output_buffer` action and `wp_template_enhancement_output_buffer` filter to warn against attempting to open an output buffer in callbacks or else a PHP fatal error will occur. Developed in #10443 Follow-up to [61088], [60936]. Props westonruter, dmsnell. See #43258. Fixes #64126. git-svn-id: https://develop.svn.wordpress.org/trunk@61111 602fd350-edb4-49c9-b593-d223f7449a82
1 parent dbab14a commit 3b7373b

File tree

4 files changed

+47
-37
lines changed

4 files changed

+47
-37
lines changed

src/wp-includes/class-wp.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,8 +588,8 @@ public function send_headers() {
588588
/**
589589
* Fires once the requested HTTP headers for caching, content type, etc. have been sent.
590590
*
591-
* The {@see 'wp_send_late_headers'} action may be used to send headers after rendering the template into an
592-
* output buffer.
591+
* The {@see 'wp_finalized_template_enhancement_output_buffer'} action may be used to send
592+
* headers after rendering the template into an output buffer.
593593
*
594594
* @since 2.1.0
595595
*

src/wp-includes/default-filters.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@
423423
add_action( 'do_all_pings', 'generic_ping', 10, 0 );
424424
add_action( 'do_robots', 'do_robots' );
425425
add_action( 'do_favicon', 'do_favicon' );
426-
add_action( 'wp_before_include_template', 'wp_start_template_enhancement_output_buffer', 1000 ); // Late priority to let `wp_template_enhancement_output_buffer` filters and `wp_send_late_headers` actions be registered.
426+
add_action( 'wp_before_include_template', 'wp_start_template_enhancement_output_buffer', 1000 ); // Late priority to let `wp_template_enhancement_output_buffer` filters and `wp_finalized_template_enhancement_output_buffer` actions be registered.
427427
add_action( 'set_comment_cookies', 'wp_set_comment_cookies', 10, 3 );
428428
add_action( 'sanitize_comment_cookies', 'sanitize_comment_cookies' );
429429
add_action( 'init', 'smilies_init', 5 );

src/wp-includes/template.php

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -844,17 +844,17 @@ function wp_should_output_buffer_template_for_enhancement(): bool {
844844
* Filters whether the template should be output-buffered for enhancement.
845845
*
846846
* By default, an output buffer is only started if a {@see 'wp_template_enhancement_output_buffer'} filter has been
847-
* added or if a plugin has added a {@see 'wp_send_late_headers'} action. For this default to apply, either of the
848-
* hooks must be added by the time the template is included at the {@see 'wp_before_include_template'} action. This
849-
* allows template responses to be streamed unless the there is code which depends on an output buffer being opened.
850-
* This filter allows a site to opt in to adding such template enhancement filters later during the rendering of the
851-
* template.
847+
* added or if a plugin has added a {@see 'wp_finalized_template_enhancement_output_buffer'} action. For this
848+
* default to apply, either of the hooks must be added by the time the template is included at the
849+
* {@see 'wp_before_include_template'} action. This allows template responses to be streamed unless the there is
850+
* code which depends on an output buffer being opened. This filter allows a site to opt in to adding such template
851+
* enhancement filters later during the rendering of the template.
852852
*
853853
* @since 6.9.0
854854
*
855855
* @param bool $use_output_buffer Whether an output buffer is started.
856856
*/
857-
return (bool) apply_filters( 'wp_should_output_buffer_template_for_enhancement', has_filter( 'wp_template_enhancement_output_buffer' ) || has_action( 'wp_send_late_headers' ) );
857+
return (bool) apply_filters( 'wp_should_output_buffer_template_for_enhancement', has_filter( 'wp_template_enhancement_output_buffer' ) || has_action( 'wp_finalized_template_enhancement_output_buffer' ) );
858858
}
859859

860860
/**
@@ -959,7 +959,7 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph
959959
// If the content type is not HTML, short-circuit since it is not relevant for enhancement.
960960
if ( ! $is_html_content_type ) {
961961
/** This action is documented in wp-includes/template.php */
962-
do_action( 'wp_send_late_headers', $output );
962+
do_action( 'wp_finalized_template_enhancement_output_buffer', $output );
963963
return $output;
964964
}
965965

@@ -975,6 +975,10 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph
975975
* (either `WP_HTML_Tag_Processor` or `WP_HTML_Processor`), or else use {@see DOM\HtmlDocument} as of PHP 8.4 which
976976
* fully supports HTML5.
977977
*
978+
* Important: Because this filter is applied inside an output buffer callback (i.e. display handler), any callbacks
979+
* added to the filter must not attempt to start their own output buffers. Otherwise, PHP will raise a fatal error:
980+
* "Cannot use output buffering in output buffering display handlers."
981+
*
978982
* @since 6.9.0
979983
*
980984
* @param string $filtered_output HTML template enhancement output buffer.
@@ -983,22 +987,28 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph
983987
$filtered_output = (string) apply_filters( 'wp_template_enhancement_output_buffer', $filtered_output, $output );
984988

985989
/**
986-
* Fires at the last moment HTTP headers may be sent.
990+
* Fires after the template enhancement output buffer has been finalized.
987991
*
988-
* This happens immediately before the template enhancement output buffer is flushed. This is in contrast with
989-
* the {@see 'send_headers'} action which fires after the initial headers have been sent before the template
990-
* has begun rendering, and thus does not depend on output buffering. This action does not fire if the "template
991-
* enhancement output buffer" was not started. This output buffer is automatically started if this action is added
992-
* before {@see wp_start_template_enhancement_output_buffer()} runs at the {@see 'wp_before_include_template'}
993-
* action with priority 1000. Before this point, the output buffer will also be started automatically if there was a
992+
* This happens immediately before the template enhancement output buffer is flushed. No output may be printed at
993+
* this action. However, HTTP headers may be sent, which makes this action complimentary to the
994+
* {@see 'send_headers'} action, in which headers may be sent before the template has started rendering. In
995+
* contrast, this `wp_finalized_template_enhancement_output_buffer` action is the possible point at which HTTP
996+
* headers can be sent. This action does not fire if the "template enhancement output buffer" was not started. This
997+
* output buffer is automatically started if this action is added before
998+
* {@see wp_start_template_enhancement_output_buffer()} runs at the {@see 'wp_before_include_template'} action with
999+
* priority 1000. Before this point, the output buffer will also be started automatically if there was a
9941000
* {@see 'wp_template_enhancement_output_buffer'} filter added, or if the
9951001
* {@see 'wp_should_output_buffer_template_for_enhancement'} filter is made to return `true`.
9961002
*
1003+
* Important: Because this action fires inside an output buffer callback (i.e. display handler), any callbacks added
1004+
* to the action must not attempt to start their own output buffers. Otherwise, PHP will raise a fatal error:
1005+
* "Cannot use output buffering in output buffering display handlers."
1006+
*
9971007
* @since 6.9.0
9981008
*
999-
* @param string $output Output buffer.
1009+
* @param string $output Finalized output buffer.
10001010
*/
1001-
do_action( 'wp_send_late_headers', $filtered_output );
1011+
do_action( 'wp_finalized_template_enhancement_output_buffer', $filtered_output );
10021012

10031013
return $filtered_output;
10041014
}

tests/phpunit/tests/template.php

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ public function test_wp_start_template_enhancement_output_buffer_for_html(): voi
646646

647647
$mock_action_callback = new MockAction();
648648
add_filter(
649-
'wp_send_late_headers',
649+
'wp_finalized_template_enhancement_output_buffer',
650650
array( $mock_action_callback, 'action' ),
651651
10,
652652
PHP_INT_MAX
@@ -733,11 +733,11 @@ static function ( string $buffer ): string {
733733
$this->assertStringContainsString( '<h1>¡Hola, mundo!</h1>', $processed_output, 'Expected processed output to contain string.' );
734734
$this->assertStringContainsString( '</html>', $processed_output, 'Expected processed output to contain string.' );
735735

736-
$this->assertSame( 1, did_action( 'wp_send_late_headers' ), 'Expected the wp_send_late_headers action to have fired.' );
737-
$this->assertSame( 1, $mock_action_callback->get_call_count(), 'Expected wp_send_late_headers action callback to have been called once.' );
736+
$this->assertSame( 1, did_action( 'wp_finalized_template_enhancement_output_buffer' ), 'Expected the wp_finalized_template_enhancement_output_buffer action to have fired.' );
737+
$this->assertSame( 1, $mock_action_callback->get_call_count(), 'Expected wp_finalized_template_enhancement_output_buffer action callback to have been called once.' );
738738
$action_args = $mock_action_callback->get_args()[0];
739-
$this->assertCount( 1, $action_args, 'Expected the wp_send_late_headers action to have been passed only one argument.' );
740-
$this->assertSame( $processed_output, $action_args[0], 'Expected the arg passed to wp_send_late_headers to be the same as the processed output buffer.' );
739+
$this->assertCount( 1, $action_args, 'Expected the wp_finalized_template_enhancement_output_buffer action to have been passed only one argument.' );
740+
$this->assertSame( $processed_output, $action_args[0], 'Expected the arg passed to wp_finalized_template_enhancement_output_buffer to be the same as the processed output buffer.' );
741741
}
742742

743743
/**
@@ -772,7 +772,7 @@ static function ( string $buffer ): string {
772772

773773
$mock_action_callback = new MockAction();
774774
add_filter(
775-
'wp_send_late_headers',
775+
'wp_finalized_template_enhancement_output_buffer',
776776
array( $mock_action_callback, 'action' ),
777777
10,
778778
PHP_INT_MAX
@@ -818,8 +818,8 @@ static function ( string $buffer ): string {
818818
$this->assertStringNotContainsString( '<title>Processed</title>', $output, 'Expected output buffer to not have string since the filter did not apply.' );
819819
$this->assertStringContainsString( '<title>Output Buffer Not Processed</title>', $output, 'Expected output buffer to have string since the output buffer was ended with cleaning.' );
820820

821-
$this->assertSame( 0, did_action( 'wp_send_late_headers' ), 'Expected the wp_send_late_headers action to not have fired.' );
822-
$this->assertSame( 0, $mock_action_callback->get_call_count(), 'Expected wp_send_late_headers action callback to have been called once.' );
821+
$this->assertSame( 0, did_action( 'wp_finalized_template_enhancement_output_buffer' ), 'Expected the wp_finalized_template_enhancement_output_buffer action to not have fired.' );
822+
$this->assertSame( 0, $mock_action_callback->get_call_count(), 'Expected wp_finalized_template_enhancement_output_buffer action callback to have been called once.' );
823823
}
824824

825825
/**
@@ -854,7 +854,7 @@ static function ( string $buffer ): string {
854854

855855
$mock_action_callback = new MockAction();
856856
add_filter(
857-
'wp_send_late_headers',
857+
'wp_finalized_template_enhancement_output_buffer',
858858
array( $mock_action_callback, 'action' ),
859859
10,
860860
PHP_INT_MAX
@@ -905,11 +905,11 @@ static function ( string $buffer ): string {
905905
$this->assertStringContainsString( '<title>Processed</title>', $output, 'Expected output buffer to have string due to filtering.' );
906906
$this->assertStringContainsString( '<h1>Template Replaced</h1>', $output, 'Expected output buffer to have string due to replaced template.' );
907907

908-
$this->assertSame( 1, did_action( 'wp_send_late_headers' ), 'Expected the wp_send_late_headers action to have fired.' );
909-
$this->assertSame( 1, $mock_action_callback->get_call_count(), 'Expected wp_send_late_headers action callback to have been called once.' );
908+
$this->assertSame( 1, did_action( 'wp_finalized_template_enhancement_output_buffer' ), 'Expected the wp_finalized_template_enhancement_output_buffer action to have fired.' );
909+
$this->assertSame( 1, $mock_action_callback->get_call_count(), 'Expected wp_finalized_template_enhancement_output_buffer action callback to have been called once.' );
910910
$action_args = $mock_action_callback->get_args()[0];
911-
$this->assertCount( 1, $action_args, 'Expected the wp_send_late_headers action to have been passed only one argument.' );
912-
$this->assertSame( $output, $action_args[0], 'Expected the arg passed to wp_send_late_headers to be the same as the processed output buffer.' );
911+
$this->assertCount( 1, $action_args, 'Expected the wp_finalized_template_enhancement_output_buffer action to have been passed only one argument.' );
912+
$this->assertSame( $output, $action_args[0], 'Expected the arg passed to wp_finalized_template_enhancement_output_buffer to be the same as the processed output buffer.' );
913913
}
914914

915915
/**
@@ -930,7 +930,7 @@ public function test_wp_start_template_enhancement_output_buffer_for_json(): voi
930930

931931
$mock_action_callback = new MockAction();
932932
add_filter(
933-
'wp_send_late_headers',
933+
'wp_finalized_template_enhancement_output_buffer',
934934
array( $mock_action_callback, 'action' ),
935935
10,
936936
PHP_INT_MAX
@@ -970,11 +970,11 @@ public function test_wp_start_template_enhancement_output_buffer_for_json(): voi
970970
$this->assertIsString( $output, 'Expected ob_get_clean() to return a string.' );
971971
$this->assertSame( $json, $output, 'Expected output to not be processed.' );
972972

973-
$this->assertSame( 1, did_action( 'wp_send_late_headers' ), 'Expected the wp_send_late_headers action to have fired even though the wp_template_enhancement_output_buffer filter did not apply.' );
974-
$this->assertSame( 1, $mock_action_callback->get_call_count(), 'Expected wp_send_late_headers action callback to have been called once.' );
973+
$this->assertSame( 1, did_action( 'wp_finalized_template_enhancement_output_buffer' ), 'Expected the wp_finalized_template_enhancement_output_buffer action to have fired even though the wp_template_enhancement_output_buffer filter did not apply.' );
974+
$this->assertSame( 1, $mock_action_callback->get_call_count(), 'Expected wp_finalized_template_enhancement_output_buffer action callback to have been called once.' );
975975
$action_args = $mock_action_callback->get_args()[0];
976-
$this->assertCount( 1, $action_args, 'Expected the wp_send_late_headers action to have been passed only one argument.' );
977-
$this->assertSame( $output, $action_args[0], 'Expected the arg passed to wp_send_late_headers to be the same as the processed output buffer.' );
976+
$this->assertCount( 1, $action_args, 'Expected the wp_finalized_template_enhancement_output_buffer action to have been passed only one argument.' );
977+
$this->assertSame( $output, $action_args[0], 'Expected the arg passed to wp_finalized_template_enhancement_output_buffer to be the same as the processed output buffer.' );
978978
}
979979

980980
/**

0 commit comments

Comments
 (0)