diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index acacb131a627b..a718b4d5f4389 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -444,7 +444,9 @@ function _wptexturize_pushpop_element( $text, &$stack, $disabled_elements ) { * @return string Text which has been converted into correct paragraph tags. */ function wpautop( $text, $br = true ) { - $pre_tags = array(); + $pre_tags = array(); + $has_block_anchor = false; + $block_anchor_placeholder = ''; if ( '' === trim( $text ) ) { return ''; @@ -486,6 +488,29 @@ function wpautop( $text, $br = true ) { $allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)'; + /* + * Anchors can contain block-level elements. Replace their tags with temporary + * block-level placeholders so the paragraph cleanup does not split the anchor. + */ + if ( str_contains( $text, ']*)>(?=\s*<' . $allblocks . '[\s/>])(.*?)()~s', + '<' . $block_anchor_placeholder . '$1>$2' . $block_anchor_placeholder . '>', + $text + ); + $has_block_anchor = str_contains( $text, '<' . $block_anchor_placeholder ); + + if ( $has_block_anchor ) { + $allblocks = substr( $allblocks, 0, -1 ) . '|' . $block_anchor_placeholder . ')'; + } + } + // Add a double line break above block-level opening tags. $text = preg_replace( '!(<' . $allblocks . '[\s/>])!', "\n\n$1", $text ); @@ -563,6 +588,13 @@ function wpautop( $text, $br = true ) { $text = preg_replace( '|
]*)>|i', '', $text ); + // Move paragraphs inside anchors that contain block-level elements. + if ( $has_block_anchor ) { + $text = preg_replace( '|', '', $text ); $text = str_replace( '
(<' . $block_anchor_placeholder . '[^>]*>)|', '$1
', $text ); + $text = preg_replace( '|(' . $block_anchor_placeholder . '>)
|', '$1', $text ); + $text = preg_replace( '|\s*
|', '', $text ); + } + // If an opening or closing block element tag is preceded by an openingtag, remove it. $text = preg_replace( '!
\s*(?' . $allblocks . '[^>]*>)!', '$1', $text );
@@ -601,6 +633,11 @@ function wpautop( $text, $br = true ) {
$text = str_replace( array( ' ', '' ), "\n", $text );
}
+ // Restore anchors that contain block-level elements.
+ if ( $has_block_anchor ) {
+ $text = str_replace( array( '<' . $block_anchor_placeholder, '' . $block_anchor_placeholder . '>' ), array( '' ), $text );
+ }
+
return $text;
}
diff --git a/tests/phpunit/tests/formatting/wpAutop.php b/tests/phpunit/tests/formatting/wpAutop.php
index ee4d90645d09c..c7ebe1fbf9907 100644
--- a/tests/phpunit/tests/formatting/wpAutop.php
+++ b/tests/phpunit/tests/formatting/wpAutop.php
@@ -490,6 +490,21 @@ public function test_that_wpautop_treats_inline_elements_as_inline() {
$this->assertSame( $expected, trim( wpautop( $content ) ) );
}
+ /**
+ * @ticket 40202
+ */
+ public function test_that_wpautop_does_not_split_anchors_containing_block_elements() {
+ $content = '