From 641fd0a43637b89f42405444411a64d64429a32e Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 15 Oct 2025 16:22:27 -0700 Subject: [PATCH 1/6] Use more explicit header parsing Co-authored-by: Dennis Snell --- src/wp-includes/template.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/template.php b/src/wp-includes/template.php index 65771bf418e42..0e0b4bdfc7abb 100644 --- a/src/wp-includes/template.php +++ b/src/wp-includes/template.php @@ -926,13 +926,13 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph $is_html_content_type = null; $html_content_types = array( 'text/html', 'application/xhtml+xml' ); foreach ( headers_list() as $header ) { - $header_parts = preg_split( '/\s*[:;]\s*/', strtolower( $header ) ); + $header_parts = explode( ':', strtolower( $header ), 2 ); if ( - is_array( $header_parts ) && - count( $header_parts ) >= 2 && - 'content-type' === $header_parts[0] + count( $header_parts ) === 2 && + 'content-type' === trim( $header_parts[0] ) ) { - $is_html_content_type = in_array( $header_parts[1], $html_content_types, true ); + $content_type = trim( strtok( $header_parts[1], ';' ) ); + $is_html_content_type = in_array( $content_type, $html_content_types, true ); break; // PHP only sends the first Content-Type header in the list. } } From 95c8bd9ff09bdfb7cbd160111e85afad194c9ec6 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 15 Oct 2025 16:22:48 -0700 Subject: [PATCH 2/6] Add explicit content type in tests --- tests/phpunit/tests/template.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index 676c08129b526..815896f2d7872 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -598,6 +598,9 @@ static function ( string $buffer ) use ( &$filter_args ): string { PHP_INT_MAX ); + $this->assertCount( 0, headers_list(), 'Expected no headers to have been sent during unit tests.' ); + ini_set( 'default_mimetype', 'text/html' ); // Since sending a header won't work. + $initial_ob_level = ob_get_level(); $this->assertTrue( wp_start_template_enhancement_output_buffer(), 'Expected wp_start_template_enhancement_output_buffer() to return true indicating the output buffer started.' ); $this->assertSame( 1, did_action( 'wp_template_enhancement_output_buffer_started' ), 'Expected the wp_template_enhancement_output_buffer_started action to have fired.' ); @@ -677,6 +680,9 @@ static function ( string $buffer ) use ( &$applied_filter ): string { } ); + $this->assertCount( 0, headers_list(), 'Expected no headers to have been sent during unit tests.' ); + ini_set( 'default_mimetype', 'text/html' ); // Since sending a header won't work. + $initial_ob_level = ob_get_level(); $this->assertTrue( wp_start_template_enhancement_output_buffer(), 'Expected wp_start_template_enhancement_output_buffer() to return true indicating the output buffer started.' ); $this->assertSame( 1, did_action( 'wp_template_enhancement_output_buffer_started' ), 'Expected the wp_template_enhancement_output_buffer_started action to have fired.' ); @@ -741,6 +747,9 @@ static function ( string $buffer ) use ( &$called_filter ): string { } ); + $this->assertCount( 0, headers_list(), 'Expected no headers to have been sent during unit tests.' ); + ini_set( 'default_mimetype', 'application/xhtml+xml' ); // Since sending a header won't work. + $initial_ob_level = ob_get_level(); $this->assertTrue( wp_start_template_enhancement_output_buffer(), 'Expected wp_start_template_enhancement_output_buffer() to return true indicating the output buffer started.' ); $this->assertSame( 1, did_action( 'wp_template_enhancement_output_buffer_started' ), 'Expected the wp_template_enhancement_output_buffer_started action to have fired.' ); @@ -750,15 +759,18 @@ static function ( string $buffer ) use ( &$called_filter ): string { + Unprocessed

Hello World!

+ '; ?> - + + Template Replaced @@ -800,7 +812,9 @@ public function test_wp_start_template_enhancement_output_buffer_for_json(): voi $this->assertSame( 1, did_action( 'wp_template_enhancement_output_buffer_started' ), 'Expected the wp_template_enhancement_output_buffer_started action to have fired.' ); $this->assertSame( $initial_ob_level + 1, ob_get_level(), 'Expected the output buffer level to have been incremented.' ); + $this->assertCount( 0, headers_list(), 'Expected no headers to have been sent during unit tests.' ); ini_set( 'default_mimetype', 'application/json' ); // Since sending a header won't work. + $json = wp_json_encode( array( 'success' => true, From 366414d02b05d810313e20a41b7577f17ef5d0e6 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 15 Oct 2025 16:27:39 -0700 Subject: [PATCH 3/6] Eliminate copy of variable Co-authored-by: Dennis Snell --- src/wp-includes/template.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wp-includes/template.php b/src/wp-includes/template.php index 0e0b4bdfc7abb..1a39ca8b430be 100644 --- a/src/wp-includes/template.php +++ b/src/wp-includes/template.php @@ -945,8 +945,6 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph return $output; } - $filtered_output = $output; - /** * Filters the template enhancement output buffer prior to sending to the client. * @@ -962,5 +960,5 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph * @param string $filtered_output HTML template enhancement output buffer. * @param string $output Original HTML template output buffer. */ - return (string) apply_filters( 'wp_template_enhancement_output_buffer', $filtered_output, $output ); + return (string) apply_filters( 'wp_template_enhancement_output_buffer', $output, $output ); } From 69ed3329a168536840b52f11c94222823079c66f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 16 Oct 2025 12:58:19 -0700 Subject: [PATCH 4/6] Revert "Eliminate copy of variable" This reverts commit 366414d02b05d810313e20a41b7577f17ef5d0e6. --- src/wp-includes/template.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/template.php b/src/wp-includes/template.php index 1a39ca8b430be..0e0b4bdfc7abb 100644 --- a/src/wp-includes/template.php +++ b/src/wp-includes/template.php @@ -945,6 +945,8 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph return $output; } + $filtered_output = $output; + /** * Filters the template enhancement output buffer prior to sending to the client. * @@ -960,5 +962,5 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph * @param string $filtered_output HTML template enhancement output buffer. * @param string $output Original HTML template output buffer. */ - return (string) apply_filters( 'wp_template_enhancement_output_buffer', $output, $output ); + return (string) apply_filters( 'wp_template_enhancement_output_buffer', $filtered_output, $output ); } From 08bf13902c21ca7d9deb41b90f203831732fdd67 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 16 Oct 2025 12:59:51 -0700 Subject: [PATCH 5/6] Eliminate trimming of header name Co-authored-by: Dennis Snell --- src/wp-includes/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/template.php b/src/wp-includes/template.php index 611d59cc704a9..a1329a99347a8 100644 --- a/src/wp-includes/template.php +++ b/src/wp-includes/template.php @@ -929,7 +929,7 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph $header_parts = explode( ':', strtolower( $header ), 2 ); if ( count( $header_parts ) === 2 && - 'content-type' === trim( $header_parts[0] ) + 'content-type' === $header_parts[0] ) { $content_type = trim( strtok( $header_parts[1], ';' ) ); $is_html_content_type = in_array( $content_type, $html_content_types, true ); From 1142f8f58af9a4d7b9f80247d3c55b1704361ea5 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 18 Oct 2025 00:22:33 -0700 Subject: [PATCH 6/6] Improve parsing of media type Co-authored-by: Dennis Snell --- src/wp-includes/template.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/template.php b/src/wp-includes/template.php index a1329a99347a8..f17307e19daa2 100644 --- a/src/wp-includes/template.php +++ b/src/wp-includes/template.php @@ -931,8 +931,19 @@ function wp_finalize_template_enhancement_output_buffer( string $output, int $ph count( $header_parts ) === 2 && 'content-type' === $header_parts[0] ) { - $content_type = trim( strtok( $header_parts[1], ';' ) ); - $is_html_content_type = in_array( $content_type, $html_content_types, true ); + /* + * This is looking for very specific content types, therefore it + * doesn’t need to fully parse the header’s value. Instead, it needs + * only assert that the content type is one of the static HTML types. + * + * Example: + * + * Content-Type: text/html; charset=utf8 + * Content-Type: text/html ;charset=latin4 + * Content-Type:application/xhtml+xml + */ + $media_type = trim( strtok( $header_parts[1], ';' ), " \t" ); + $is_html_content_type = in_array( $media_type, $html_content_types, true ); break; // PHP only sends the first Content-Type header in the list. } }