X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/d3b1ea255664edd2deef17f900a655613d20820d..refs/tags/wordpress-4.2.3:/wp-includes/formatting.php
diff --git a/wp-includes/formatting.php b/wp-includes/formatting.php
index 26bfc694..08c367c6 100644
--- a/wp-includes/formatting.php
+++ b/wp-includes/formatting.php
@@ -1,180 +1,653 @@
- * 'cause today's effort makes it worth tomorrow's "holiday"...
- *
+ *
+ * 'cause today's effort makes it worth tomorrow's "holiday" ...
+ *
* Becomes:
- *
- * ’cause today’s effort makes it worth tomorrow’s “holiday”…
- *
+ *
+ * ’cause today’s effort makes it worth tomorrow’s “holiday” …
+ *
* Code within certain html blocks are skipped.
*
* @since 0.71
* @uses $wp_cockneyreplace Array of formatted entities for certain common phrases
*
* @param string $text The text to be formatted
+ * @param bool $reset Set to true for unit testing. Translated patterns will reset.
* @return string The string replaced with html entities
*/
-function wptexturize($text) {
- global $wp_cockneyreplace;
- $output = '';
- $curl = '';
- $textarr = preg_split('/(<.*>|\[.*\])/Us', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
- $stop = count($textarr);
-
- /* translators: opening curly quote */
- $opening_quote = _x('“', 'opening curly quote');
- /* translators: closing curly quote */
- $closing_quote = _x('”', 'closing curly quote');
-
- $no_texturize_tags = apply_filters('no_texturize_tags', array('pre', 'code', 'kbd', 'style', 'script', 'tt'));
- $no_texturize_shortcodes = apply_filters('no_texturize_shortcodes', array('code'));
- $no_texturize_tags_stack = array();
- $no_texturize_shortcodes_stack = array();
+function wptexturize($text, $reset = false) {
+ global $wp_cockneyreplace, $shortcode_tags;
+ static $static_characters, $static_replacements, $dynamic_characters, $dynamic_replacements,
+ $default_no_texturize_tags, $default_no_texturize_shortcodes, $run_texturize = true;
- // if a plugin has provided an autocorrect array, use it
- if ( isset($wp_cockneyreplace) ) {
- $cockney = array_keys($wp_cockneyreplace);
- $cockneyreplace = array_values($wp_cockneyreplace);
- } else {
- $cockney = array("'tain't","'twere","'twas","'tis","'twill","'til","'bout","'nuff","'round","'cause");
- $cockneyreplace = array("’tain’t","’twere","’twas","’tis","’twill","’til","’bout","’nuff","’round","’cause");
+ // If there's nothing to do, just stop.
+ if ( empty( $text ) || false === $run_texturize ) {
+ return $text;
}
- $static_characters = array_merge(array('---', ' -- ', '--', ' - ', 'xn–', '...', '``', '\'s', '\'\'', ' (tm)'), $cockney);
- $static_replacements = array_merge(array('—', ' — ', '–', ' – ', 'xn--', '…', $opening_quote, '’s', $closing_quote, ' ™'), $cockneyreplace);
-
- $dynamic_characters = array('/\'(\d\d(?:’|\')?s)/', '/(\s|\A|")\'/', '/(\d+)"/', '/(\d+)\'/', '/(\S)\'([^\'\s])/', '/(\s|\A)"(?!\s)/', '/"(\s|\S|\Z)/', '/\'([\s.]|\Z)/', '/(\d+)x(\d+)/');
- $dynamic_replacements = array('’$1','$1‘', '$1″', '$1′', '$1’$2', '$1' . $opening_quote . '$2', $closing_quote . '$1', '’$1', '$1×$2');
+ // Set up static variables. Run once only.
+ if ( $reset || ! isset( $static_characters ) ) {
+ /**
+ * Filter whether to skip running wptexturize().
+ *
+ * Passing false to the filter will effectively short-circuit wptexturize().
+ * returning the original text passed to the function instead.
+ *
+ * The filter runs only once, the first time wptexturize() is called.
+ *
+ * @since 4.0.0
+ *
+ * @see wptexturize()
+ *
+ * @param bool $run_texturize Whether to short-circuit wptexturize().
+ */
+ $run_texturize = apply_filters( 'run_wptexturize', $run_texturize );
+ if ( false === $run_texturize ) {
+ return $text;
+ }
- for ( $i = 0; $i < $stop; $i++ ) {
- $curl = $textarr[$i];
-
- if ( !empty($curl) && '<' != $curl{0} && '[' != $curl{0}
- && empty($no_texturize_shortcodes_stack) && empty($no_texturize_tags_stack)) { // If it's not a tag
- // static strings
- $curl = str_replace($static_characters, $static_replacements, $curl);
- // regular expressions
- $curl = preg_replace($dynamic_characters, $dynamic_replacements, $curl);
+ /* translators: opening curly double quote */
+ $opening_quote = _x( '“', 'opening curly double quote' );
+ /* translators: closing curly double quote */
+ $closing_quote = _x( '”', 'closing curly double quote' );
+
+ /* translators: apostrophe, for example in 'cause or can't */
+ $apos = _x( '’', 'apostrophe' );
+
+ /* translators: prime, for example in 9' (nine feet) */
+ $prime = _x( '′', 'prime' );
+ /* translators: double prime, for example in 9" (nine inches) */
+ $double_prime = _x( '″', 'double prime' );
+
+ /* translators: opening curly single quote */
+ $opening_single_quote = _x( '‘', 'opening curly single quote' );
+ /* translators: closing curly single quote */
+ $closing_single_quote = _x( '’', 'closing curly single quote' );
+
+ /* translators: en dash */
+ $en_dash = _x( '–', 'en dash' );
+ /* translators: em dash */
+ $em_dash = _x( '—', 'em dash' );
+
+ $default_no_texturize_tags = array('pre', 'code', 'kbd', 'style', 'script', 'tt');
+ $default_no_texturize_shortcodes = array('code');
+
+ // if a plugin has provided an autocorrect array, use it
+ if ( isset($wp_cockneyreplace) ) {
+ $cockney = array_keys( $wp_cockneyreplace );
+ $cockneyreplace = array_values( $wp_cockneyreplace );
+ } elseif ( "'" != $apos ) { // Only bother if we're doing a replacement.
+ $cockney = array( "'tain't", "'twere", "'twas", "'tis", "'twill", "'til", "'bout", "'nuff", "'round", "'cause", "'em" );
+ $cockneyreplace = array( $apos . "tain" . $apos . "t", $apos . "twere", $apos . "twas", $apos . "tis", $apos . "twill", $apos . "til", $apos . "bout", $apos . "nuff", $apos . "round", $apos . "cause", $apos . "em" );
} else {
- wptexturize_pushpop_element($curl, $no_texturize_tags_stack, $no_texturize_tags, '<', '>');
- wptexturize_pushpop_element($curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes, '[', ']');
+ $cockney = $cockneyreplace = array();
+ }
+
+ $static_characters = array_merge( array( '...', '``', '\'\'', ' (tm)' ), $cockney );
+ $static_replacements = array_merge( array( '…', $opening_quote, $closing_quote, ' ™' ), $cockneyreplace );
+
+
+ // Pattern-based replacements of characters.
+ // Sort the remaining patterns into several arrays for performance tuning.
+ $dynamic_characters = array( 'apos' => array(), 'quote' => array(), 'dash' => array() );
+ $dynamic_replacements = array( 'apos' => array(), 'quote' => array(), 'dash' => array() );
+ $dynamic = array();
+ $spaces = wp_spaces_regexp();
+
+ // '99' and '99" are ambiguous among other patterns; assume it's an abbreviated year at the end of a quotation.
+ if ( "'" !== $apos || "'" !== $closing_single_quote ) {
+ $dynamic[ '/\'(\d\d)\'(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos . '$1' . $closing_single_quote;
+ }
+ if ( "'" !== $apos || '"' !== $closing_quote ) {
+ $dynamic[ '/\'(\d\d)"(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos . '$1' . $closing_quote;
+ }
+
+ // '99 '99s '99's (apostrophe) But never '9 or '99% or '999 or '99.0.
+ if ( "'" !== $apos ) {
+ $dynamic[ '/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/' ] = $apos;
+ }
+
+ // Quoted Numbers like '0.42'
+ if ( "'" !== $opening_single_quote && "'" !== $closing_single_quote ) {
+ $dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $opening_single_quote . '$1' . $closing_single_quote;
+ }
+
+ // Single quote at start, or preceded by (, {, <, [, ", -, or spaces.
+ if ( "'" !== $opening_single_quote ) {
+ $dynamic[ '/(?<=\A|[([{"\-]|<|' . $spaces . ')\'/' ] = $opening_single_quote;
+ }
+
+ // Apostrophe in a word. No spaces, double apostrophes, or other punctuation.
+ if ( "'" !== $apos ) {
+ $dynamic[ '/(? is found.
+ . '-(?!->)' // Dash not followed by end of comment.
+ . '[^\-]*+' // Consume non-dashes.
+ . ')*+' // Loop possessively.
+ . '(?:-->)?'; // End of comment. If not found, match all input.
+
+ $shortcode_regex =
+ '\[' // Find start of shortcode.
+ . '[\/\[]?' // Shortcodes may begin with [/ or [[
+ . $tagregexp // Only match registered shortcodes, because performance.
+ . '(?:'
+ . '[^\[\]<>]+' // Shortcodes do not contain other shortcodes. Quantifier critical.
+ . '|'
+ . '<[^\[\]>]*>' // HTML elements permitted. Prevents matching ] before >.
+ . ')*+' // Possessive critical.
+ . '\]' // Find end of shortcode.
+ . '\]?'; // Shortcodes may end with ]]
+
+ $regex =
+ '/(' // Capture the entire match.
+ . '<' // Find start of element.
+ . '(?(?=!--)' // Is this a comment?
+ . $comment_regex // Find end of comment.
+ . '|'
+ . '[^>]*>' // Find end of element.
+ . ')'
+ . '|'
+ . $shortcode_regex // Find shortcodes.
+ . ')/s';
+
+ $textarr = preg_split( $regex, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
+
+ foreach ( $textarr as &$curl ) {
+ // Only call _wptexturize_pushpop_element if $curl is a delimiter.
+ $first = $curl[0];
+ if ( '<' === $first && ' is found.
+ . '-(?!->)' // Dash not followed by end of comment.
+ . '[^\-]*+' // Consume non-dashes.
+ . ')*+' // Loop possessively.
+ . '(?:-->)?'; // End of comment. If not found, match all input.
+
+ $regex =
+ '/(' // Capture the entire match.
+ . '<' // Find start of element.
+ . '(?(?=!--)' // Is this a comment?
+ . $comments // Find end of comment.
+ . '|'
+ . '[^>]*>?' // Find end of element. If not found, match all input.
+ . ')'
+ . ')/s';
+
+ $textarr = preg_split( $regex, $haystack, -1, PREG_SPLIT_DELIM_CAPTURE );
+ $changed = false;
+
+ // Optimize when searching for one item.
+ if ( 1 === count( $replace_pairs ) ) {
+ // Extract $needle and $replace.
+ foreach ( $replace_pairs as $needle => $replace );
+
+ // Loop through delimeters (elements) only.
+ for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
+ if ( false !== strpos( $textarr[$i], $needle ) ) {
+ $textarr[$i] = str_replace( $needle, $replace, $textarr[$i] );
+ $changed = true;
+ }
+ }
+ } else {
+ // Extract all $needles.
+ $needles = array_keys( $replace_pairs );
+
+ // Loop through delimeters (elements) only.
+ for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
+ foreach ( $needles as $needle ) {
+ if ( false !== strpos( $textarr[$i], $needle ) ) {
+ $textarr[$i] = strtr( $textarr[$i], $replace_pairs );
+ $changed = true;
+ // After one strtr() break out of the foreach loop and look at next element.
+ break;
+ }
+ }
+ }
+ }
+
+ if ( $changed ) {
+ $haystack = implode( $textarr );
+ }
+
+ return $haystack;
+}
+
+/**
+ * Newline preservation help function for wpautop
+ *
+ * @since 3.1.0
+ * @access private
+ *
+ * @param array $matches preg_replace_callback matches array
+ * @return string
+ */
+function _autop_newline_preservation_helper( $matches ) {
+ return str_replace("\n", "
...
`. + * + * @since 2.9.0 + * + * @param string $pee The content. + * @return string The filtered content. + */ +function shortcode_unautop( $pee ) { + global $shortcode_tags; + + if ( empty( $shortcode_tags ) || !is_array( $shortcode_tags ) ) { + return $pee; + } + + $tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) ); + $spaces = wp_spaces_regexp(); + + $pattern = + '/' + . '' // Opening paragraph + . '(?:' . $spaces . ')*+' // Optional leading whitespace + . '(' // 1: The shortcode + . '\\[' // Opening bracket + . "($tagregexp)" // 2: Shortcode name + . '(?![\\w-])' // Not followed by word character or hyphen + // Unroll the loop: Inside the opening shortcode tag + . '[^\\]\\/]*' // Not a closing bracket or forward slash + . '(?:' + . '\\/(?!\\])' // A forward slash not followed by a closing bracket + . '[^\\]\\/]*' // Not a closing bracket or forward slash + . ')*?' + . '(?:' + . '\\/\\]' // Self closing tag and closing bracket + . '|' + . '\\]' // Closing bracket + . '(?:' // Unroll the loop: Optionally, anything between the opening and closing shortcode tags + . '[^\\[]*+' // Not an opening bracket + . '(?:' + . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag + . '[^\\[]*+' // Not an opening bracket + . ')*+' + . '\\[\\/\\2\\]' // Closing shortcode tag + . ')?' + . ')' + . ')' + . '(?:' . $spaces . ')*+' // optional trailing whitespace + . '<\\/p>' // closing paragraph + . '/s'; + + return preg_replace( $pattern, '$1', $pee ); +} + /** * Checks to see if a string is utf8 encoded. * @@ -188,17 +661,19 @@ function wpautop($pee, $br = 1) { * @return bool True if $str fits a UTF-8 model, false otherwise. */ function seems_utf8($str) { + mbstring_binary_safe_encoding(); $length = strlen($str); + reset_mbstring_encoding(); for ($i=0; $i < $length; $i++) { $c = ord($str[$i]); - if ($c < 0x80) $n = 0; # 0bbbbbbb - elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb - elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb - elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb - elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb - elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b - else return false; # Does not match any model - for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ? + if ($c < 0x80) $n = 0; // 0bbbbbbb + elseif (($c & 0xE0) == 0xC0) $n=1; // 110bbbbb + elseif (($c & 0xF0) == 0xE0) $n=2; // 1110bbbb + elseif (($c & 0xF8) == 0xF0) $n=3; // 11110bbb + elseif (($c & 0xFC) == 0xF8) $n=4; // 111110bb + elseif (($c & 0xFE) == 0xFC) $n=5; // 1111110b + else return false; // Does not match any model + for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ? if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) return false; } @@ -215,44 +690,42 @@ function seems_utf8($str) { * ", or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded. * * @since 1.2.2 + * @access private * * @param string $string The text which is to be encoded. - * @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES. + * @param int $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES. * @param string $charset Optional. The character encoding of the string. Default is false. - * @param boolean $double_encode Optional. Whether or not to encode existing html entities. Default is false. + * @param boolean $double_encode Optional. Whether to encode existing html entities. Default is false. * @return string The encoded text with HTML entities. */ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) { $string = (string) $string; - if ( 0 === strlen( $string ) ) { + if ( 0 === strlen( $string ) ) return ''; - } // Don't bother if there are no specialchars - saves some processing - if ( !preg_match( '/[&<>"\']/', $string ) ) { + if ( ! preg_match( '/[&<>"\']/', $string ) ) return $string; - } // Account for the previous behaviour of the function when the $quote_style is not an accepted value - if ( empty( $quote_style ) ) { + if ( empty( $quote_style ) ) $quote_style = ENT_NOQUOTES; - } elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) { + elseif ( ! in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) $quote_style = ENT_QUOTES; - } // Store the site charset as a static to avoid multiple calls to wp_load_alloptions() - if ( !$charset ) { + if ( ! $charset ) { static $_charset; - if ( !isset( $_charset ) ) { + if ( ! isset( $_charset ) ) { $alloptions = wp_load_alloptions(); $_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : ''; } $charset = $_charset; } - if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) ) { + + if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) ) $charset = 'UTF-8'; - } $_quote_style = $quote_style; @@ -264,22 +737,27 @@ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = fals } // Handle double encoding ourselves - if ( !$double_encode ) { + if ( $double_encode ) { + $string = @htmlspecialchars( $string, $quote_style, $charset ); + } else { + // Decode & into & $string = wp_specialchars_decode( $string, $_quote_style ); - $string = preg_replace( '/&(#?x?[0-9a-z]+);/i', '|wp_entity|$1|/wp_entity|', $string ); - } - $string = @htmlspecialchars( $string, $quote_style, $charset ); + // Guarantee every &entity; is valid or re-encode the & + $string = wp_kses_normalize_entities( $string ); - // Handle double encoding ourselves - if ( !$double_encode ) { - $string = str_replace( array( '|wp_entity|', '|/wp_entity|' ), array( '&', ';' ), $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 ) { + if ( 'single' === $_quote_style ) $string = str_replace( "'", ''', $string ); - } return $string; } @@ -292,7 +770,7 @@ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = fals * $quote_style can be set to ENT_COMPAT to decode " entities, * or ENT_QUOTES to do both " and '. Default is ENT_NOQUOTES where no quotes are decoded. * - * @since 2.8 + * @since 2.8.0 * * @param string $string The text which is to be decoded. * @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old _wp_specialchars() values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES. @@ -349,7 +827,7 @@ function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) { /** * Checks for invalid UTF8 in a string. * - * @since 2.8 + * @since 2.8.0 * * @param string $string The text which is to be checked. * @param boolean $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false. @@ -409,7 +887,10 @@ function utf8_uri_encode( $utf8_string, $length = 0 ) { $num_octets = 1; $unicode_length = 0; + mbstring_binary_safe_encoding(); $string_length = strlen( $utf8_string ); + reset_mbstring_encoding(); + for ($i = 0; $i < $string_length; $i++ ) { $value = ord( $utf8_string[ $i ] ); @@ -420,21 +901,27 @@ function utf8_uri_encode( $utf8_string, $length = 0 ) { $unicode .= chr($value); $unicode_length++; } else { - if ( count( $values ) == 0 ) $num_octets = ( $value < 224 ) ? 2 : 3; + if ( count( $values ) == 0 ) { + if ( $value < 224 ) { + $num_octets = 2; + } elseif ( $value < 240 ) { + $num_octets = 3; + } else { + $num_octets = 4; + } + } $values[] = $value; if ( $length && ( $unicode_length + ($num_octets * 3) ) > $length ) break; if ( count( $values ) == $num_octets ) { - if ($num_octets == 3) { - $unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]) . '%' . dechex($values[2]); - $unicode_length += 9; - } else { - $unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]); - $unicode_length += 6; + for ( $j = 0; $j < $num_octets; $j++ ) { + $unicode .= '%' . dechex( $values[ $j ] ); } + $unicode_length += $num_octets * 3; + $values = array(); $num_octets = 1; } @@ -461,34 +948,38 @@ function remove_accents($string) { if (seems_utf8($string)) { $chars = array( // Decompositions for Latin-1 Supplement + chr(194).chr(170) => 'a', chr(194).chr(186) => 'o', chr(195).chr(128) => 'A', chr(195).chr(129) => 'A', chr(195).chr(130) => 'A', chr(195).chr(131) => 'A', chr(195).chr(132) => 'A', chr(195).chr(133) => 'A', - chr(195).chr(135) => 'C', chr(195).chr(136) => 'E', - chr(195).chr(137) => 'E', chr(195).chr(138) => 'E', - chr(195).chr(139) => 'E', chr(195).chr(140) => 'I', - chr(195).chr(141) => 'I', chr(195).chr(142) => 'I', - chr(195).chr(143) => 'I', chr(195).chr(145) => 'N', + chr(195).chr(134) => 'AE',chr(195).chr(135) => 'C', + chr(195).chr(136) => 'E', chr(195).chr(137) => 'E', + chr(195).chr(138) => 'E', chr(195).chr(139) => 'E', + chr(195).chr(140) => 'I', chr(195).chr(141) => 'I', + chr(195).chr(142) => 'I', chr(195).chr(143) => 'I', + chr(195).chr(144) => 'D', chr(195).chr(145) => 'N', chr(195).chr(146) => 'O', chr(195).chr(147) => 'O', chr(195).chr(148) => 'O', chr(195).chr(149) => 'O', chr(195).chr(150) => 'O', chr(195).chr(153) => 'U', chr(195).chr(154) => 'U', chr(195).chr(155) => 'U', chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y', - chr(195).chr(159) => 's', chr(195).chr(160) => 'a', - chr(195).chr(161) => 'a', chr(195).chr(162) => 'a', - chr(195).chr(163) => 'a', chr(195).chr(164) => 'a', - chr(195).chr(165) => 'a', chr(195).chr(167) => 'c', + chr(195).chr(158) => 'TH',chr(195).chr(159) => 's', + chr(195).chr(160) => 'a', chr(195).chr(161) => 'a', + chr(195).chr(162) => 'a', chr(195).chr(163) => 'a', + chr(195).chr(164) => 'a', chr(195).chr(165) => 'a', + chr(195).chr(166) => 'ae',chr(195).chr(167) => 'c', chr(195).chr(168) => 'e', chr(195).chr(169) => 'e', chr(195).chr(170) => 'e', chr(195).chr(171) => 'e', chr(195).chr(172) => 'i', chr(195).chr(173) => 'i', chr(195).chr(174) => 'i', chr(195).chr(175) => 'i', - chr(195).chr(177) => 'n', chr(195).chr(178) => 'o', - chr(195).chr(179) => 'o', chr(195).chr(180) => 'o', - chr(195).chr(181) => 'o', chr(195).chr(182) => 'o', - chr(195).chr(182) => 'o', chr(195).chr(185) => 'u', - chr(195).chr(186) => 'u', chr(195).chr(187) => 'u', - chr(195).chr(188) => 'u', chr(195).chr(189) => 'y', - chr(195).chr(191) => 'y', + chr(195).chr(176) => 'd', chr(195).chr(177) => 'n', + chr(195).chr(178) => 'o', chr(195).chr(179) => 'o', + chr(195).chr(180) => 'o', chr(195).chr(181) => 'o', + chr(195).chr(182) => 'o', chr(195).chr(184) => 'o', + chr(195).chr(185) => 'u', chr(195).chr(186) => 'u', + chr(195).chr(187) => 'u', chr(195).chr(188) => 'u', + chr(195).chr(189) => 'y', chr(195).chr(190) => 'th', + chr(195).chr(191) => 'y', chr(195).chr(152) => 'O', // Decompositions for Latin Extended-A chr(196).chr(128) => 'A', chr(196).chr(129) => 'a', chr(196).chr(130) => 'A', chr(196).chr(131) => 'a', @@ -554,13 +1045,106 @@ function remove_accents($string) { chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z', chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z', chr(197).chr(190) => 'z', chr(197).chr(191) => 's', + // Decompositions for Latin Extended-B + chr(200).chr(152) => 'S', chr(200).chr(153) => 's', + chr(200).chr(154) => 'T', chr(200).chr(155) => 't', // Euro Sign chr(226).chr(130).chr(172) => 'E', // GBP (Pound) Sign - chr(194).chr(163) => ''); + chr(194).chr(163) => '', + // Vowels with diacritic (Vietnamese) + // unmarked + chr(198).chr(160) => 'O', chr(198).chr(161) => 'o', + chr(198).chr(175) => 'U', chr(198).chr(176) => 'u', + // grave accent + chr(225).chr(186).chr(166) => 'A', chr(225).chr(186).chr(167) => 'a', + chr(225).chr(186).chr(176) => 'A', chr(225).chr(186).chr(177) => 'a', + chr(225).chr(187).chr(128) => 'E', chr(225).chr(187).chr(129) => 'e', + chr(225).chr(187).chr(146) => 'O', chr(225).chr(187).chr(147) => 'o', + chr(225).chr(187).chr(156) => 'O', chr(225).chr(187).chr(157) => 'o', + chr(225).chr(187).chr(170) => 'U', chr(225).chr(187).chr(171) => 'u', + chr(225).chr(187).chr(178) => 'Y', chr(225).chr(187).chr(179) => 'y', + // hook + chr(225).chr(186).chr(162) => 'A', chr(225).chr(186).chr(163) => 'a', + chr(225).chr(186).chr(168) => 'A', chr(225).chr(186).chr(169) => 'a', + chr(225).chr(186).chr(178) => 'A', chr(225).chr(186).chr(179) => 'a', + chr(225).chr(186).chr(186) => 'E', chr(225).chr(186).chr(187) => 'e', + chr(225).chr(187).chr(130) => 'E', chr(225).chr(187).chr(131) => 'e', + chr(225).chr(187).chr(136) => 'I', chr(225).chr(187).chr(137) => 'i', + chr(225).chr(187).chr(142) => 'O', chr(225).chr(187).chr(143) => 'o', + chr(225).chr(187).chr(148) => 'O', chr(225).chr(187).chr(149) => 'o', + chr(225).chr(187).chr(158) => 'O', chr(225).chr(187).chr(159) => 'o', + chr(225).chr(187).chr(166) => 'U', chr(225).chr(187).chr(167) => 'u', + chr(225).chr(187).chr(172) => 'U', chr(225).chr(187).chr(173) => 'u', + chr(225).chr(187).chr(182) => 'Y', chr(225).chr(187).chr(183) => 'y', + // tilde + chr(225).chr(186).chr(170) => 'A', chr(225).chr(186).chr(171) => 'a', + chr(225).chr(186).chr(180) => 'A', chr(225).chr(186).chr(181) => 'a', + chr(225).chr(186).chr(188) => 'E', chr(225).chr(186).chr(189) => 'e', + chr(225).chr(187).chr(132) => 'E', chr(225).chr(187).chr(133) => 'e', + chr(225).chr(187).chr(150) => 'O', chr(225).chr(187).chr(151) => 'o', + chr(225).chr(187).chr(160) => 'O', chr(225).chr(187).chr(161) => 'o', + chr(225).chr(187).chr(174) => 'U', chr(225).chr(187).chr(175) => 'u', + chr(225).chr(187).chr(184) => 'Y', chr(225).chr(187).chr(185) => 'y', + // acute accent + chr(225).chr(186).chr(164) => 'A', chr(225).chr(186).chr(165) => 'a', + chr(225).chr(186).chr(174) => 'A', chr(225).chr(186).chr(175) => 'a', + chr(225).chr(186).chr(190) => 'E', chr(225).chr(186).chr(191) => 'e', + chr(225).chr(187).chr(144) => 'O', chr(225).chr(187).chr(145) => 'o', + chr(225).chr(187).chr(154) => 'O', chr(225).chr(187).chr(155) => 'o', + chr(225).chr(187).chr(168) => 'U', chr(225).chr(187).chr(169) => 'u', + // dot below + chr(225).chr(186).chr(160) => 'A', chr(225).chr(186).chr(161) => 'a', + chr(225).chr(186).chr(172) => 'A', chr(225).chr(186).chr(173) => 'a', + chr(225).chr(186).chr(182) => 'A', chr(225).chr(186).chr(183) => 'a', + chr(225).chr(186).chr(184) => 'E', chr(225).chr(186).chr(185) => 'e', + chr(225).chr(187).chr(134) => 'E', chr(225).chr(187).chr(135) => 'e', + chr(225).chr(187).chr(138) => 'I', chr(225).chr(187).chr(139) => 'i', + chr(225).chr(187).chr(140) => 'O', chr(225).chr(187).chr(141) => 'o', + chr(225).chr(187).chr(152) => 'O', chr(225).chr(187).chr(153) => 'o', + chr(225).chr(187).chr(162) => 'O', chr(225).chr(187).chr(163) => 'o', + chr(225).chr(187).chr(164) => 'U', chr(225).chr(187).chr(165) => 'u', + chr(225).chr(187).chr(176) => 'U', chr(225).chr(187).chr(177) => 'u', + chr(225).chr(187).chr(180) => 'Y', chr(225).chr(187).chr(181) => 'y', + // Vowels with diacritic (Chinese, Hanyu Pinyin) + chr(201).chr(145) => 'a', + // macron + chr(199).chr(149) => 'U', chr(199).chr(150) => 'u', + // acute accent + chr(199).chr(151) => 'U', chr(199).chr(152) => 'u', + // caron + chr(199).chr(141) => 'A', chr(199).chr(142) => 'a', + chr(199).chr(143) => 'I', chr(199).chr(144) => 'i', + chr(199).chr(145) => 'O', chr(199).chr(146) => 'o', + chr(199).chr(147) => 'U', chr(199).chr(148) => 'u', + chr(199).chr(153) => 'U', chr(199).chr(154) => 'u', + // grave accent + chr(199).chr(155) => 'U', chr(199).chr(156) => 'u', + ); + + // Used for locale-specific rules + $locale = get_locale(); + + if ( 'de_DE' == $locale ) { + $chars[ chr(195).chr(132) ] = 'Ae'; + $chars[ chr(195).chr(164) ] = 'ae'; + $chars[ chr(195).chr(150) ] = 'Oe'; + $chars[ chr(195).chr(182) ] = 'oe'; + $chars[ chr(195).chr(156) ] = 'Ue'; + $chars[ chr(195).chr(188) ] = 'ue'; + $chars[ chr(195).chr(159) ] = 'ss'; + } elseif ( 'da_DK' === $locale ) { + $chars[ chr(195).chr(134) ] = 'Ae'; + $chars[ chr(195).chr(166) ] = 'ae'; + $chars[ chr(195).chr(152) ] = 'Oe'; + $chars[ chr(195).chr(184) ] = 'oe'; + $chars[ chr(195).chr(133) ] = 'Aa'; + $chars[ chr(195).chr(165) ] = 'aa'; + } $string = strtr($string, $chars); } else { + $chars = array(); // Assume ISO-8859-1 if not UTF-8 $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158) .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194) @@ -576,6 +1160,7 @@ function remove_accents($string) { $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy"; $string = strtr($string, $chars['in'], $chars['out']); + $double_chars = array(); $double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254)); $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'); $string = str_replace($double_chars['in'], $double_chars['out'], $string); @@ -585,12 +1170,12 @@ function remove_accents($string) { } /** - * Sanitizes a filename replacing whitespace with dashes + * Sanitizes a filename, replacing whitespace with dashes. * * Removes special characters that are illegal in filenames on certain * operating systems and special characters requiring special escaping * to manipulate at the command line. Replaces spaces and consecutive - * dashes with a single dash. Trim period, dash and underscore from beginning + * dashes with a single dash. Trims period, dash and underscore from beginning * and end of filename. * * @since 2.1.0 @@ -600,27 +1185,77 @@ function remove_accents($string) { */ function sanitize_file_name( $filename ) { $filename_raw = $filename; - $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}"); - $special_chars = apply_filters('sanitize_file_name_chars', $special_chars, $filename_raw); - $filename = str_replace($special_chars, '', $filename); - $filename = preg_replace('/[\s-]+/', '-', $filename); - $filename = trim($filename, '.-_'); + $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", chr(0)); + /** + * Filter the list of characters to remove from a filename. + * + * @since 2.8.0 + * + * @param array $special_chars Characters to remove. + * @param string $filename_raw Filename as it was passed into sanitize_file_name(). + */ + $special_chars = apply_filters( 'sanitize_file_name_chars', $special_chars, $filename_raw ); + $filename = preg_replace( "#\x{00a0}#siu", ' ', $filename ); + $filename = str_replace( $special_chars, '', $filename ); + $filename = str_replace( array( '%20', '+' ), '-', $filename ); + $filename = preg_replace( '/[\r\n\t -]+/', '-', $filename ); + $filename = trim( $filename, '.-_' ); + + // Split the filename into a base and extension[s] + $parts = explode('.', $filename); + + // Return if only one extension + if ( count( $parts ) <= 2 ) { + /** + * Filter a sanitized filename string. + * + * @since 2.8.0 + * + * @param string $filename Sanitized filename. + * @param string $filename_raw The filename prior to sanitization. + */ + return apply_filters( 'sanitize_file_name', $filename, $filename_raw ); + } + + // Process multiple extensions + $filename = array_shift($parts); + $extension = array_pop($parts); + $mimes = get_allowed_mime_types(); + + /* + * Loop over any intermediate extensions. Postfix them with a trailing underscore + * if they are a 2 - 5 character long alpha string not in the extension whitelist. + */ + foreach ( (array) $parts as $part) { + $filename .= '.' . $part; + + if ( preg_match("/^[a-zA-Z]{2,5}\d?$/", $part) ) { + $allowed = false; + foreach ( $mimes as $ext_preg => $mime_match ) { + $ext_preg = '!^(' . $ext_preg . ')$!i'; + if ( preg_match( $ext_preg, $part ) ) { + $allowed = true; + break; + } + } + if ( !$allowed ) + $filename .= '_'; + } + } + $filename .= '.' . $extension; + /** This filter is documented in wp-includes/formatting.php */ return apply_filters('sanitize_file_name', $filename, $filename_raw); } /** - * Sanitize username stripping out unsafe characters. + * Sanitizes a username, stripping out unsafe characters. * - * If $strict is true, only alphanumeric characters (as well as _, space, ., -, - * @) are returned. - * Removes tags, octets, entities, and if strict is enabled, will remove all - * non-ASCII characters. After sanitizing, it passes the username, raw username - * (the username in the parameter), and the strict parameter as parameters for - * the filter. + * Removes tags, octets, entities, and if strict is enabled, will only keep + * alphanumeric, _, space, ., -, @. After sanitizing, it passes the username, + * raw username (the username in the parameter), and the value of $strict as + * parameters for the 'sanitize_user' filter. * * @since 2.0.0 - * @uses apply_filters() Calls 'sanitize_user' hook on username, raw username, - * and $strict parameter. * * @param string $username The username to be sanitized. * @param bool $strict If set limits $username to specific characters. Default false. @@ -628,23 +1263,60 @@ function sanitize_file_name( $filename ) { */ function sanitize_user( $username, $strict = false ) { $raw_username = $username; - $username = strip_tags($username); + $username = wp_strip_all_tags( $username ); + $username = remove_accents( $username ); // Kill octets - $username = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '', $username); - $username = preg_replace('/&.+?;/', '', $username); // Kill entities + $username = preg_replace( '|%([a-fA-F0-9][a-fA-F0-9])|', '', $username ); + $username = preg_replace( '/&.+?;/', '', $username ); // Kill entities // If strict, reduce to ASCII for max portability. if ( $strict ) - $username = preg_replace('|[^a-z0-9 _.\-@]|i', '', $username); + $username = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $username ); + $username = trim( $username ); // Consolidate contiguous whitespace - $username = preg_replace('|\s+|', ' ', $username); + $username = preg_replace( '|\s+|', ' ', $username ); + + /** + * Filter a sanitized username string. + * + * @since 2.0.1 + * + * @param string $username Sanitized username. + * @param string $raw_username The username prior to sanitization. + * @param bool $strict Whether to limit the sanitization to specific characters. Default false. + */ + return apply_filters( 'sanitize_user', $username, $raw_username, $strict ); +} - return apply_filters('sanitize_user', $username, $raw_username, $strict); +/** + * Sanitizes a string key. + * + * Keys are used as internal identifiers. Lowercase alphanumeric characters, dashes and underscores are allowed. + * + * @since 3.0.0 + * + * @param string $key String key + * @return string Sanitized key + */ +function sanitize_key( $key ) { + $raw_key = $key; + $key = strtolower( $key ); + $key = preg_replace( '/[^a-z0-9_\-]/', '', $key ); + + /** + * Filter a sanitized key string. + * + * @since 3.0.0 + * + * @param string $key Sanitized key. + * @param string $raw_key The key prior to sanitization. + */ + return apply_filters( 'sanitize_key', $key, $raw_key ); } /** - * Sanitizes title or use fallback title. + * Sanitizes a title, or returns a fallback title. * * Specifically, HTML and PHP tags are stripped. Further actions can be added * via the plugin API. If $title is empty and $fallback_title is set, the latter @@ -654,12 +1326,25 @@ function sanitize_user( $username, $strict = false ) { * * @param string $title The string to be sanitized. * @param string $fallback_title Optional. A title to use if $title is empty. + * @param string $context Optional. The operation for which the string is sanitized * @return string The sanitized string. */ -function sanitize_title($title, $fallback_title = '') { +function sanitize_title( $title, $fallback_title = '', $context = 'save' ) { $raw_title = $title; - $title = strip_tags($title); - $title = apply_filters('sanitize_title', $title, $raw_title); + + if ( 'save' == $context ) + $title = remove_accents($title); + + /** + * Filter a sanitized title string. + * + * @since 1.2.0 + * + * @param string $title Sanitized title. + * @param string $raw_title The title prior to sanitization. + * @param string $context The context for which the title is being sanitized. + */ + $title = apply_filters( 'sanitize_title', $title, $raw_title, $context ); if ( '' === $title || false === $title ) $title = $fallback_title; @@ -668,7 +1353,21 @@ function sanitize_title($title, $fallback_title = '') { } /** - * Sanitizes title, replacing whitespace with dashes. + * Sanitizes a title with the 'query' context. + * + * Used for querying the database for a value from URL. + * + * @since 3.1.0 + * + * @param string $title The string to be sanitized. + * @return string The sanitized string. + */ +function sanitize_title_for_query( $title ) { + return sanitize_title( $title, '', 'query' ); +} + +/** + * Sanitizes a title, replacing whitespace and a few other characters with dashes. * * Limits the output to alphanumeric characters, underscore (_) and dash (-). * Whitespace becomes a dash. @@ -676,9 +1375,11 @@ function sanitize_title($title, $fallback_title = '') { * @since 1.2.0 * * @param string $title The title to be sanitized. + * @param string $raw_title Optional. Not used. + * @param string $context Optional. The operation for which the string is sanitized. * @return string The sanitized title. */ -function sanitize_title_with_dashes($title) { +function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'display' ) { $title = strip_tags($title); // Preserve escaped octets. $title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title); @@ -687,7 +1388,6 @@ function sanitize_title_with_dashes($title) { // Restore octets. $title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title); - $title = remove_accents($title); if (seems_utf8($title)) { if (function_exists('mb_strtolower')) { $title = mb_strtolower($title, 'UTF-8'); @@ -698,6 +1398,32 @@ function sanitize_title_with_dashes($title) { $title = strtolower($title); $title = preg_replace('/&.+?;/', '', $title); // kill entities $title = str_replace('.', '-', $title); + + if ( 'save' == $context ) { + // Convert nbsp, ndash and mdash to hyphens + $title = str_replace( array( '%c2%a0', '%e2%80%93', '%e2%80%94' ), '-', $title ); + + // Strip these characters entirely + $title = str_replace( array( + // iexcl and iquest + '%c2%a1', '%c2%bf', + // angle quotes + '%c2%ab', '%c2%bb', '%e2%80%b9', '%e2%80%ba', + // curly quotes + '%e2%80%98', '%e2%80%99', '%e2%80%9c', '%e2%80%9d', + '%e2%80%9a', '%e2%80%9b', '%e2%80%9e', '%e2%80%9f', + // copy, reg, deg, hellip and trade + '%c2%a9', '%c2%ae', '%c2%b0', '%e2%80%a6', '%e2%84%a2', + // acute accents + '%c2%b4', '%cb%8a', '%cc%81', '%cd%81', + // grave accent, macron, caron + '%cc%80', '%cc%84', '%cc%8c', + ), '', $title ); + + // Convert times to x + $title = str_replace( '%c3%97', 'x', $title ); + } + $title = preg_replace('/[^%a-z0-9 _-]/', '', $title); $title = preg_replace('/\s+/', '-', $title); $title = preg_replace('|-+|', '-', $title); @@ -707,27 +1433,29 @@ function sanitize_title_with_dashes($title) { } /** - * Ensures a string is a valid SQL order by clause. + * Ensures a string is a valid SQL 'order by' clause. + * + * Accepts one or more columns, with or without a sort order (ASC / DESC). + * e.g. 'column_1', 'column_1, column_2', 'column_1 ASC, column_2 DESC' etc. * - * Accepts one or more columns, with or without ASC/DESC, and also accepts - * RAND(). + * Also accepts 'RAND()'. * * @since 2.5.1 * - * @param string $orderby Order by string to be checked. - * @return string|false Returns the order by clause if it is a match, false otherwise. + * @param string $orderby Order by clause to be validated. + * @return string|bool Returns $orderby if valid, false otherwise. */ -function sanitize_sql_orderby( $orderby ){ - preg_match('/^\s*([a-z0-9_]+(\s+(ASC|DESC))?(\s*,\s*|\s*$))+|^\s*RAND\(\s*\)\s*$/i', $orderby, $obmatches); - if ( !$obmatches ) - return false; - return $orderby; +function sanitize_sql_orderby( $orderby ) { + if ( preg_match( '/^\s*(([a-z0-9_]+|`[a-z0-9_]+`)(\s+(ASC|DESC))?\s*(,\s*(?=[a-z0-9_`])|$))+$/i', $orderby ) || preg_match( '/^\s*RAND\(\s*\)\s*$/i', $orderby ) ) { + return $orderby; + } + return false; } /** - * Santizes a html classname to ensure it only contains valid characters + * Sanitizes an HTML classname to ensure it only contains valid characters. * - * Strips the string down to A-Z,a-z,0-9,'-' if this results in an empty + * Strips the string down to A-Z,a-z,0-9,_,-. If this results in an empty * string then it will return the alternative value supplied. * * @todo Expand to support the full range of CDATA that a class attribute can contain. @@ -735,26 +1463,36 @@ function sanitize_sql_orderby( $orderby ){ * @since 2.8.0 * * @param string $class The classname to be sanitized - * @param string $fallback The value to return if the sanitization end's up as an empty string. + * @param string $fallback Optional. The value to return if the sanitization ends up as an empty string. + * Defaults to an empty string. * @return string The sanitized value */ -function sanitize_html_class($class, $fallback){ +function sanitize_html_class( $class, $fallback = '' ) { //Strip out any % encoded octets - $sanitized = preg_replace('|%[a-fA-F0-9][a-fA-F0-9]|', '', $class); + $sanitized = preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $class ); - //Limit to A-Z,a-z,0-9,'-' - $sanitized = preg_replace('/[^A-Za-z0-9-]/', '', $sanitized); + //Limit to A-Z,a-z,0-9,_,- + $sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $sanitized ); - if ('' == $sanitized) + if ( '' == $sanitized ) $sanitized = $fallback; - return apply_filters('sanitize_html_class',$sanitized, $class, $fallback); + /** + * Filter a sanitized HTML class string. + * + * @since 2.8.0 + * + * @param string $sanitized The sanitized HTML class. + * @param string $class HTML class before sanitization. + * @param string $fallback The fallback string. + */ + return apply_filters( 'sanitize_html_class', $sanitized, $class, $fallback ); } /** * Converts a number of characters from a string. * - * Metadata tags <