WordPress.org

Make WordPress Core

Ticket #47014: 47014-1.diff

File 47014-1.diff, 5.9 KB (added by dmsnell, 6 months ago)

Updated the RegExp to move all parsing into the pattern and use helper variables to clarify logic

  • src/wp-includes/formatting.php

    diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php
    index 51a1388dfd..ea091d0b00 100644
    a b function force_balance_tags( $text ) { 
    24652465        // WP bug fix for LOVE <3 (and other situations with '<' before a number)
    24662466        $text = preg_replace( '#<([0-9]{1})#', '&lt;$1', $text );
    24672467
    2468         while ( preg_match( '/<(\/?[\w:]*)\s*([^>]*)>/', $text, $regex ) ) {
     2468        $tag_pattern = (
     2469                '#<' . // start with an opening bracket
     2470                '(/?)' . // Group 1 - if it's a closing tag it'll have a leading slash
     2471                '(' . // Group 2 - tag name
     2472                        // custom element tags have more lenient rules than HTML tag names
     2473                        '(?:[a-z0-9]+(?:-[a-z0-9-]+)*)' .
     2474                                '|' .
     2475                        // traditional tag rules approximate HTML tag names
     2476                        '(?:[\w:]+)' .
     2477                ')' .
     2478                '(?:' .
     2479                        // we _either_ immediately close the tag with its '>' and have nothing here
     2480                        '\s*' .
     2481                        '(/?)' . // Group 3 - "attributes" for empty tag
     2482                                '|' .
     2483                        // _or_ we must start with space characters to separate the tag name from the attributes (or whitespace)
     2484                        '(\s+)' . // Group 4 - pre-attribute whitespace
     2485                        '([^>]*)' . // Group 5 - attributes
     2486                ')' .
     2487                '>#' // end with a closing bracket
     2488        );
     2489
     2490        while ( preg_match( $tag_pattern, $text, $regex ) ) {
     2491                $full_match        = $regex[0];
     2492                $has_leading_slash = ! empty( $regex[1] );
     2493                $tag_name          = $regex[2];
     2494                $tag               = strtolower( $tag_name );
     2495                $is_single_tag     = in_array( $tag, $single_tags );
     2496                $pre_attribute_ws  = isset( $regex[4] ) ? $regex[4] : '';
     2497                $attributes        = trim( isset( $regex[5] ) ? $regex[5] : $regex[3] );
     2498                $has_self_closer   = '/' === substr( $attributes, -1 );
     2499
    24692500                $newtext .= $tagqueue;
    24702501
    2471                 $i = strpos( $text, $regex[0] );
    2472                 $l = strlen( $regex[0] );
     2502                $i = strpos( $text, $full_match );
     2503                $l = strlen( $full_match );
    24732504
    24742505                // clear the shifter
    24752506                $tagqueue = '';
    24762507                // Pop or Push
    2477                 if ( isset( $regex[1][0] ) && '/' == $regex[1][0] ) { // End Tag
    2478                         $tag = strtolower( substr( $regex[1], 1 ) );
     2508                if ( $has_leading_slash ) { // End Tag
    24792509                        // if too many closing tags
    24802510                        if ( $stacksize <= 0 ) {
    24812511                                $tag = '';
    function force_balance_tags( $text ) { 
    25012531                                $tag = '';
    25022532                        }
    25032533                } else { // Begin Tag
    2504                         $tag = strtolower( $regex[1] );
    2505 
    25062534                        // Tag Cleaning
    2507 
    2508                         // If it's an empty tag "< >", do nothing
    2509                         if ( '' == $tag ) {
    2510                                 // do nothing
    2511                         } elseif ( substr( $regex[2], -1 ) == '/' ) { // ElseIf it presents itself as a self-closing tag...
     2535                        if ( $has_self_closer ) { // If it presents itself as a self-closing tag...
    25122536                                // ...but it isn't a known single-entity self-closing tag, then don't let it be treated as such and
    25132537                                // immediately close it with a closing tag (the tag will encapsulate no text as a result)
    2514                                 if ( ! in_array( $tag, $single_tags ) ) {
    2515                                         $regex[2] = trim( substr( $regex[2], 0, -1 ) ) . "></$tag";
     2538                                if ( ! $is_single_tag ) {
     2539                                        $attributes = trim( substr( $attributes, 0, -1 ) ) . "></$tag";
    25162540                                }
    2517                         } elseif ( in_array( $tag, $single_tags ) ) { // ElseIf it's a known single-entity tag but it doesn't close itself, do so
    2518                                 $regex[2] .= '/';
     2541                        } elseif ( $is_single_tag ) { // ElseIf it's a known single-entity tag but it doesn't close itself, do so
     2542                                $pre_attribute_ws = ' ';
     2543                                $attributes .= '/';
    25192544                        } else { // Else it's not a single-entity tag
    25202545                                // If the top of the stack is the same as the tag we want to push, close previous tag
    25212546                                if ( $stacksize > 0 && ! in_array( $tag, $nestable_tags ) && $tagstack[ $stacksize - 1 ] == $tag ) {
    function force_balance_tags( $text ) { 
    25262551                        }
    25272552
    25282553                        // Attributes
    2529                         $attributes = $regex[2];
    2530                         if ( ! empty( $attributes ) && $attributes[0] != '>' ) {
    2531                                 $attributes = ' ' . $attributes;
     2554                        if ( $has_self_closer && $is_single_tag ) {
     2555                            // we need some space - avoid <br/> and prefer <br />
     2556                                $pre_attribute_ws = ' ';
    25322557                        }
    25332558
    2534                         $tag = '<' . $tag . $attributes . '>';
     2559                        $tag = '<' . $tag . $pre_attribute_ws . $attributes . '>';
    25352560                        //If already queuing a close tag, then put this tag on, too
    25362561                        if ( ! empty( $tagqueue ) ) {
    25372562                                $tagqueue .= $tag;
  • tests/phpunit/tests/formatting/balanceTags.php

    diff --git a/tests/phpunit/tests/formatting/balanceTags.php b/tests/phpunit/tests/formatting/balanceTags.php
    index 783c320780..aeb02d15ff 100644
    a b class Tests_Formatting_BalanceTags extends WP_UnitTestCase { 
    6868                        '<em />',
    6969                        '<p class="main1"/>',
    7070                        '<p class="main2" />',
     71                        '<STRONG/>',
    7172                );
    7273                $expected = array(
    7374                        '<strong></strong>',
    7475                        '<em></em>',
    7576                        '<p class="main1"></p>',
    7677                        '<p class="main2"></p>',
     78                        // Valid tags are transformed to lowercase.
     79                        '<strong></strong>',
    7780                );
    7881
    7982                foreach ( $inputs as $key => $input ) {
    class Tests_Formatting_BalanceTags extends WP_UnitTestCase { 
    221224                }
    222225        }
    223226
     227        /**
     228         * Get custom element data.
     229         *
     230         * @return array Data.
     231         */
     232        public function data_custom_elements() {
     233                return array(
     234                        // Valid custom element tags.
     235                        array(
     236                                '<my-custom-element>Test</my-custom-element>',
     237                                '<my-custom-element>Test</my-custom-element>',
     238                        ),
     239                        array(
     240                                '<my-custom-element>Test',
     241                                '<my-custom-element>Test</my-custom-element>',
     242                        ),
     243                        array(
     244                                'Test</my-custom-element>',
     245                                'Test',
     246                        ),
     247                        array(
     248                                '</my-custom-element>Test',
     249                                'Test',
     250                        ),
     251                        // Invalid (or at least temporarily unsupported) custom element tags.
     252                        array(
     253                                '<MY-CUSTOM-ELEMENT>Test',
     254                                '<MY-CUSTOM-ELEMENT>Test',
     255                        ),
     256                        array(
     257                                '<my->Test',
     258                                '<my->Test',
     259                        ),
     260                        array(
     261                                '<--->Test',
     262                                '<--->Test',
     263                        ),
     264                );
     265        }
     266
     267        /**
     268         * Test custom elements.
     269         *
     270         * @ticket 47014
     271         * @dataProvider data_custom_elements
     272         *
     273         * @param string $source   Source.
     274         * @param string $expected Expected.
     275         */
     276        public function test_custom_elements( $source, $expected ) {
     277                $this->assertEquals( $expected, balanceTags( $source, true ) );
     278        }
    224279}