diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index ecae45083a6ca..9a26c3f22f468 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -666,7 +666,7 @@ function get_html_split_regex() { . '(?' // Conditional expression follows. . $escaped // Find end of escaped element. . '|' // ...else... - . '[^>]*>?' // Find end of normal element. + . '[^>\t\n\f\r \/]*+(?:[^>=]++|=[ \t\f\r\n]*+(?:"[^"]*+"|\'[^\']*+\')?)*+>?' // Find end of normal element, skipping quoted attribute values after the tag name. . ')' . ')/'; // phpcs:enable @@ -704,7 +704,7 @@ function _get_wptexturize_split_regex( $shortcode_regex = '' ) { . '(?(?=!--)' // Is this a comment? . $comment_regex // Find end of comment. . '|' - . '[^>]*>?' // Find end of element. If not found, match all input. + . '[^>\t\n\f\r \/]*+(?:[^>=]++|=[ \t\f\r\n]*+(?:"[^"]*+"|\'[^\']*+\')?)*+>?' // Find end of element, skipping quoted attribute values after the tag name. If not found, match all input. . ')'; // phpcs:enable } diff --git a/tests/phpunit/tests/formatting/wpHtmlSplit.php b/tests/phpunit/tests/formatting/wpHtmlSplit.php index 5635b3a9a3b27..e1a885b88299b 100644 --- a/tests/phpunit/tests/formatting/wpHtmlSplit.php +++ b/tests/phpunit/tests/formatting/wpHtmlSplit.php @@ -37,6 +37,100 @@ public static function data_basic_features() { ); } + /** + * Ensures that a ">" inside a quoted attribute value does not end the tag. + * + * @ticket 63997 + * @ticket 57381 + * @ticket 63426 + * + * @dataProvider data_gt_in_quoted_attribute_value + * + * @param string $input HTML to split. + * @param string[] $output Expected split. + */ + public function test_gt_in_quoted_attribute_value( string $input, array $output ): void { + $this->assertSame( $output, wp_html_split( $input ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public static function data_gt_in_quoted_attribute_value(): array { + return array( + 'Double-quoted value' => array( + '
text
', + array( '', '', 'text', '
', '' ), + ), + 'Single-quoted value' => array( + "text
", + array( '', "", 'text', '
', '' ), + ), + 'Several values, one containing >' => array( + 'text
', + array( '', '', 'text', '
', '' ), + ), + 'Ticket 63997: template attribute' => array( + 'Hi
\nIt's here", + array( '', '', 'Hi', '
', "\n", "", "It's here", '', '' ), + ), + 'Stray quote after an unquoted value' => array( + 'hello', + array( '', '', 'hello', '', '' ), + ), + 'Unclosed tag matches the rest of the input' => array( + "Truncated ', 'Truncated ', " array(
+ ' x <=' y is not a tag and this isn't an attribute ', 'x ', "<=' y is not a tag ', "and this isn't an attribute", ' 0 ? "yes" : "no"\'>It\'s text 0 ? "yes" : "no"\'>It’s text 0 ? 'yes' : 'no'\">It's text 0 ? 'yes' : 'no'\">It’s text Hi Hi x <=' y is not a tag and this isn't an attribute x <=' y is not a tag and this isn’t an attribute