diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index d860784356..b4ea8bd0fc 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -751,13 +751,24 @@ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = fals $quote_style = ENT_NOQUOTES; } - if ( ! $double_encode ) { - // Guarantee every &entity; is valid, convert &garbage; into &garbage; - // This is required for PHP < 5.4.0 because ENT_HTML401 flag is unavailable. - $string = wp_kses_normalize_entities( $string ); - } + // Handle double encoding ourselves + if ( $double_encode ) { + $string = @htmlspecialchars( $string, $quote_style, $charset ); + } else { + // Decode & into & + $string = wp_specialchars_decode( $string, $_quote_style ); - $string = @htmlspecialchars( $string, $quote_style, $charset, $double_encode ); + // Guarantee every &entity; is valid or re-encode the & + $string = wp_kses_normalize_entities( $string ); + + // Now re-encode everything except &entity; + $string = preg_split( '/(&#?x?[0-9a-z]+;)/i', $string, -1, PREG_SPLIT_DELIM_CAPTURE ); + + for ( $i = 0, $c = count( $string ); $i < $c; $i += 2 ) { + $string[$i] = @htmlspecialchars( $string[$i], $quote_style, $charset ); + } + $string = implode( '', $string ); + } // Backwards compatibility if ( 'single' === $_quote_style ) diff --git a/tests/phpunit/tests/formatting/EscAttr.php b/tests/phpunit/tests/formatting/EscAttr.php index 55ca837d05..35ea2f54a5 100644 --- a/tests/phpunit/tests/formatting/EscAttr.php +++ b/tests/phpunit/tests/formatting/EscAttr.php @@ -26,7 +26,7 @@ class Tests_Formatting_EscAttr extends WP_UnitTestCase { } function test_esc_attr_amp() { - $out = esc_attr( 'foo & bar &baz;  ' ); - $this->assertEquals( "foo & bar &baz;  ", $out ); + $out = esc_attr( 'foo & bar &baz; '' ); + $this->assertEquals( "foo & bar &baz; '", $out ); } } diff --git a/tests/phpunit/tests/formatting/EscHtml.php b/tests/phpunit/tests/formatting/EscHtml.php index 14f17dfa8c..101692622e 100644 --- a/tests/phpunit/tests/formatting/EscHtml.php +++ b/tests/phpunit/tests/formatting/EscHtml.php @@ -34,7 +34,7 @@ class Tests_Formatting_EscHtml extends WP_UnitTestCase { function test_ignores_existing_entities() { $source = '& £ " &'; - $res = '& £ " &'; + $res = '& £ " &'; $this->assertEquals( $res, esc_html($source) ); } } diff --git a/tests/phpunit/tests/formatting/JSEscape.php b/tests/phpunit/tests/formatting/JSEscape.php index 286bf616e2..6ec3892982 100644 --- a/tests/phpunit/tests/formatting/JSEscape.php +++ b/tests/phpunit/tests/formatting/JSEscape.php @@ -23,13 +23,13 @@ class Tests_Formatting_JSEscape extends WP_UnitTestCase { } function test_js_escape_amp() { - $out = esc_js('foo & bar &baz;  '); - $this->assertEquals("foo & bar &baz;  ", $out); + $out = esc_js('foo & bar &baz; ''); + $this->assertEquals("foo & bar &baz; '", $out); } function test_js_escape_quote_entity() { $out = esc_js('foo ' bar ' baz &'); - $this->assertEquals("foo \\' bar \\' baz &", $out); + $this->assertEquals("foo \\' bar \\' baz &", $out); } function test_js_no_carriage_return() { diff --git a/tests/phpunit/tests/formatting/WPSpecialchars.php b/tests/phpunit/tests/formatting/WPSpecialchars.php index ddae7fdefa..6c8765d4db 100644 --- a/tests/phpunit/tests/formatting/WPSpecialchars.php +++ b/tests/phpunit/tests/formatting/WPSpecialchars.php @@ -17,10 +17,6 @@ class Tests_Formatting_WPSpecialchars extends WP_UnitTestCase { // Allowed entities should be unchanged foreach ( $allowedentitynames as $ent ) { - if ( 'apos' == $ent ) { - // But for some reason, PHP doesn't allow ' - continue; - } $ent = '&' . $ent . ';'; $this->assertEquals( $ent, _wp_specialchars( $ent ) ); } @@ -43,58 +39,4 @@ class Tests_Formatting_WPSpecialchars extends WP_UnitTestCase { $this->assertEquals( '"'hello!'"', _wp_specialchars($source, true) ); $this->assertEquals( $source, _wp_specialchars($source) ); } - - /** - * Check some of the double-encoding features for entity references. - * - * @ticket 17780 - * @dataProvider data_double_encoding - */ - function test_double_encoding( $input, $output ) { - return $this->assertEquals( $output, _wp_specialchars( $input, ENT_NOQUOTES, false, true ) ); - } - - function data_double_encoding() { - return array( - array( - 'This & that, this & that, — " " Ú   " " " " " $ ×', - 'This & that, this &amp; that, &#8212; &quot; &QUOT; &Uacute; &nbsp; &#34; &#034; &#0034; &#x00022; &#x22; &dollar; &times;', - ), - array( - '&& && && &;', - '&& &&amp; &amp;&amp; &amp;;', - ), - array( - '&garbage; &***; &aaaa; &0000; &####; &;;', - '&garbage; &***; &aaaa; &0000; &####; &;;', - ), - ); - } - - /** - * Check some of the double-encoding features for entity references. - * - * @ticket 17780 - * @dataProvider data_no_double_encoding - */ - function test_no_double_encoding( $input, $output ) { - return $this->assertEquals( $output, _wp_specialchars( $input, ENT_NOQUOTES, false, false ) ); - } - - function data_no_double_encoding() { - return array( - array( - 'This & that, this & that, — " " Ú   " " " " " $ ×', - 'This & that, this & that, — " &QUOT; Ú   " " " " " &dollar; ×', - ), - array( - '&& && && &;', - '&& && && &;', - ), - array( - '&garbage; &***; &aaaa; &0000; &####; &;;', - '&garbage; &***; &aaaa; &0000; &####; &;;', - ), - ); - } }