Make WordPress Core

Ticket #38656: 38656.4.diff

File 38656.4.diff, 7.1 KB (added by desrosj, 5 years ago)
  • src/wp-includes/formatting.php

     
    492492        // Change multiple <br>s into two line breaks, which will turn into paragraphs.
    493493        $pee = preg_replace( '|<br\s*/?>\s*<br\s*/?>|', "\n\n", $pee );
    494494
    495         $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)';
     495        // Block level elements that can contain a <p>.
     496        $peeblocks = 'caption|td|th|div|dd|dt|li|form|map|blockquote|address|fieldset|section|article|aside|header|footer|nav|figure|figcaption|details|menu';
     497
     498        // Block level elements that cannot contain a <p>.
     499        $peefreeblocks = 'table|thead|tfoot|col|colgroup|tbody|tr|dl|ul|ol|pre|area|math|style|p|h[1-6]|hr|legend|hgroup|summary';
     500
     501        $allblocks     = "(?:$peeblocks|$peefreeblocks)";
     502        $peeblocks     = "(?:$peeblocks)";
     503        $peefreeblocks = "(?:$peefreeblocks)";
     504
     505        // Empty elements that cannot contain anything.
     506        $emptyblocks = '(?:hr|br)';
     507
     508        // Media elements that will be processed later.
     509        $mediablocks = '(?:object|param|embed|audio|video|source|track)';
     510
     511        // Split the HTML into a stream of elements and text nodes.
     512        $stream = wp_html_split( $pee );
     513
     514        // Assume that the top level is pee-able.
     515        $peeable = array( true );
     516
     517        foreach ( $stream as $id => $droplet ) {
     518                if ( '<' === substr( $droplet, 0, 1 ) ) {
     519                        if ( preg_match( '!<' . $emptyblocks . '(?: [^>]*)?>!s', $droplet ) ) {
     520                                // If we encounter an empty element, ignore it.
     521                                continue;
     522                        } elseif ( preg_match( '!^<.+/>$!s', $droplet ) ) {
     523                                // Ignore self-closing elements, too.
     524                                continue;
     525                        } elseif ( preg_match( '%^<!--.+-->$%s', $droplet ) ) {
     526                                // Comments can be ignored.
     527                                continue;
     528                        } elseif ( preg_match( '%^<!\[CDATA\[.+\]\]>$%s', $droplet ) ) {
     529                                // CDATA can totally be ignored.
     530                                continue;
     531                        } elseif ( preg_match( '!^</.+>$!s', $droplet ) ) {
     532                                // Here's closing element, let's move back up a level.
     533                                array_pop( $peeable );
     534                        } elseif ( preg_match( '!^<' . $peeblocks . '(?: [^>]*)?>$!s', $droplet ) ) {
     535                                // We've just entered a pee-able block, mark it so.
     536                                $peeable[] = true;
     537                        } elseif ( preg_match( '!^<' . $mediablocks . '(?: [^>]*)?>$!s', $droplet ) ) {
     538                                // Media blocks can't really be pee'd, but they're handle later on.
     539                                $peeable[] = true;
     540                        } else {
     541                                $peeable[] = false;
     542                        }
     543
     544                        // We can't pee an element, so move to the next droplet.
     545                        continue;
     546                }
     547
     548                // If the current level can't be pee'd, protect it from being pee'd later.
     549                if ( ! end( $peeable ) ) {
     550                        $stream[ $id ] = str_replace( "\n", '<!-- wpnl -->', $droplet );
     551                }
     552        }
     553
     554        // If we accidentally marked the final newline as being unpee-able (for example, if there's
     555        // some malformed HTML that didn't close properly), fix it.
     556        if ( '<!-- wpnl -->' === $stream[ $id ] ) {
     557                $stream[ $id ] = "\n";
     558        }
     559
     560        // Reform the HTML stream.
     561        $pee = implode( $stream );
    496562
    497563        // Add a double line break above block-level opening tags.
    498564        $pee = preg_replace( '!(<' . $allblocks . '[\s/>])!', "\n\n$1", $pee );
     
    500566        // Add a double line break below block-level closing tags.
    501567        $pee = preg_replace( '!(</' . $allblocks . '>)!', "$1\n\n", $pee );
    502568
     569        // Add a double line break below block-level opening tags that are allowed to contain a <p>.
     570        $pee = preg_replace( '%(<' . $peeblocks . '(?: [^>]*)?>)%', "$1\n\n", $pee );
     571
     572        // Add a double line break above block-level closing tags that are allowed to contain a <p>.
     573        $pee = preg_replace( '%(</' . $peeblocks . '>)%', "\n\n$1", $pee );
     574
    503575        // Standardize newline characters to "\n".
    504576        $pee = str_replace( array( "\r\n", "\r" ), "\n", $pee );
    505577
     
    538610                $pee = preg_replace( '|</figcaption>\s*|', '</figcaption>', $pee );
    539611        }
    540612
     613        // If there's only one paragraph inside a pee block, remove the newlines.
     614        $pee = preg_replace( '%(<(' . $peeblocks . ")(?: [^>]*)?>)\n\n((?(?!\n\n).)*)\n\n(</\\2>)%s", '$1$3$4', $pee );
     615
    541616        // Remove more than two contiguous line breaks.
    542617        $pee = preg_replace( "/\n\n+/", "\n\n", $pee );
    543618
     
    569644        $pee = str_replace( '</blockquote></p>', '</p></blockquote>', $pee );
    570645
    571646        // If an opening or closing block element tag is preceded by an opening <p> tag, remove it.
    572         $pee = preg_replace( '!<p>\s*(</?' . $allblocks . '[^>]*>)!', '$1', $pee );
     647        $pee = preg_replace( '%<p>(?:\s|<!-- wpnl -->)*(</?' . $allblocks . '[^>]*>)%', '$1', $pee );
    573648
    574649        // If an opening or closing block element tag is followed by a closing <p> tag, remove it.
    575         $pee = preg_replace( '!(</?' . $allblocks . '[^>]*>)\s*</p>!', '$1', $pee );
     650        $pee = preg_replace( '%(</?' . $allblocks . '[^>]*>)(?:\s|<!-- wpnl -->)*</p>%', '$1', $pee );
    576651
    577652        // Optionally insert line breaks.
    578653        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
     
    506512        }
    507513
    508514        /**
    509          * wpautop() should convert multiple line breaks into a paragraph regarless of <br /> format
     515         * wpautop() should convert multiple line breaks into a paragraph regardless of <br /> format
    510516         *
    511517         * @ticket 33377
    512518         */
     
    558564                $this->assertEquals( $expected2, trim( wpautop( $content2 ) ) );
    559565        }
    560566
     567        /**
     568         * @ticket 38656
     569         */
     570        function data_paragraphs_inside_blocks() {
     571                $data = array(
     572                        array(
     573                                "<div>a\n\nb</div>",
     574                                "<div>\n<p>a</p>\n<p>b</p>\n</div>",
     575                        ),
     576                        array(
     577                                "<h1>a\n\nb</h1>",
     578                                "<h1>a\n\nb</h1>",
     579                        ),
     580                        array(
     581                                "<h1>a\nb</h1>",
     582                                "<h1>a\nb</h1>",
     583                        ),
     584                        array(
     585                                "<ul><li>a\n\nb</li></ul>",
     586                                "<ul>\n<li>\n<p>a</p>\n<p>b</p>\n</li>\n</ul>",
     587                        ),
     588                        array(
     589                                "<ul><li>a\n\n<ul><li>b\n\nc</li></ul></li></ul>",
     590                                "<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>",
     591                        ),
     592                        array(
     593                                "<div>a\n\n<hr>\n\nb</div>",
     594                                "<div>\n<p>a</p>\n<hr>\n<p>b</p>\n</div>",
     595                        ),
     596                        array(
     597                                "<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>",
     598                                "<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>",
     599                        ),
     600                );
     601
     602                return $data;
     603        }
     604
     605        /**
     606         * @ticket 38656
     607         * @dataProvider data_paragraphs_inside_blocks
     608         */
     609        function test_paragraphs_inside_blocks( $content, $expected ) {
     610                $this->assertEquals( $expected, trim( wpautop( $content ) ) );
     611        }
    561612}