diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index bc150c8a537cb..48300acb3e872 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -249,10 +249,6 @@ function wptexturize( $text, $reset = false ) { continue; } else { // This is an HTML element delimiter. - - // Replace each & with & unless it already looks like an entity. - $curl = preg_replace( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', '&', $curl ); - _wptexturize_pushpop_element( $curl, $no_texturize_tags_stack, $no_texturize_tags ); } } elseif ( '' === trim( $curl ) ) { @@ -657,13 +653,24 @@ function get_html_split_regex() { . $cdata . ')'; + $ignore_attr = + '(?:' + . '[^>"\']*' // Match any characters except >, " or '. + . '(?:' + . '"[^"]*"' // Double-quoted attribute value. + . '|' + . '\'[^\']*\'' // Single-quoted attribute value. + . ')?' + . ')*'; // End of attribute value. + $regex = - '/(' // Capture the entire match. - . '<' // Find start of element. - . '(?' // Conditional expression follows. - . $escaped // Find end of escaped element. - . '|' // ...else... - . '[^>]*>?' // Find end of normal element. + '/(' // Capture the entire match. + . '<' // Find start of element. + . '(?' // Conditional expression follows. + . $escaped // Find end of escaped element. + . '|' // ...else... + . $ignore_attr // Exclude matching within attribute values. + . '[^>]*>?' // Find end of normal element. . ')' . ')/'; // phpcs:enable @@ -696,12 +703,26 @@ function _get_wptexturize_split_regex( $shortcode_regex = '' ) { . ')*+' // Loop possessively. . '(?:-->)?'; // End of comment. If not found, match all input. + /** + * @see https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + */ + $ignore_attr_regex = + '(?:' + . '[^>"\']*' // Match any characters except >, " or '. + . '(?:' + . '"[^"]*"' // Double-quoted attribute value. + . '|' + . '\'[^\']*\'' // Single-quoted attribute value. + . ')?' + . ')*'; // End of attribute value. + $html_regex = // Needs replaced with wp_html_split() per Shortcode API Roadmap. - '<' // Find start of element. - . '(?(?=!--)' // Is this a comment? - . $comment_regex // Find end of comment. + '<' // Find start of element. + . '(?(?=!--)' // Is this a comment? + . $comment_regex // Find end of comment. . '|' - . '[^>]*>?' // Find end of element. If not found, match all input. + . $ignore_attr_regex // Ignore matching of element end within attribute values. + . '[^>]*>?' // Find end of element. 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 750ad3821cc54..bf0ade5c04123 100644 --- a/tests/phpunit/tests/formatting/wpHtmlSplit.php +++ b/tests/phpunit/tests/formatting/wpHtmlSplit.php @@ -34,6 +34,24 @@ public function data_basic_features() { 'abcd ]]> efgh', array( 'abcd ', ' ]]>', ' efgh' ), ), + array( + 'abcd efgh', + array( 'abcd ', '', ' efgh' ), + ), + array( + 'abcd bar\' /> efgh', + array( 'abcd ', 'bar\' />', ' efgh' ), + ), + array( + '
numbers
', + array( + '', + '', + 'numbers', + '
', + '', + ), + ), ); } diff --git a/tests/phpunit/tests/formatting/wpTexturize.php b/tests/phpunit/tests/formatting/wpTexturize.php index 3202db4ba760f..4ff0fa49d08f2 100644 --- a/tests/phpunit/tests/formatting/wpTexturize.php +++ b/tests/phpunit/tests/formatting/wpTexturize.php @@ -1278,11 +1278,11 @@ public function data_tag_avoidance() { ), array( '[ photos by this guy & that guy ]', - '[ photos by this guy & that guy ]', + '[ photos by this guy & that guy ]', ), array( '[photos by this guy & that guy ]', - '[photos by this guy & that guy ]', + '[photos by this guy & that guy ]', ), array( '& ', @@ -2115,4 +2115,47 @@ public function data_whole_posts() { require_once DIR_TESTDATA . '/formatting/whole-posts.php'; return data_whole_posts(); } + + /** + * @ticket 43457 + * @ticket 45387 + * @ticket 57381 + * @dataProvider data_greater_than_in_attribute_value + */ + public function test_greater_than_in_attribute_value( $input, $output ) { + $this->assertSame( $output, wptexturize( $input ) ); + } + + public function data_greater_than_in_attribute_value() { + return array( + array( + ' + + + ', + ' + + + ', + ), + array( + ' + + bar\' /> + ', + ' + + bar\' /> + ', + ), + array( + 'loading...', + 'loading…', + ), + array( + 'Go to WordPress ->
', + 'Go to WordPress ->
', + ), + ); + } }