From a5397469b7254bf54c32fd38e67c1332d65e23be Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Mon, 6 Apr 2026 16:34:40 +0530 Subject: [PATCH 01/11] Formatting: Add `strip_html_newlines()` function to preserve newlines in specific HTML elements --- src/wp-includes/formatting.php | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 94f8b3da849cb..17b5074df26d9 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -868,6 +868,52 @@ function shortcode_unautop( $text ) { return preg_replace( $pattern, '$1', $text ); } +function strip_html_newlines( $text ) { + if ( ! str_contains( $text, "\n" ) && ! str_contains( $text, "\r" ) ) { + return $text; + } + + $preserve_newlines_elements = array( 'pre', 'code', 'kbd', 'script', 'style' ); + + $textarr = wp_html_split( $text ); + $changed = false; + $skip = false; + + foreach ( $textarr as $i => $chunk ) { + if ( '' === $chunk ) { + continue; + } + + if ( 0 !== $i % 2 ) { + foreach ( $preserve_newlines_elements as $element ) { + if ( stripos( $chunk, '<' . $element ) === 0 ) { + $skip = true; + break; + } + if ( stripos( $chunk, '' ) === 0 ) { + $skip = false; + break; + } + } + continue; + } + + if ( ! $skip ) { + $stripped = preg_replace( '/[\n\r]+/', ' ', $chunk ); + if ( $stripped !== $chunk ) { + $textarr[ $i ] = $stripped; + $changed = true; + } + } + } + + if ( $changed ) { + return implode( '', $textarr ); + } + + return $text; +} + /** * Checks to see if a string is utf8 encoded. * From d9388146a39929ca17f4a25b3d89147febe2fe3e Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Mon, 6 Apr 2026 16:35:25 +0530 Subject: [PATCH 02/11] Formatting: Add documentation for `strip_html_newlines()` function --- src/wp-includes/formatting.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 17b5074df26d9..502afb76534cc 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -868,6 +868,14 @@ function shortcode_unautop( $text ) { return preg_replace( $pattern, '$1', $text ); } +/** + * Strips unnecessary newlines from HTML content. + * + * @since 7.1.0 + * + * @param string $text The HTML content to strip newlines from. + * @return string The HTML content with unnecessary newlines removed. + */ function strip_html_newlines( $text ) { if ( ! str_contains( $text, "\n" ) && ! str_contains( $text, "\r" ) ) { return $text; From 58407b1b994c199e570b31fcb04415c34eeff6d8 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Tue, 7 Apr 2026 20:04:47 +0530 Subject: [PATCH 03/11] Tests: Add initial test class --- tests/phpunit/tests/formatting/stripHtmlNewlines.php | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/phpunit/tests/formatting/stripHtmlNewlines.php diff --git a/tests/phpunit/tests/formatting/stripHtmlNewlines.php b/tests/phpunit/tests/formatting/stripHtmlNewlines.php new file mode 100644 index 0000000000000..5e6c1a0c3ca97 --- /dev/null +++ b/tests/phpunit/tests/formatting/stripHtmlNewlines.php @@ -0,0 +1,5 @@ + Date: Tue, 7 Apr 2026 20:05:13 +0530 Subject: [PATCH 04/11] Tests: Add documentation --- tests/phpunit/tests/formatting/stripHtmlNewlines.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/formatting/stripHtmlNewlines.php b/tests/phpunit/tests/formatting/stripHtmlNewlines.php index 5e6c1a0c3ca97..d984a3111ea93 100644 --- a/tests/phpunit/tests/formatting/stripHtmlNewlines.php +++ b/tests/phpunit/tests/formatting/stripHtmlNewlines.php @@ -1,5 +1,11 @@ Date: Tue, 7 Apr 2026 20:06:21 +0530 Subject: [PATCH 05/11] Tests: Verify if it strips newlines from the nodes --- tests/phpunit/tests/formatting/stripHtmlNewlines.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/phpunit/tests/formatting/stripHtmlNewlines.php b/tests/phpunit/tests/formatting/stripHtmlNewlines.php index d984a3111ea93..e999844dda581 100644 --- a/tests/phpunit/tests/formatting/stripHtmlNewlines.php +++ b/tests/phpunit/tests/formatting/stripHtmlNewlines.php @@ -8,4 +8,14 @@ * @covers ::strip_html_newlines */ class Tests_Formatting_StripHtmlNewlines extends WP_UnitTestCase { + public function test_strips_newlines_from_text_nodes() { + $this->assertSame( '', strip_html_newlines( '' ), 'Empty string should be returned as-is.' ); + $this->assertSame( '

No newlines here.

', strip_html_newlines( '

No newlines here.

' ), 'Text without newlines should be returned as-is.' ); + $this->assertSame( '

Line one Line two Line three

', strip_html_newlines( "

Line one\n\nLine two\r\nLine three

" ), 'Multiple newlines and carriage returns should be collapsed to a single space.' ); + $this->assertSame( + '

This is a paragraph in which the wpautop() wrapping will happen in the middle of an anchor, which is an inline element.

', + strip_html_newlines( "

This is a paragraph in which the\nwpautop() wrapping will\nhappen in the middle of an\nanchor, which is an inline element.

" ), + 'Newlines within and around inline elements should be stripped.' + ); + } } From 91ca8a4b8fddf0df6a199397e32512a33ea4bb81 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Tue, 7 Apr 2026 20:06:42 +0530 Subject: [PATCH 06/11] Tests: Add documentation --- tests/phpunit/tests/formatting/stripHtmlNewlines.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/phpunit/tests/formatting/stripHtmlNewlines.php b/tests/phpunit/tests/formatting/stripHtmlNewlines.php index e999844dda581..63a51b380e577 100644 --- a/tests/phpunit/tests/formatting/stripHtmlNewlines.php +++ b/tests/phpunit/tests/formatting/stripHtmlNewlines.php @@ -8,6 +8,13 @@ * @covers ::strip_html_newlines */ class Tests_Formatting_StripHtmlNewlines extends WP_UnitTestCase { + + /** + * Verifies that newlines and carriage returns in text nodes are replaced + * with spaces, including across inline elements like anchors. + * + * @ticket 5678 + */ public function test_strips_newlines_from_text_nodes() { $this->assertSame( '', strip_html_newlines( '' ), 'Empty string should be returned as-is.' ); $this->assertSame( '

No newlines here.

', strip_html_newlines( '

No newlines here.

' ), 'Text without newlines should be returned as-is.' ); From 2c6305c0e259f774d7b0f8a24d4a9eb5cf030d28 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Tue, 7 Apr 2026 20:07:08 +0530 Subject: [PATCH 07/11] Tests: Verify preservation of newlines in preformatted elements --- .../tests/formatting/stripHtmlNewlines.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/phpunit/tests/formatting/stripHtmlNewlines.php b/tests/phpunit/tests/formatting/stripHtmlNewlines.php index 63a51b380e577..efeecb4f41fa3 100644 --- a/tests/phpunit/tests/formatting/stripHtmlNewlines.php +++ b/tests/phpunit/tests/formatting/stripHtmlNewlines.php @@ -25,4 +25,25 @@ public function test_strips_newlines_from_text_nodes() { 'Newlines within and around inline elements should be stripped.' ); } + public function test_preserves_newlines_in_preformatted_elements() { + $input = "

Normal\ntext

\n
\nPreformatted\nlines\n
\n

More\ntext

"; + $result = strip_html_newlines( $input ); + + $this->assertStringContainsString( 'Normal text', $result, 'Newlines in normal text should be stripped.' ); + $this->assertStringContainsString( 'More text', $result, 'Newlines in trailing paragraph should be stripped.' ); + $this->assertStringContainsString( "\nPreformatted\nlines\n", $result, 'Newlines inside
 should be preserved.' );
+
+		$preserved_cases = array(
+			'code'   => "

A\nB

x\ny", + 'kbd' => "

A\nB

x\ny", + 'script' => "

A\nB

", + 'style' => "

A\nB

", + ); + + foreach ( $preserved_cases as $tag => $html ) { + $out = strip_html_newlines( $html ); + $this->assertStringContainsString( 'A B', $out, "Text node newline should be stripped around <{$tag}>." ); + $this->assertStringContainsString( "x\ny", $out, "Newline inside <{$tag}> should be preserved." ); + } + } } From 9eeed3715ca252c2f8e4d3d23d266154a60dddf3 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Tue, 7 Apr 2026 20:07:41 +0530 Subject: [PATCH 08/11] Tests: Add documentation for newline preservation test --- tests/phpunit/tests/formatting/stripHtmlNewlines.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/phpunit/tests/formatting/stripHtmlNewlines.php b/tests/phpunit/tests/formatting/stripHtmlNewlines.php index efeecb4f41fa3..aa2ed7608b6e3 100644 --- a/tests/phpunit/tests/formatting/stripHtmlNewlines.php +++ b/tests/phpunit/tests/formatting/stripHtmlNewlines.php @@ -25,6 +25,11 @@ public function test_strips_newlines_from_text_nodes() { 'Newlines within and around inline elements should be stripped.' ); } + + /** + * + * @ticket 5678 + */ public function test_preserves_newlines_in_preformatted_elements() { $input = "

Normal\ntext

\n
\nPreformatted\nlines\n
\n

More\ntext

"; $result = strip_html_newlines( $input ); From c53a2b7e6cee51df4f8d1879371c8b45a34ec958 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Mon, 17 Aug 2026 18:03:19 +0530 Subject: [PATCH 09/11] Ensure strip_html_newlines only preserves tags that exactly match the allowed list using regex word boundaries --- src/wp-includes/formatting.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 502afb76534cc..2d691b0ecce6b 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -894,7 +894,7 @@ function strip_html_newlines( $text ) { if ( 0 !== $i % 2 ) { foreach ( $preserve_newlines_elements as $element ) { - if ( stripos( $chunk, '<' . $element ) === 0 ) { + if ( preg_match( '~^<' . $element . '[\s/>]~i', $chunk ) ) { $skip = true; break; } From 504ab8be115e689c0cbf179fdef64ee2923c0485 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Mon, 17 Aug 2026 18:04:20 +0530 Subject: [PATCH 10/11] Add unit test coverage for strip_html_newlines with non-preserved tags --- .../tests/formatting/stripHtmlNewlines.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/phpunit/tests/formatting/stripHtmlNewlines.php b/tests/phpunit/tests/formatting/stripHtmlNewlines.php index aa2ed7608b6e3..4dc012698beb6 100644 --- a/tests/phpunit/tests/formatting/stripHtmlNewlines.php +++ b/tests/phpunit/tests/formatting/stripHtmlNewlines.php @@ -51,4 +51,20 @@ public function test_preserves_newlines_in_preformatted_elements() { $this->assertStringContainsString( "x\ny", $out, "Newline inside <{$tag}> should be preserved." ); } } + + /** + * @ticket 5678 + */ + public function test_does_not_preserve_newlines_in_tags_prefixed_with_preserved_name() { + $cases = array( + 'preload' => "some\ncontent

normal\ntext

", + 'codeblock' => "x\ny", + 'keyboard' => "a\nb", + ); + + foreach ( $cases as $tag => $html ) { + $out = strip_html_newlines( $html ); + $this->assertStringNotContainsString( "\n", $out, "Newlines inside <{$tag}> should be stripped (not a preserved element)." ); + } + } } From b74bae58b36d3a0bdc22bc021976a8e9a5753f4a Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Tue, 18 Aug 2026 16:20:29 +0530 Subject: [PATCH 11/11] Remove empty docblock comment from stripHtmlNewlines test case --- tests/phpunit/tests/formatting/stripHtmlNewlines.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/tests/formatting/stripHtmlNewlines.php b/tests/phpunit/tests/formatting/stripHtmlNewlines.php index 4dc012698beb6..2f13c52dcd135 100644 --- a/tests/phpunit/tests/formatting/stripHtmlNewlines.php +++ b/tests/phpunit/tests/formatting/stripHtmlNewlines.php @@ -27,7 +27,6 @@ public function test_strips_newlines_from_text_nodes() { } /** - * * @ticket 5678 */ public function test_preserves_newlines_in_preformatted_elements() {