From 9e853c0e88885faa1c9d0f4eb1163a12413fc27b Mon Sep 17 00:00:00 2001
From: VladimirAus .
+ $peeblocks = 'caption|td|th|div|dd|dt|li|form|map|blockquote|address|fieldset|section|article|aside|header|footer|nav|figure|figcaption|details|menu';
+
+ // Block level elements that cannot contain a .
+ $peefreeblocks = 'table|thead|tfoot|col|colgroup|tbody|tr|dl|ul|ol|pre|area|math|style|p|h[1-6]|hr|legend|hgroup|summary';
+
+ $allblocks = "(?:$peeblocks|$peefreeblocks)";
+ $peeblocks = "(?:$peeblocks)";
+ $peefreeblocks = "(?:$peefreeblocks)";
+
+ // Empty elements that cannot contain anything.
+ $emptyblocks = '(?:hr|br)';
+
+ // Media elements that will be processed later.
+ $mediablocks = '(?:object|param|embed|audio|video|source|track)';
+
+ // Split the HTML into a stream of elements and text nodes.
+ $stream = wp_html_split( $text );
+
+ // Assume that the top level is pee-able.
+ $peeable = array( true );
+
+ foreach ( $stream as $id => $droplet ) {
+ if ( '<' === substr( $droplet, 0, 1 ) ) {
+ if (preg_match('!<' . $emptyblocks . '(?: [^>]*)?>!s', $droplet)) {
+ // If we encounter an empty element, ignore it.
+ continue;
+ } elseif (preg_match('!^<.+/>$!s', $droplet)) {
+ // Ignore self-closing elements, too.
+ continue;
+ } elseif (preg_match('%^$%s', $droplet)) {
+ // Comments can be ignored.
+ continue;
+ } elseif (preg_match('%^$%s', $droplet)) {
+ // CDATA can totally be ignored.
+ continue;
+ } elseif (preg_match('!^$!s', $droplet)) {
+ // Here's closing element, let's move back up a level.
+ array_pop($peeable);
+ } elseif (preg_match('!^<' . $peeblocks . '(?: [^>]*)?>$!s', $droplet)) {
+ // We've just entered a pee-able block, mark it so.
+ $peeable[] = true;
+ } elseif (preg_match('!^<' . $mediablocks . '(?: [^>]*)?>$!s', $droplet)) {
+ // Media blocks can't really be pee'd, but they are handled later on.
+ $peeable[] = true;
+ } else {
+ $peeable[] = false;
+ }
+
+ // We can't pee an element, so move to the next droplet.
+ continue;
+
+ }
+
+ // If the current level can't be pee'd, protect it from being pee'd later.
+ if ( ! end( $peeable ) ) {
+ $stream[ $id ] = str_replace( "\n", '', $droplet );
+ }
+ }
+
+ // If we accidentally marked the final newline as being unpee-able (for
+ // example, if there's some malformed HTML that didn't close properly), fix it.
+ if ( '' === $stream[ $id ] ) {
+ $stream[ $id ] = "\n";
+ }
+
+ $text = implode( $stream );
// Add a double line break above block-level opening tags.
$text = preg_replace( '!(<' . $allblocks . '[\s/>])!', "\n\n$1", $text );
@@ -492,6 +558,12 @@ function wpautop( $text, $br = true ) {
// Add a double line break below block-level closing tags.
$text = preg_replace( '!(' . $allblocks . '>)!', "$1\n\n", $text );
+ // Add a double line break below block-level opening tags that are allowed to contain a .
+ $text = preg_replace( '%(<' . $peeblocks . '(?: [^>]*)?>)%', "$1\n\n", $text );
+
+ // Add a double line break above block-level closing tags that are allowed to contain a .
+ $text = preg_replace( '%(' . $peeblocks . '>)%', "\n\n$1", $text );
+
// Add a double line break after hr tags, which are self closing.
$text = preg_replace( '!(
's into two line breaks, which will turn into paragraphs.
$text = preg_replace( '|
\s*
|', "\n\n", $text );
- $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)';
+ // Block level elements that can contain a
)!', "$1\n\n", $text );
@@ -533,6 +605,9 @@ function wpautop( $text, $br = true ) {
$text = preg_replace( '|\s*|', '', $text );
}
+ // If there's only one paragraph inside a pee block, remove the newlines.
+ $text = preg_replace( '%(<(' . $peeblocks . ")(?: [^>]*)?>)\n\n((?(?!\n\n).)*)\n\n(\\2>)%s", '$1$3$4', $text );
+
// Remove more than two contiguous line breaks.
$text = preg_replace( "/\n\n+/", "\n\n", $text );
@@ -564,10 +639,10 @@ function wpautop( $text, $br = true ) {
$text = str_replace( '
tag, remove it. - $text = preg_replace( '!
\s*(?' . $allblocks . '[^>]*>)!', '$1', $text ); + $text = preg_replace( '%
(?:\s|)*(?' . $allblocks . '[^>]*>)%', '$1', $text ); // If an opening or closing block element tag is followed by a closing
tag, remove it. - $text = preg_replace( '!(?' . $allblocks . '[^>]*>)\s*
!', '$1', $text ); + $text = preg_replace( '%(?' . $allblocks . '[^>]*>)(?:\s|)*%', '$1', $text ); // Optionally insert line breaks. if ( $br ) { diff --git a/tests/phpunit/tests/formatting/wpAutop.php b/tests/phpunit/tests/formatting/wpAutop.php index ee4d90645d09c..d0b472aa8cb01 100644 --- a/tests/phpunit/tests/formatting/wpAutop.php +++ b/tests/phpunit/tests/formatting/wpAutop.php @@ -392,6 +392,9 @@ public function test_that_wpautop_treats_block_level_elements_as_blocks() { $content = array(); foreach ( $blocks as $block ) { + if ( 'hr' === $block ) { + continue; + } $content[] = "<$block>foo$block>"; } @@ -420,6 +423,9 @@ public function test_that_wpautop_treats_block_level_elements_as_blocks() { $content = array(); foreach ( $blocks as $block ) { + if ( 'hr' === $block ) { + continue; + } $content[] = "<$block attr='value'>foo$block>"; } @@ -660,4 +666,50 @@ public function test_that_wpautop_ignores_inline_scripts() { $this->assertSameIgnoreEOL( $expected, trim( wpautop( $content ) ) ); } + + /** + * Data provider for test_paragraphs_inside_blocks(). + * + * @ticket 38656 + */ + public function data_paragraphs_inside_blocks() { + $data = array( + array( + "a
\nb
\na
\nb
\na
\nb
\nc
\na
\nb
\n| a\n\nb | \n\n
| \n\n
| \n a \nb \n | \n\n
h \n | \n