WordPress.org

Make WordPress Core

Ticket #38656: 38656.3.diff

File 38656.3.diff, 6.8 KB (added by pento, 5 years ago)
  • src/wp-includes/formatting.php

     
    466466        // Change multiple <br>s into two line breaks, which will turn into paragraphs.
    467467        $pee = preg_replace('|<br\s*/?>\s*<br\s*/?>|', "\n\n", $pee);
    468468
    469         $allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)';
     469        // Block level elements that can contain a <p>.
     470        $peeblocks     = 'caption|td|th|div|dd|dt|li|form|map|blockquote|address|fieldset|section|article|aside|header|footer|nav|figure|figcaption|details|menu';
     471
     472        // Block level elements that cannot contain a <p>.
     473        $peefreeblocks = 'table|thead|tfoot|col|colgroup|tbody|tr|dl|ul|ol|pre|area|math|style|p|h[1-6]|hr|legend|hgroup|summary';
     474
     475        $allblocks     = "(?:$peeblocks|$peefreeblocks)";
     476        $peeblocks     = "(?:$peeblocks)";
     477        $peefreeblocks = "(?:$peefreeblocks)";
     478
     479        // Empty elements that cannot contain anything.
     480        $emptyblocks   = "(?:hr|br)";
     481
     482        // Media elements that will be processed later.
     483        $mediablocks   = "(?:object|param|embed|audio|video|source|track)";
     484
     485        // Split the HTML into a stream of elements and text nodes.
     486        $stream = wp_html_split( $pee );
     487
     488        // Assume that the top level is pee-able.
     489        $peeable = array( true );
     490
     491        foreach ( $stream as $id => $droplet ) {
     492                if ( '<' === substr( $droplet, 0, 1 ) ) {
     493                        if ( preg_match( '!<' . $emptyblocks . '(?: [^>]*)?>!s', $droplet ) ) {
     494                                // If we encounter an empty element, ignore it.
     495                                continue;
     496                        } elseif ( preg_match( '!^<.+/>$!s', $droplet ) ) {
     497                                // Ignore self-closing elements, too.
     498                                continue;
     499                        } elseif ( preg_match( '%^<!--.+-->$%s', $droplet ) ) {
     500                                // Comments can be ignored.
     501                                continue;
     502                        } elseif ( preg_match( '%^<!\[CDATA\[.+\]\]>$%s', $droplet ) ) {
     503                                // CDATA can totally be ignored.
     504                                continue;
     505                        } elseif ( preg_match( '!^</.+>$!s', $droplet ) ) {
     506                                // Here's closing element, let's move back up a level.
     507                                array_pop( $peeable );
     508                        } elseif ( preg_match( '!^<' . $peeblocks . '(?: [^>]*)?>$!s', $droplet ) ) {
     509                                // We've just entered a pee-able block, mark it so.
     510                                $peeable[] = true;
     511                        } elseif ( preg_match( '!^<' . $mediablocks . '(?: [^>]*)?>$!s', $droplet ) ) {
     512                                // Media blocks can't really be pee'd, but they're handle later on.
     513                                $peeable[] = true;
     514                        } else {
     515                                $peeable[] = false;
     516                        }
     517
     518                        // We can't pee an element, so move to the next droplet.
     519                        continue;
     520                }
     521
     522                // If the current level can't be pee'd, protect it from being pee'd later.
     523                if( ! end( $peeable ) ) {
     524                        $stream[ $id ] = str_replace( "\n", '<!-- wpnl -->', $droplet );
     525                }
     526        }
     527
     528        // If we accidentally marked the final newline as being unpee-able (for example, if there's
     529        // some malformed HTML that didn't close properly), fix it.
     530        if ( '<!-- wpnl -->' === $stream[ $id ] ) {
     531                $stream[ $id ] = "\n";
     532        }
     533
     534        // Reform the HTML stream.
     535        $pee = implode( $stream );
    470536
    471537        // Add a double line break above block-level opening tags.
    472538        $pee = preg_replace('!(<' . $allblocks . '[\s/>])!', "\n\n$1", $pee);
     
    474540        // Add a double line break below block-level closing tags.
    475541        $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
    476542
     543        // Add a double line break below block-level opening tags that are allowed to contain a <p>.
     544        $pee = preg_replace('%(<' . $peeblocks . '(?: [^>]*)?>)%', "$1\n\n", $pee);
     545
     546        // Add a double line break above block-level closing tags that are allowed to contain a <p>.
     547        $pee = preg_replace('%(</' . $peeblocks . '>)%', "\n\n$1", $pee);
     548
    477549        // Standardize newline characters to "\n".
    478550        $pee = str_replace(array("\r\n", "\r"), "\n", $pee);
    479551
     
    506578                $pee = preg_replace( '%\s*(<(?:source|track)[^>]*>)\s*%', '$1', $pee );
    507579        }
    508580
     581        // If there's only one paragraph inside a pee block, remove the newlines.
     582        $pee = preg_replace( '%(<(' . $peeblocks . ")(?: [^>]*)?>)\n\n((?(?!\n\n).)*)\n\n(</\\2>)%s", '$1$3$4', $pee );
     583
    509584        // Remove more than two contiguous line breaks.
    510585        $pee = preg_replace("/\n\n+/", "\n\n", $pee);
    511586
     
    537612        $pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
    538613
    539614        // If an opening or closing block element tag is preceded by an opening <p> tag, remove it.
    540         $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
     615        $pee = preg_replace('%<p>(?:\s|<!-- wpnl -->)*(</?' . $allblocks . '[^>]*>)%', "$1", $pee);
    541616
    542617        // If an opening or closing block element tag is followed by a closing <p> tag, remove it.
    543         $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
     618        $pee = preg_replace('%(</?' . $allblocks . '[^>]*>)(?:\s|<!-- wpnl -->)*</p>%', "$1", $pee);
    544619
    545620        // Optionally insert line breaks.
    546621        if ( $br ) {
  • tests/phpunit/tests/formatting/Autop.php

     
    332332                $content = array();
    333333
    334334                foreach ( $blocks as $block ) {
     335                        if ( 'hr' === $block ) {
     336                                continue;
     337                        }
    335338                        $content[] = "<$block>foo</$block>";
    336339                }
    337340
     
    360363                $content = array();
    361364
    362365                foreach ( $blocks as $block ) {
     366                        if ( 'hr' === $block ) {
     367                                continue;
     368                        }
    363369                        $content[] = "<$block attr='value'>foo</$block>";
    364370                }
    365371
     
    534540
    535541                $this->assertEquals( $expected, trim( wpautop( $content ) ) );
    536542        }
     543
     544        /**
     545         * @ticket 38656
     546         */
     547        function data_paragraphs_inside_blocks() {
     548                $data = array(
     549                        array(
     550                                "<div>a\n\nb</div>",
     551                                "<div>\n<p>a</p>\n<p>b</p>\n</div>",
     552                        ),
     553                        array(
     554                                "<h1>a\n\nb</h1>",
     555                                "<h1>a\n\nb</h1>",
     556                        ),
     557                        array(
     558                                "<h1>a\nb</h1>",
     559                                "<h1>a\nb</h1>",
     560                        ),
     561                        array(
     562                                "<ul><li>a\n\nb</li></ul>",
     563                                "<ul>\n<li>\n<p>a</p>\n<p>b</p>\n</li>\n</ul>",
     564                        ),
     565                        array(
     566                                "<ul><li>a\n\n<ul><li>b\n\nc</li></ul></li></ul>",
     567                                "<ul>\n<li>\n<p>a</p>\n<ul>\n<li>\n<p>b</p>\n<p>c</p>\n</li>\n</ul>\n</li>\n</ul>",
     568                        ),
     569                        array(
     570                                "<div>a\n\n<hr>\n\nb</div>",
     571                                "<div>\n<p>a</p>\n<hr>\n<p>b</p>\n</div>",
     572                        ),
     573                        array(
     574                                "<table>\n\n<tr>\n\n<td>a\n\nb</td><td>\n\n<ul><li>c\n\n<ul><li>d\n\ne</li></ul>f\n\ng</li></ul>h</td>\n\n</tr>\n\n</table>",
     575                                "<table>\n<tr>\n<td>\n<p>a</p>\n<p>b</p>\n</td>\n<td>\n<ul>\n<li>\n<p>c</p>\n<ul>\n<li>\n<p>d</p>\n<p>e</p>\n</li>\n</ul>\n<p>f</p>\n<p>g</p>\n</li>\n</ul>\n<p>h</p>\n</td>\n</tr>\n</table>",
     576                        )
     577                );
     578
     579                return $data;
     580        }
     581
     582        /**
     583         * @ticket 38656
     584         * @dataProvider data_paragraphs_inside_blocks
     585         */
     586        function test_paragraphs_inside_blocks( $content, $expected ) {
     587                $this->assertEquals( $expected, trim( wpautop( $content ) ) );
     588        }
    537589}