| [ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Main WordPress Formatting API. 4 * 5 * Handles many functions for formatting output. 6 * 7 * @package WordPress 8 **/ 9 10 /** 11 * Replaces common plain text characters into formatted entities 12 * 13 * As an example, 14 * <code> 15 * 'cause today's effort makes it worth tomorrow's "holiday"... 16 * </code> 17 * Becomes: 18 * <code> 19 * ’cause today’s effort makes it worth tomorrow’s “holiday”… 20 * </code> 21 * Code within certain html blocks are skipped. 22 * 23 * @since 0.71 24 * @uses $wp_cockneyreplace Array of formatted entities for certain common phrases 25 * 26 * @param string $text The text to be formatted 27 * @return string The string replaced with html entities 28 */ 29 function wptexturize($text) { 30 global $wp_cockneyreplace; 31 static $static_characters, $static_replacements, $dynamic_characters, $dynamic_replacements, 32 $default_no_texturize_tags, $default_no_texturize_shortcodes; 33 34 // No need to set up these static variables more than once 35 if ( ! isset( $static_characters ) ) { 36 /* translators: opening curly double quote */ 37 $opening_quote = _x( '“', 'opening curly double quote' ); 38 /* translators: closing curly double quote */ 39 $closing_quote = _x( '”', 'closing curly double quote' ); 40 41 /* translators: apostrophe, for example in 'cause or can't */ 42 $apos = _x( '’', 'apostrophe' ); 43 44 /* translators: prime, for example in 9' (nine feet) */ 45 $prime = _x( '′', 'prime' ); 46 /* translators: double prime, for example in 9" (nine inches) */ 47 $double_prime = _x( '″', 'double prime' ); 48 49 /* translators: opening curly single quote */ 50 $opening_single_quote = _x( '‘', 'opening curly single quote' ); 51 /* translators: closing curly single quote */ 52 $closing_single_quote = _x( '’', 'closing curly single quote' ); 53 54 /* translators: en dash */ 55 $en_dash = _x( '–', 'en dash' ); 56 /* translators: em dash */ 57 $em_dash = _x( '—', 'em dash' ); 58 59 $default_no_texturize_tags = array('pre', 'code', 'kbd', 'style', 'script', 'tt'); 60 $default_no_texturize_shortcodes = array('code'); 61 62 // if a plugin has provided an autocorrect array, use it 63 if ( isset($wp_cockneyreplace) ) { 64 $cockney = array_keys($wp_cockneyreplace); 65 $cockneyreplace = array_values($wp_cockneyreplace); 66 } elseif ( "'" != $apos ) { // Only bother if we're doing a replacement. 67 $cockney = array( "'tain't", "'twere", "'twas", "'tis", "'twill", "'til", "'bout", "'nuff", "'round", "'cause" ); 68 $cockneyreplace = array( $apos . "tain" . $apos . "t", $apos . "twere", $apos . "twas", $apos . "tis", $apos . "twill", $apos . "til", $apos . "bout", $apos . "nuff", $apos . "round", $apos . "cause" ); 69 } else { 70 $cockney = $cockneyreplace = array(); 71 } 72 73 $static_characters = array_merge( array( '---', ' -- ', '--', ' - ', 'xn–', '...', '``', '\'\'', ' (tm)' ), $cockney ); 74 $static_replacements = array_merge( array( $em_dash, ' ' . $em_dash . ' ', $en_dash, ' ' . $en_dash . ' ', 'xn--', '…', $opening_quote, $closing_quote, ' ™' ), $cockneyreplace ); 75 76 $dynamic = array(); 77 if ( "'" != $apos ) { 78 $dynamic[ '/\'(\d\d(?:’|\')?s)/' ] = $apos . '$1'; // '99's 79 $dynamic[ '/\'(\d)/' ] = $apos . '$1'; // '99 80 } 81 if ( "'" != $opening_single_quote ) 82 $dynamic[ '/(\s|\A|[([{<]|")\'/' ] = '$1' . $opening_single_quote; // opening single quote, even after (, {, <, [ 83 if ( '"' != $double_prime ) 84 $dynamic[ '/(\d)"/' ] = '$1' . $double_prime; // 9" (double prime) 85 if ( "'" != $prime ) 86 $dynamic[ '/(\d)\'/' ] = '$1' . $prime; // 9' (prime) 87 if ( "'" != $apos ) 88 $dynamic[ '/(\S)\'([^\'\s])/' ] = '$1' . $apos . '$2'; // apostrophe in a word 89 if ( '"' != $opening_quote ) 90 $dynamic[ '/(\s|\A|[([{<])"(?!\s)/' ] = '$1' . $opening_quote . '$2'; // opening double quote, even after (, {, <, [ 91 if ( '"' != $closing_quote ) 92 $dynamic[ '/"(\s|\S|\Z)/' ] = $closing_quote . '$1'; // closing double quote 93 if ( "'" != $closing_single_quote ) 94 $dynamic[ '/\'([\s.]|\Z)/' ] = $closing_single_quote . '$2'; // closing single quote 95 96 $dynamic[ '/\b(\d+)x(\d+)\b/' ] = '$1×$2'; // 9x9 (times) 97 98 $dynamic_characters = array_keys( $dynamic ); 99 $dynamic_replacements = array_values( $dynamic ); 100 } 101 102 // Transform into regexp sub-expression used in _wptexturize_pushpop_element 103 // Must do this everytime in case plugins use these filters in a context sensitive manner 104 $no_texturize_tags = '(' . implode('|', apply_filters('no_texturize_tags', $default_no_texturize_tags) ) . ')'; 105 $no_texturize_shortcodes = '(' . implode('|', apply_filters('no_texturize_shortcodes', $default_no_texturize_shortcodes) ) . ')'; 106 107 $no_texturize_tags_stack = array(); 108 $no_texturize_shortcodes_stack = array(); 109 110 $textarr = preg_split('/(<.*>|\[.*\])/Us', $text, -1, PREG_SPLIT_DELIM_CAPTURE); 111 112 foreach ( $textarr as &$curl ) { 113 if ( empty( $curl ) ) 114 continue; 115 116 // Only call _wptexturize_pushpop_element if first char is correct tag opening 117 $first = $curl[0]; 118 if ( '<' === $first ) { 119 _wptexturize_pushpop_element($curl, $no_texturize_tags_stack, $no_texturize_tags, '<', '>'); 120 } elseif ( '[' === $first ) { 121 _wptexturize_pushpop_element($curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes, '[', ']'); 122 } elseif ( empty($no_texturize_shortcodes_stack) && empty($no_texturize_tags_stack) ) { 123 // This is not a tag, nor is the texturization disabled static strings 124 $curl = str_replace($static_characters, $static_replacements, $curl); 125 // regular expressions 126 $curl = preg_replace($dynamic_characters, $dynamic_replacements, $curl); 127 } 128 $curl = preg_replace('/&([^#])(?![a-zA-Z1-4]{1,8};)/', '&$1', $curl); 129 } 130 return implode( '', $textarr ); 131 } 132 133 /** 134 * Search for disabled element tags. Push element to stack on tag open and pop 135 * on tag close. Assumes first character of $text is tag opening. 136 * 137 * @access private 138 * @since 2.9.0 139 * 140 * @param string $text Text to check. First character is assumed to be $opening 141 * @param array $stack Array used as stack of opened tag elements 142 * @param string $disabled_elements Tags to match against formatted as regexp sub-expression 143 * @param string $opening Tag opening character, assumed to be 1 character long 144 * @param string $opening Tag closing character 145 * @return object 146 */ 147 function _wptexturize_pushpop_element($text, &$stack, $disabled_elements, $opening = '<', $closing = '>') { 148 // Check if it is a closing tag -- otherwise assume opening tag 149 if (strncmp($opening . '/', $text, 2)) { 150 // Opening? Check $text+1 against disabled elements 151 if (preg_match('/^' . $disabled_elements . '\b/', substr($text, 1), $matches)) { 152 /* 153 * This disables texturize until we find a closing tag of our type 154 * (e.g. <pre>) even if there was invalid nesting before that 155 * 156 * Example: in the case <pre>sadsadasd</code>"baba"</pre> 157 * "baba" won't be texturize 158 */ 159 160 array_push($stack, $matches[1]); 161 } 162 } else { 163 // Closing? Check $text+2 against disabled elements 164 $c = preg_quote($closing, '/'); 165 if (preg_match('/^' . $disabled_elements . $c . '/', substr($text, 2), $matches)) { 166 $last = array_pop($stack); 167 168 // Make sure it matches the opening tag 169 if ($last != $matches[1]) 170 array_push($stack, $last); 171 } 172 } 173 } 174 175 /** 176 * Accepts matches array from preg_replace_callback in wpautop() or a string. 177 * 178 * Ensures that the contents of a <<pre>>...<</pre>> HTML block are not 179 * converted into paragraphs or line-breaks. 180 * 181 * @since 1.2.0 182 * 183 * @param array|string $matches The array or string 184 * @return string The pre block without paragraph/line-break conversion. 185 */ 186 function clean_pre($matches) { 187 if ( is_array($matches) ) 188 $text = $matches[1] . $matches[2] . "</pre>"; 189 else 190 $text = $matches; 191 192 $text = str_replace('<br />', '', $text); 193 $text = str_replace('<p>', "\n", $text); 194 $text = str_replace('</p>', '', $text); 195 196 return $text; 197 } 198 199 /** 200 * Replaces double line-breaks with paragraph elements. 201 * 202 * A group of regex replaces used to identify text formatted with newlines and 203 * replace double line-breaks with HTML paragraph tags. The remaining 204 * line-breaks after conversion become <<br />> tags, unless $br is set to '0' 205 * or 'false'. 206 * 207 * @since 0.71 208 * 209 * @param string $pee The text which has to be formatted. 210 * @param int|bool $br Optional. If set, this will convert all remaining line-breaks after paragraphing. Default true. 211 * @return string Text which has been converted into correct paragraph tags. 212 */ 213 function wpautop($pee, $br = 1) { 214 215 if ( trim($pee) === '' ) 216 return ''; 217 $pee = $pee . "\n"; // just to make things a little easier, pad the end 218 $pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee); 219 // Space things out a little 220 $allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|option|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)'; 221 $pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee); 222 $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee); 223 $pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines 224 if ( strpos($pee, '<object') !== false ) { 225 $pee = preg_replace('|\s*<param([^>]*)>\s*|', "<param$1>", $pee); // no pee inside object/embed 226 $pee = preg_replace('|\s*</embed>\s*|', '</embed>', $pee); 227 } 228 $pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates 229 // make paragraphs, including one at the end 230 $pees = preg_split('/\n\s*\n/', $pee, -1, PREG_SPLIT_NO_EMPTY); 231 $pee = ''; 232 foreach ( $pees as $tinkle ) 233 $pee .= '<p>' . trim($tinkle, "\n") . "</p>\n"; 234 $pee = preg_replace('|<p>\s*</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace 235 $pee = preg_replace('!<p>([^<]+)</(div|address|form)>!', "<p>$1</p></$2>", $pee); 236 $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag 237 $pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists 238 $pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee); 239 $pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee); 240 $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee); 241 $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); 242 if ($br) { 243 $pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', '_autop_newline_preservation_helper', $pee); 244 $pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks 245 $pee = str_replace('<WPPreserveNewline />', "\n", $pee); 246 } 247 $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee); 248 $pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee); 249 if (strpos($pee, '<pre') !== false) 250 $pee = preg_replace_callback('!(<pre[^>]*>)(.*?)</pre>!is', 'clean_pre', $pee ); 251 $pee = preg_replace( "|\n</p>$|", '</p>', $pee ); 252 253 return $pee; 254 } 255 256 /** 257 * Newline preservation help function for wpautop 258 * 259 * @since 3.1.0 260 * @access private 261 * @param array $matches preg_replace_callback matches array 262 * @returns string 263 */ 264 function _autop_newline_preservation_helper( $matches ) { 265 return str_replace("\n", "<WPPreserveNewline />", $matches[0]); 266 } 267 268 /** 269 * Don't auto-p wrap shortcodes that stand alone 270 * 271 * Ensures that shortcodes are not wrapped in <<p>>...<</p>>. 272 * 273 * @since 2.9.0 274 * 275 * @param string $pee The content. 276 * @return string The filtered content. 277 */ 278 function shortcode_unautop( $pee ) { 279 global $shortcode_tags; 280 281 if ( empty( $shortcode_tags ) || !is_array( $shortcode_tags ) ) { 282 return $pee; 283 } 284 285 $tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) ); 286 287 $pattern = 288 '/' 289 . '<p>' // Opening paragraph 290 . '\\s*+' // Optional leading whitespace 291 . '(' // 1: The shortcode 292 . '\\[' // Opening bracket 293 . "($tagregexp)" // 2: Shortcode name 294 . '\\b' // Word boundary 295 // Unroll the loop: Inside the opening shortcode tag 296 . '[^\\]\\/]*' // Not a closing bracket or forward slash 297 . '(?:' 298 . '\\/(?!\\])' // A forward slash not followed by a closing bracket 299 . '[^\\]\\/]*' // Not a closing bracket or forward slash 300 . ')*?' 301 . '(?:' 302 . '\\/\\]' // Self closing tag and closing bracket 303 . '|' 304 . '\\]' // Closing bracket 305 . '(?:' // Unroll the loop: Optionally, anything between the opening and closing shortcode tags 306 . '[^\\[]*+' // Not an opening bracket 307 . '(?:' 308 . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag 309 . '[^\\[]*+' // Not an opening bracket 310 . ')*+' 311 . '\\[\\/\\2\\]' // Closing shortcode tag 312 . ')?' 313 . ')' 314 . ')' 315 . '\\s*+' // optional trailing whitespace 316 . '<\\/p>' // closing paragraph 317 . '/s'; 318 319 return preg_replace( $pattern, '$1', $pee ); 320 } 321 322 /** 323 * Checks to see if a string is utf8 encoded. 324 * 325 * NOTE: This function checks for 5-Byte sequences, UTF8 326 * has Bytes Sequences with a maximum length of 4. 327 * 328 * @author bmorel at ssi dot fr (modified) 329 * @since 1.2.1 330 * 331 * @param string $str The string to be checked 332 * @return bool True if $str fits a UTF-8 model, false otherwise. 333 */ 334 function seems_utf8($str) { 335 $length = strlen($str); 336 for ($i=0; $i < $length; $i++) { 337 $c = ord($str[$i]); 338 if ($c < 0x80) $n = 0; # 0bbbbbbb 339 elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb 340 elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb 341 elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb 342 elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb 343 elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b 344 else return false; # Does not match any model 345 for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ? 346 if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) 347 return false; 348 } 349 } 350 return true; 351 } 352 353 /** 354 * Converts a number of special characters into their HTML entities. 355 * 356 * Specifically deals with: &, <, >, ", and '. 357 * 358 * $quote_style can be set to ENT_COMPAT to encode " to 359 * ", or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded. 360 * 361 * @since 1.2.2 362 * 363 * @param string $string The text which is to be encoded. 364 * @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. 365 * @param string $charset Optional. The character encoding of the string. Default is false. 366 * @param boolean $double_encode Optional. Whether to encode existing html entities. Default is false. 367 * @return string The encoded text with HTML entities. 368 */ 369 function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) { 370 $string = (string) $string; 371 372 if ( 0 === strlen( $string ) ) 373 return ''; 374 375 // Don't bother if there are no specialchars - saves some processing 376 if ( ! preg_match( '/[&<>"\']/', $string ) ) 377 return $string; 378 379 // Account for the previous behaviour of the function when the $quote_style is not an accepted value 380 if ( empty( $quote_style ) ) 381 $quote_style = ENT_NOQUOTES; 382 elseif ( ! in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) 383 $quote_style = ENT_QUOTES; 384 385 // Store the site charset as a static to avoid multiple calls to wp_load_alloptions() 386 if ( ! $charset ) { 387 static $_charset; 388 if ( ! isset( $_charset ) ) { 389 $alloptions = wp_load_alloptions(); 390 $_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : ''; 391 } 392 $charset = $_charset; 393 } 394 395 if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) ) 396 $charset = 'UTF-8'; 397 398 $_quote_style = $quote_style; 399 400 if ( $quote_style === 'double' ) { 401 $quote_style = ENT_COMPAT; 402 $_quote_style = ENT_COMPAT; 403 } elseif ( $quote_style === 'single' ) { 404 $quote_style = ENT_NOQUOTES; 405 } 406 407 // Handle double encoding ourselves 408 if ( $double_encode ) { 409 $string = @htmlspecialchars( $string, $quote_style, $charset ); 410 } else { 411 // Decode & into & 412 $string = wp_specialchars_decode( $string, $_quote_style ); 413 414 // Guarantee every &entity; is valid or re-encode the & 415 $string = wp_kses_normalize_entities( $string ); 416 417 // Now re-encode everything except &entity; 418 $string = preg_split( '/(&#?x?[0-9a-z]+;)/i', $string, -1, PREG_SPLIT_DELIM_CAPTURE ); 419 420 for ( $i = 0; $i < count( $string ); $i += 2 ) 421 $string[$i] = @htmlspecialchars( $string[$i], $quote_style, $charset ); 422 423 $string = implode( '', $string ); 424 } 425 426 // Backwards compatibility 427 if ( 'single' === $_quote_style ) 428 $string = str_replace( "'", ''', $string ); 429 430 return $string; 431 } 432 433 /** 434 * Converts a number of HTML entities into their special characters. 435 * 436 * Specifically deals with: &, <, >, ", and '. 437 * 438 * $quote_style can be set to ENT_COMPAT to decode " entities, 439 * or ENT_QUOTES to do both " and '. Default is ENT_NOQUOTES where no quotes are decoded. 440 * 441 * @since 2.8 442 * 443 * @param string $string The text which is to be decoded. 444 * @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. 445 * @return string The decoded text without HTML entities. 446 */ 447 function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) { 448 $string = (string) $string; 449 450 if ( 0 === strlen( $string ) ) { 451 return ''; 452 } 453 454 // Don't bother if there are no entities - saves a lot of processing 455 if ( strpos( $string, '&' ) === false ) { 456 return $string; 457 } 458 459 // Match the previous behaviour of _wp_specialchars() when the $quote_style is not an accepted value 460 if ( empty( $quote_style ) ) { 461 $quote_style = ENT_NOQUOTES; 462 } elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) { 463 $quote_style = ENT_QUOTES; 464 } 465 466 // More complete than get_html_translation_table( HTML_SPECIALCHARS ) 467 $single = array( ''' => '\'', ''' => '\'' ); 468 $single_preg = array( '/�*39;/' => ''', '/�*27;/i' => ''' ); 469 $double = array( '"' => '"', '"' => '"', '"' => '"' ); 470 $double_preg = array( '/�*34;/' => '"', '/�*22;/i' => '"' ); 471 $others = array( '<' => '<', '<' => '<', '>' => '>', '>' => '>', '&' => '&', '&' => '&', '&' => '&' ); 472 $others_preg = array( '/�*60;/' => '<', '/�*62;/' => '>', '/�*38;/' => '&', '/�*26;/i' => '&' ); 473 474 if ( $quote_style === ENT_QUOTES ) { 475 $translation = array_merge( $single, $double, $others ); 476 $translation_preg = array_merge( $single_preg, $double_preg, $others_preg ); 477 } elseif ( $quote_style === ENT_COMPAT || $quote_style === 'double' ) { 478 $translation = array_merge( $double, $others ); 479 $translation_preg = array_merge( $double_preg, $others_preg ); 480 } elseif ( $quote_style === 'single' ) { 481 $translation = array_merge( $single, $others ); 482 $translation_preg = array_merge( $single_preg, $others_preg ); 483 } elseif ( $quote_style === ENT_NOQUOTES ) { 484 $translation = $others; 485 $translation_preg = $others_preg; 486 } 487 488 // Remove zero padding on numeric entities 489 $string = preg_replace( array_keys( $translation_preg ), array_values( $translation_preg ), $string ); 490 491 // Replace characters according to translation table 492 return strtr( $string, $translation ); 493 } 494 495 /** 496 * Checks for invalid UTF8 in a string. 497 * 498 * @since 2.8 499 * 500 * @param string $string The text which is to be checked. 501 * @param boolean $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false. 502 * @return string The checked text. 503 */ 504 function wp_check_invalid_utf8( $string, $strip = false ) { 505 $string = (string) $string; 506 507 if ( 0 === strlen( $string ) ) { 508 return ''; 509 } 510 511 // Store the site charset as a static to avoid multiple calls to get_option() 512 static $is_utf8; 513 if ( !isset( $is_utf8 ) ) { 514 $is_utf8 = in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ); 515 } 516 if ( !$is_utf8 ) { 517 return $string; 518 } 519 520 // Check for support for utf8 in the installed PCRE library once and store the result in a static 521 static $utf8_pcre; 522 if ( !isset( $utf8_pcre ) ) { 523 $utf8_pcre = @preg_match( '/^./u', 'a' ); 524 } 525 // We can't demand utf8 in the PCRE installation, so just return the string in those cases 526 if ( !$utf8_pcre ) { 527 return $string; 528 } 529 530 // preg_match fails when it encounters invalid UTF8 in $string 531 if ( 1 === @preg_match( '/^./us', $string ) ) { 532 return $string; 533 } 534 535 // Attempt to strip the bad chars if requested (not recommended) 536 if ( $strip && function_exists( 'iconv' ) ) { 537 return iconv( 'utf-8', 'utf-8', $string ); 538 } 539 540 return ''; 541 } 542 543 /** 544 * Encode the Unicode values to be used in the URI. 545 * 546 * @since 1.5.0 547 * 548 * @param string $utf8_string 549 * @param int $length Max length of the string 550 * @return string String with Unicode encoded for URI. 551 */ 552 function utf8_uri_encode( $utf8_string, $length = 0 ) { 553 $unicode = ''; 554 $values = array(); 555 $num_octets = 1; 556 $unicode_length = 0; 557 558 $string_length = strlen( $utf8_string ); 559 for ($i = 0; $i < $string_length; $i++ ) { 560 561 $value = ord( $utf8_string[ $i ] ); 562 563 if ( $value < 128 ) { 564 if ( $length && ( $unicode_length >= $length ) ) 565 break; 566 $unicode .= chr($value); 567 $unicode_length++; 568 } else { 569 if ( count( $values ) == 0 ) $num_octets = ( $value < 224 ) ? 2 : 3; 570 571 $values[] = $value; 572 573 if ( $length && ( $unicode_length + ($num_octets * 3) ) > $length ) 574 break; 575 if ( count( $values ) == $num_octets ) { 576 if ($num_octets == 3) { 577 $unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]) . '%' . dechex($values[2]); 578 $unicode_length += 9; 579 } else { 580 $unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]); 581 $unicode_length += 6; 582 } 583 584 $values = array(); 585 $num_octets = 1; 586 } 587 } 588 } 589 590 return $unicode; 591 } 592 593 /** 594 * Converts all accent characters to ASCII characters. 595 * 596 * If there are no accent characters, then the string given is just returned. 597 * 598 * @since 1.2.1 599 * 600 * @param string $string Text that might have accent characters 601 * @return string Filtered string with replaced "nice" characters. 602 */ 603 function remove_accents($string) { 604 if ( !preg_match('/[\x80-\xff]/', $string) ) 605 return $string; 606 607 if (seems_utf8($string)) { 608 $chars = array( 609 // Decompositions for Latin-1 Supplement 610 chr(194).chr(170) => 'a', chr(194).chr(186) => 'o', 611 chr(195).chr(128) => 'A', chr(195).chr(129) => 'A', 612 chr(195).chr(130) => 'A', chr(195).chr(131) => 'A', 613 chr(195).chr(132) => 'A', chr(195).chr(133) => 'A', 614 chr(195).chr(134) => 'AE',chr(195).chr(135) => 'C', 615 chr(195).chr(136) => 'E', chr(195).chr(137) => 'E', 616 chr(195).chr(138) => 'E', chr(195).chr(139) => 'E', 617 chr(195).chr(140) => 'I', chr(195).chr(141) => 'I', 618 chr(195).chr(142) => 'I', chr(195).chr(143) => 'I', 619 chr(195).chr(144) => 'D', chr(195).chr(145) => 'N', 620 chr(195).chr(146) => 'O', chr(195).chr(147) => 'O', 621 chr(195).chr(148) => 'O', chr(195).chr(149) => 'O', 622 chr(195).chr(150) => 'O', chr(195).chr(153) => 'U', 623 chr(195).chr(154) => 'U', chr(195).chr(155) => 'U', 624 chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y', 625 chr(195).chr(158) => 'TH',chr(195).chr(159) => 's', 626 chr(195).chr(160) => 'a', chr(195).chr(161) => 'a', 627 chr(195).chr(162) => 'a', chr(195).chr(163) => 'a', 628 chr(195).chr(164) => 'a', chr(195).chr(165) => 'a', 629 chr(195).chr(166) => 'ae',chr(195).chr(167) => 'c', 630 chr(195).chr(168) => 'e', chr(195).chr(169) => 'e', 631 chr(195).chr(170) => 'e', chr(195).chr(171) => 'e', 632 chr(195).chr(172) => 'i', chr(195).chr(173) => 'i', 633 chr(195).chr(174) => 'i', chr(195).chr(175) => 'i', 634 chr(195).chr(176) => 'd', chr(195).chr(177) => 'n', 635 chr(195).chr(178) => 'o', chr(195).chr(179) => 'o', 636 chr(195).chr(180) => 'o', chr(195).chr(181) => 'o', 637 chr(195).chr(182) => 'o', chr(195).chr(184) => 'o', 638 chr(195).chr(185) => 'u', chr(195).chr(186) => 'u', 639 chr(195).chr(187) => 'u', chr(195).chr(188) => 'u', 640 chr(195).chr(189) => 'y', chr(195).chr(190) => 'th', 641 chr(195).chr(191) => 'y', chr(195).chr(152) => 'O', 642 // Decompositions for Latin Extended-A 643 chr(196).chr(128) => 'A', chr(196).chr(129) => 'a', 644 chr(196).chr(130) => 'A', chr(196).chr(131) => 'a', 645 chr(196).chr(132) => 'A', chr(196).chr(133) => 'a', 646 chr(196).chr(134) => 'C', chr(196).chr(135) => 'c', 647 chr(196).chr(136) => 'C', chr(196).chr(137) => 'c', 648 chr(196).chr(138) => 'C', chr(196).chr(139) => 'c', 649 chr(196).chr(140) => 'C', chr(196).chr(141) => 'c', 650 chr(196).chr(142) => 'D', chr(196).chr(143) => 'd', 651 chr(196).chr(144) => 'D', chr(196).chr(145) => 'd', 652 chr(196).chr(146) => 'E', chr(196).chr(147) => 'e', 653 chr(196).chr(148) => 'E', chr(196).chr(149) => 'e', 654 chr(196).chr(150) => 'E', chr(196).chr(151) => 'e', 655 chr(196).chr(152) => 'E', chr(196).chr(153) => 'e', 656 chr(196).chr(154) => 'E', chr(196).chr(155) => 'e', 657 chr(196).chr(156) => 'G', chr(196).chr(157) => 'g', 658 chr(196).chr(158) => 'G', chr(196).chr(159) => 'g', 659 chr(196).chr(160) => 'G', chr(196).chr(161) => 'g', 660 chr(196).chr(162) => 'G', chr(196).chr(163) => 'g', 661 chr(196).chr(164) => 'H', chr(196).chr(165) => 'h', 662 chr(196).chr(166) => 'H', chr(196).chr(167) => 'h', 663 chr(196).chr(168) => 'I', chr(196).chr(169) => 'i', 664 chr(196).chr(170) => 'I', chr(196).chr(171) => 'i', 665 chr(196).chr(172) => 'I', chr(196).chr(173) => 'i', 666 chr(196).chr(174) => 'I', chr(196).chr(175) => 'i', 667 chr(196).chr(176) => 'I', chr(196).chr(177) => 'i', 668 chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij', 669 chr(196).chr(180) => 'J', chr(196).chr(181) => 'j', 670 chr(196).chr(182) => 'K', chr(196).chr(183) => 'k', 671 chr(196).chr(184) => 'k', chr(196).chr(185) => 'L', 672 chr(196).chr(186) => 'l', chr(196).chr(187) => 'L', 673 chr(196).chr(188) => 'l', chr(196).chr(189) => 'L', 674 chr(196).chr(190) => 'l', chr(196).chr(191) => 'L', 675 chr(197).chr(128) => 'l', chr(197).chr(129) => 'L', 676 chr(197).chr(130) => 'l', chr(197).chr(131) => 'N', 677 chr(197).chr(132) => 'n', chr(197).chr(133) => 'N', 678 chr(197).chr(134) => 'n', chr(197).chr(135) => 'N', 679 chr(197).chr(136) => 'n', chr(197).chr(137) => 'N', 680 chr(197).chr(138) => 'n', chr(197).chr(139) => 'N', 681 chr(197).chr(140) => 'O', chr(197).chr(141) => 'o', 682 chr(197).chr(142) => 'O', chr(197).chr(143) => 'o', 683 chr(197).chr(144) => 'O', chr(197).chr(145) => 'o', 684 chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe', 685 chr(197).chr(148) => 'R',chr(197).chr(149) => 'r', 686 chr(197).chr(150) => 'R',chr(197).chr(151) => 'r', 687 chr(197).chr(152) => 'R',chr(197).chr(153) => 'r', 688 chr(197).chr(154) => 'S',chr(197).chr(155) => 's', 689 chr(197).chr(156) => 'S',chr(197).chr(157) => 's', 690 chr(197).chr(158) => 'S',chr(197).chr(159) => 's', 691 chr(197).chr(160) => 'S', chr(197).chr(161) => 's', 692 chr(197).chr(162) => 'T', chr(197).chr(163) => 't', 693 chr(197).chr(164) => 'T', chr(197).chr(165) => 't', 694 chr(197).chr(166) => 'T', chr(197).chr(167) => 't', 695 chr(197).chr(168) => 'U', chr(197).chr(169) => 'u', 696 chr(197).chr(170) => 'U', chr(197).chr(171) => 'u', 697 chr(197).chr(172) => 'U', chr(197).chr(173) => 'u', 698 chr(197).chr(174) => 'U', chr(197).chr(175) => 'u', 699 chr(197).chr(176) => 'U', chr(197).chr(177) => 'u', 700 chr(197).chr(178) => 'U', chr(197).chr(179) => 'u', 701 chr(197).chr(180) => 'W', chr(197).chr(181) => 'w', 702 chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y', 703 chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z', 704 chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z', 705 chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z', 706 chr(197).chr(190) => 'z', chr(197).chr(191) => 's', 707 // Decompositions for Latin Extended-B 708 chr(200).chr(152) => 'S', chr(200).chr(153) => 's', 709 chr(200).chr(154) => 'T', chr(200).chr(155) => 't', 710 // Euro Sign 711 chr(226).chr(130).chr(172) => 'E', 712 // GBP (Pound) Sign 713 chr(194).chr(163) => ''); 714 715 $string = strtr($string, $chars); 716 } else { 717 // Assume ISO-8859-1 if not UTF-8 718 $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158) 719 .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194) 720 .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202) 721 .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210) 722 .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218) 723 .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227) 724 .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235) 725 .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243) 726 .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251) 727 .chr(252).chr(253).chr(255); 728 729 $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy"; 730 731 $string = strtr($string, $chars['in'], $chars['out']); 732 $double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254)); 733 $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'); 734 $string = str_replace($double_chars['in'], $double_chars['out'], $string); 735 } 736 737 return $string; 738 } 739 740 /** 741 * Sanitizes a filename replacing whitespace with dashes 742 * 743 * Removes special characters that are illegal in filenames on certain 744 * operating systems and special characters requiring special escaping 745 * to manipulate at the command line. Replaces spaces and consecutive 746 * dashes with a single dash. Trim period, dash and underscore from beginning 747 * and end of filename. 748 * 749 * @since 2.1.0 750 * 751 * @param string $filename The filename to be sanitized 752 * @return string The sanitized filename 753 */ 754 function sanitize_file_name( $filename ) { 755 $filename_raw = $filename; 756 $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", chr(0)); 757 $special_chars = apply_filters('sanitize_file_name_chars', $special_chars, $filename_raw); 758 $filename = str_replace($special_chars, '', $filename); 759 $filename = preg_replace('/[\s-]+/', '-', $filename); 760 $filename = trim($filename, '.-_'); 761 762 // Split the filename into a base and extension[s] 763 $parts = explode('.', $filename); 764 765 // Return if only one extension 766 if ( count($parts) <= 2 ) 767 return apply_filters('sanitize_file_name', $filename, $filename_raw); 768 769 // Process multiple extensions 770 $filename = array_shift($parts); 771 $extension = array_pop($parts); 772 $mimes = get_allowed_mime_types(); 773 774 // Loop over any intermediate extensions. Munge them with a trailing underscore if they are a 2 - 5 character 775 // long alpha string not in the extension whitelist. 776 foreach ( (array) $parts as $part) { 777 $filename .= '.' . $part; 778 779 if ( preg_match("/^[a-zA-Z]{2,5}\d?$/", $part) ) { 780 $allowed = false; 781 foreach ( $mimes as $ext_preg => $mime_match ) { 782 $ext_preg = '!^(' . $ext_preg . ')$!i'; 783 if ( preg_match( $ext_preg, $part ) ) { 784 $allowed = true; 785 break; 786 } 787 } 788 if ( !$allowed ) 789 $filename .= '_'; 790 } 791 } 792 $filename .= '.' . $extension; 793 794 return apply_filters('sanitize_file_name', $filename, $filename_raw); 795 } 796 797 /** 798 * Sanitize username stripping out unsafe characters. 799 * 800 * Removes tags, octets, entities, and if strict is enabled, will only keep 801 * alphanumeric, _, space, ., -, @. After sanitizing, it passes the username, 802 * raw username (the username in the parameter), and the value of $strict as 803 * parameters for the 'sanitize_user' filter. 804 * 805 * @since 2.0.0 806 * @uses apply_filters() Calls 'sanitize_user' hook on username, raw username, 807 * and $strict parameter. 808 * 809 * @param string $username The username to be sanitized. 810 * @param bool $strict If set limits $username to specific characters. Default false. 811 * @return string The sanitized username, after passing through filters. 812 */ 813 function sanitize_user( $username, $strict = false ) { 814 $raw_username = $username; 815 $username = wp_strip_all_tags( $username ); 816 $username = remove_accents( $username ); 817 // Kill octets 818 $username = preg_replace( '|%([a-fA-F0-9][a-fA-F0-9])|', '', $username ); 819 $username = preg_replace( '/&.+?;/', '', $username ); // Kill entities 820 821 // If strict, reduce to ASCII for max portability. 822 if ( $strict ) 823 $username = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $username ); 824 825 $username = trim( $username ); 826 // Consolidate contiguous whitespace 827 $username = preg_replace( '|\s+|', ' ', $username ); 828 829 return apply_filters( 'sanitize_user', $username, $raw_username, $strict ); 830 } 831 832 /** 833 * Sanitize a string key. 834 * 835 * Keys are used as internal identifiers. Lowercase alphanumeric characters, dashes and underscores are allowed. 836 * 837 * @since 3.0.0 838 * 839 * @param string $key String key 840 * @return string Sanitized key 841 */ 842 function sanitize_key( $key ) { 843 $raw_key = $key; 844 $key = strtolower( $key ); 845 $key = preg_replace( '/[^a-z0-9_\-]/', '', $key ); 846 return apply_filters( 'sanitize_key', $key, $raw_key ); 847 } 848 849 /** 850 * Sanitizes title or use fallback title. 851 * 852 * Specifically, HTML and PHP tags are stripped. Further actions can be added 853 * via the plugin API. If $title is empty and $fallback_title is set, the latter 854 * will be used. 855 * 856 * @since 1.0.0 857 * 858 * @param string $title The string to be sanitized. 859 * @param string $fallback_title Optional. A title to use if $title is empty. 860 * @param string $context Optional. The operation for which the string is sanitized 861 * @return string The sanitized string. 862 */ 863 function sanitize_title($title, $fallback_title = '', $context = 'save') { 864 $raw_title = $title; 865 866 if ( 'save' == $context ) 867 $title = remove_accents($title); 868 869 $title = apply_filters('sanitize_title', $title, $raw_title, $context); 870 871 if ( '' === $title || false === $title ) 872 $title = $fallback_title; 873 874 return $title; 875 } 876 877 function sanitize_title_for_query($title) { 878 return sanitize_title($title, '', 'query'); 879 } 880 881 /** 882 * Sanitizes title, replacing whitespace and a few other characters with dashes. 883 * 884 * Limits the output to alphanumeric characters, underscore (_) and dash (-). 885 * Whitespace becomes a dash. 886 * 887 * @since 1.2.0 888 * 889 * @param string $title The title to be sanitized. 890 * @param string $raw_title Optional. Not used. 891 * @param string $context Optional. The operation for which the string is sanitized. 892 * @return string The sanitized title. 893 */ 894 function sanitize_title_with_dashes($title, $raw_title = '', $context = 'display') { 895 $title = strip_tags($title); 896 // Preserve escaped octets. 897 $title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title); 898 // Remove percent signs that are not part of an octet. 899 $title = str_replace('%', '', $title); 900 // Restore octets. 901 $title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title); 902 903 if (seems_utf8($title)) { 904 if (function_exists('mb_strtolower')) { 905 $title = mb_strtolower($title, 'UTF-8'); 906 } 907 $title = utf8_uri_encode($title, 200); 908 } 909 910 $title = strtolower($title); 911 $title = preg_replace('/&.+?;/', '', $title); // kill entities 912 $title = str_replace('.', '-', $title); 913 914 if ( 'save' == $context ) { 915 // nbsp, ndash and mdash 916 $title = str_replace( array( '%c2%a0', '%e2%80%93', '%e2%80%94' ), '-', $title ); 917 // iexcl and iquest 918 $title = str_replace( array( '%c2%a1', '%c2%bf' ), '', $title ); 919 // angle quotes 920 $title = str_replace( array( '%c2%ab', '%c2%bb', '%e2%80%b9', '%e2%80%ba' ), '', $title ); 921 // curly quotes 922 $title = str_replace( array( '%e2%80%98', '%e2%80%99', '%e2%80%9c', '%e2%80%9d' ), '', $title ); 923 // copy, reg, deg, hellip and trade 924 $title = str_replace( array( '%c2%a9', '%c2%ae', '%c2%b0', '%e2%80%a6', '%e2%84%a2' ), '', $title ); 925 } 926 927 $title = preg_replace('/[^%a-z0-9 _-]/', '', $title); 928 $title = preg_replace('/\s+/', '-', $title); 929 $title = preg_replace('|-+|', '-', $title); 930 $title = trim($title, '-'); 931 932 return $title; 933 } 934 935 /** 936 * Ensures a string is a valid SQL order by clause. 937 * 938 * Accepts one or more columns, with or without ASC/DESC, and also accepts 939 * RAND(). 940 * 941 * @since 2.5.1 942 * 943 * @param string $orderby Order by string to be checked. 944 * @return string|false Returns the order by clause if it is a match, false otherwise. 945 */ 946 function sanitize_sql_orderby( $orderby ){ 947 preg_match('/^\s*([a-z0-9_]+(\s+(ASC|DESC))?(\s*,\s*|\s*$))+|^\s*RAND\(\s*\)\s*$/i', $orderby, $obmatches); 948 if ( !$obmatches ) 949 return false; 950 return $orderby; 951 } 952 953 /** 954 * Santizes a html classname to ensure it only contains valid characters 955 * 956 * Strips the string down to A-Z,a-z,0-9,_,-. If this results in an empty 957 * string then it will return the alternative value supplied. 958 * 959 * @todo Expand to support the full range of CDATA that a class attribute can contain. 960 * 961 * @since 2.8.0 962 * 963 * @param string $class The classname to be sanitized 964 * @param string $fallback Optional. The value to return if the sanitization end's up as an empty string. 965 * Defaults to an empty string. 966 * @return string The sanitized value 967 */ 968 function sanitize_html_class( $class, $fallback = '' ) { 969 //Strip out any % encoded octets 970 $sanitized = preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $class ); 971 972 //Limit to A-Z,a-z,0-9,_,- 973 $sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $sanitized ); 974 975 if ( '' == $sanitized ) 976 $sanitized = $fallback; 977 978 return apply_filters( 'sanitize_html_class', $sanitized, $class, $fallback ); 979 } 980 981 /** 982 * Converts a number of characters from a string. 983 * 984 * Metadata tags <<title>> and <<category>> are removed, <<br>> and <<hr>> are 985 * converted into correct XHTML and Unicode characters are converted to the 986 * valid range. 987 * 988 * @since 0.71 989 * 990 * @param string $content String of characters to be converted. 991 * @param string $deprecated Not used. 992 * @return string Converted string. 993 */ 994 function convert_chars($content, $deprecated = '') { 995 if ( !empty( $deprecated ) ) 996 _deprecated_argument( __FUNCTION__, '0.71' ); 997 998 // Translation of invalid Unicode references range to valid range 999 $wp_htmltranswinuni = array( 1000 '€' => '€', // the Euro sign 1001 '' => '', 1002 '‚' => '‚', // these are Windows CP1252 specific characters 1003 'ƒ' => 'ƒ', // they would look weird on non-Windows browsers 1004 '„' => '„', 1005 '…' => '…', 1006 '†' => '†', 1007 '‡' => '‡', 1008 'ˆ' => 'ˆ', 1009 '‰' => '‰', 1010 'Š' => 'Š', 1011 '‹' => '‹', 1012 'Œ' => 'Œ', 1013 '' => '', 1014 'Ž' => 'ž', 1015 '' => '', 1016 '' => '', 1017 '‘' => '‘', 1018 '’' => '’', 1019 '“' => '“', 1020 '”' => '”', 1021 '•' => '•', 1022 '–' => '–', 1023 '—' => '—', 1024 '˜' => '˜', 1025 '™' => '™', 1026 'š' => 'š', 1027 '›' => '›', 1028 'œ' => 'œ', 1029 '' => '', 1030 'ž' => '', 1031 'Ÿ' => 'Ÿ' 1032 ); 1033 1034 // Remove metadata tags 1035 $content = preg_replace('/<title>(.+?)<\/title>/','',$content); 1036 $content = preg_replace('/<category>(.+?)<\/category>/','',$content); 1037 1038 // Converts lone & characters into & (a.k.a. &) 1039 $content = preg_replace('/&([^#])(?![a-z1-4]{1,8};)/i', '&$1', $content); 1040 1041 // Fix Word pasting 1042 $content = strtr($content, $wp_htmltranswinuni); 1043 1044 // Just a little XHTML help 1045 $content = str_replace('<br>', '<br />', $content); 1046 $content = str_replace('<hr>', '<hr />', $content); 1047 1048 return $content; 1049 } 1050 1051 /** 1052 * Will only balance the tags if forced to and the option is set to balance tags. 1053 * 1054 * The option 'use_balanceTags' is used to determine whether the tags will be balanced. 1055 * 1056 * @since 0.71 1057 * 1058 * @param string $text Text to be balanced 1059 * @param bool $force If true, forces balancing, ignoring the value of the option. Default false. 1060 * @return string Balanced text 1061 */ 1062 function balanceTags( $text, $force = false ) { 1063 if ( !$force && get_option('use_balanceTags') == 0 ) 1064 return $text; 1065 return force_balance_tags( $text ); 1066 } 1067 1068 /** 1069 * Balances tags of string using a modified stack. 1070 * 1071 * @since 2.0.4 1072 * 1073 * @author Leonard Lin <leonard@acm.org> 1074 * @license GPL 1075 * @copyright November 4, 2001 1076 * @version 1.1 1077 * @todo Make better - change loop condition to $text in 1.2 1078 * @internal Modified by Scott Reilly (coffee2code) 02 Aug 2004 1079 * 1.1 Fixed handling of append/stack pop order of end text 1080 * Added Cleaning Hooks 1081 * 1.0 First Version 1082 * 1083 * @param string $text Text to be balanced. 1084 * @return string Balanced text. 1085 */ 1086 function force_balance_tags( $text ) { 1087 $tagstack = array(); 1088 $stacksize = 0; 1089 $tagqueue = ''; 1090 $newtext = ''; 1091 $single_tags = array( 'br', 'hr', 'img', 'input' ); // Known single-entity/self-closing tags 1092 $nestable_tags = array( 'blockquote', 'div', 'span', 'q' ); // Tags that can be immediately nested within themselves 1093 1094 // WP bug fix for comments - in case you REALLY meant to type '< !--' 1095 $text = str_replace('< !--', '< !--', $text); 1096 // WP bug fix for LOVE <3 (and other situations with '<' before a number) 1097 $text = preg_replace('#<([0-9]{1})#', '<$1', $text); 1098 1099 while ( preg_match("/<(\/?[\w:]*)\s*([^>]*)>/", $text, $regex) ) { 1100 $newtext .= $tagqueue; 1101 1102 $i = strpos($text, $regex[0]); 1103 $l = strlen($regex[0]); 1104 1105 // clear the shifter 1106 $tagqueue = ''; 1107 // Pop or Push 1108 if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag 1109 $tag = strtolower(substr($regex[1],1)); 1110 // if too many closing tags 1111 if( $stacksize <= 0 ) { 1112 $tag = ''; 1113 // or close to be safe $tag = '/' . $tag; 1114 } 1115 // if stacktop value = tag close value then pop 1116 else if ( $tagstack[$stacksize - 1] == $tag ) { // found closing tag 1117 $tag = '</' . $tag . '>'; // Close Tag 1118 // Pop 1119 array_pop( $tagstack ); 1120 $stacksize--; 1121 } else { // closing tag not at top, search for it 1122 for ( $j = $stacksize-1; $j >= 0; $j-- ) { 1123 if ( $tagstack[$j] == $tag ) { 1124 // add tag to tagqueue 1125 for ( $k = $stacksize-1; $k >= $j; $k--) { 1126 $tagqueue .= '</' . array_pop( $tagstack ) . '>'; 1127 $stacksize--; 1128 } 1129 break; 1130 } 1131 } 1132 $tag = ''; 1133 } 1134 } else { // Begin Tag 1135 $tag = strtolower($regex[1]); 1136 1137 // Tag Cleaning 1138 1139 // If self-closing or '', don't do anything. 1140 if ( substr($regex[2],-1) == '/' || $tag == '' ) { 1141 // do nothing 1142 } 1143 // ElseIf it's a known single-entity tag but it doesn't close itself, do so 1144 elseif ( in_array($tag, $single_tags) ) { 1145 $regex[2] .= '/'; 1146 } else { // Push the tag onto the stack 1147 // If the top of the stack is the same as the tag we want to push, close previous tag 1148 if ( $stacksize > 0 && !in_array($tag, $nestable_tags) && $tagstack[$stacksize - 1] == $tag ) { 1149 $tagqueue = '</' . array_pop ($tagstack) . '>'; 1150 $stacksize--; 1151 } 1152 $stacksize = array_push ($tagstack, $tag); 1153 } 1154 1155 // Attributes 1156 $attributes = $regex[2]; 1157 if( !empty($attributes) ) 1158 $attributes = ' '.$attributes; 1159 1160 $tag = '<' . $tag . $attributes . '>'; 1161 //If already queuing a close tag, then put this tag on, too 1162 if ( !empty($tagqueue) ) { 1163 $tagqueue .= $tag; 1164 $tag = ''; 1165 } 1166 } 1167 $newtext .= substr($text, 0, $i) . $tag; 1168 $text = substr($text, $i + $l); 1169 } 1170 1171 // Clear Tag Queue 1172 $newtext .= $tagqueue; 1173 1174 // Add Remaining text 1175 $newtext .= $text; 1176 1177 // Empty Stack 1178 while( $x = array_pop($tagstack) ) 1179 $newtext .= '</' . $x . '>'; // Add remaining tags to close 1180 1181 // WP fix for the bug with HTML comments 1182 $newtext = str_replace("< !--","<!--",$newtext); 1183 $newtext = str_replace("< !--","< !--",$newtext); 1184 1185 return $newtext; 1186 } 1187 1188 /** 1189 * Acts on text which is about to be edited. 1190 * 1191 * Unless $richedit is set, it is simply a holder for the 'format_to_edit' 1192 * filter. If $richedit is set true htmlspecialchars(), through esc_textarea(), 1193 * will be run on the content, converting special characters to HTML entities. 1194 * 1195 * @since 0.71 1196 * 1197 * @param string $content The text about to be edited. 1198 * @param bool $richedit Whether the $content should pass through htmlspecialchars(). Default false. 1199 * @return string The text after the filter (and possibly htmlspecialchars()) has been run. 1200 */ 1201 function format_to_edit( $content, $richedit = false ) { 1202 $content = apply_filters( 'format_to_edit', $content ); 1203 if ( ! $richedit ) 1204 $content = esc_textarea( $content ); 1205 return $content; 1206 } 1207 1208 /** 1209 * Holder for the 'format_to_post' filter. 1210 * 1211 * @since 0.71 1212 * 1213 * @param string $content The text to pass through the filter. 1214 * @return string Text returned from the 'format_to_post' filter. 1215 */ 1216 function format_to_post($content) { 1217 $content = apply_filters('format_to_post', $content); 1218 return $content; 1219 } 1220 1221 /** 1222 * Add leading zeros when necessary. 1223 * 1224 * If you set the threshold to '4' and the number is '10', then you will get 1225 * back '0010'. If you set the threshold to '4' and the number is '5000', then you 1226 * will get back '5000'. 1227 * 1228 * Uses sprintf to append the amount of zeros based on the $threshold parameter 1229 * and the size of the number. If the number is large enough, then no zeros will 1230 * be appended. 1231 * 1232 * @since 0.71 1233 * 1234 * @param mixed $number Number to append zeros to if not greater than threshold. 1235 * @param int $threshold Digit places number needs to be to not have zeros added. 1236 * @return string Adds leading zeros to number if needed. 1237 */ 1238 function zeroise($number, $threshold) { 1239 return sprintf('%0'.$threshold.'s', $number); 1240 } 1241 1242 /** 1243 * Adds backslashes before letters and before a number at the start of a string. 1244 * 1245 * @since 0.71 1246 * 1247 * @param string $string Value to which backslashes will be added. 1248 * @return string String with backslashes inserted. 1249 */ 1250 function backslashit($string) { 1251 $string = preg_replace('/^([0-9])/', '\\\\\\\\\1', $string); 1252 $string = preg_replace('/([a-z])/i', '\\\\\1', $string); 1253 return $string; 1254 } 1255 1256 /** 1257 * Appends a trailing slash. 1258 * 1259 * Will remove trailing slash if it exists already before adding a trailing 1260 * slash. This prevents double slashing a string or path. 1261 * 1262 * The primary use of this is for paths and thus should be used for paths. It is 1263 * not restricted to paths and offers no specific path support. 1264 * 1265 * @since 1.2.0 1266 * @uses untrailingslashit() Unslashes string if it was slashed already. 1267 * 1268 * @param string $string What to add the trailing slash to. 1269 * @return string String with trailing slash added. 1270 */ 1271 function trailingslashit($string) { 1272 return untrailingslashit($string) . '/'; 1273 } 1274 1275 /** 1276 * Removes trailing slash if it exists. 1277 * 1278 * The primary use of this is for paths and thus should be used for paths. It is 1279 * not restricted to paths and offers no specific path support. 1280 * 1281 * @since 2.2.0 1282 * 1283 * @param string $string What to remove the trailing slash from. 1284 * @return string String without the trailing slash. 1285 */ 1286 function untrailingslashit($string) { 1287 return rtrim($string, '/'); 1288 } 1289 1290 /** 1291 * Adds slashes to escape strings. 1292 * 1293 * Slashes will first be removed if magic_quotes_gpc is set, see {@link 1294 * http://www.php.net/magic_quotes} for more details. 1295 * 1296 * @since 0.71 1297 * 1298 * @param string $gpc The string returned from HTTP request data. 1299 * @return string Returns a string escaped with slashes. 1300 */ 1301 function addslashes_gpc($gpc) { 1302 if ( get_magic_quotes_gpc() ) 1303 $gpc = stripslashes($gpc); 1304 1305 return esc_sql($gpc); 1306 } 1307 1308 /** 1309 * Navigates through an array and removes slashes from the values. 1310 * 1311 * If an array is passed, the array_map() function causes a callback to pass the 1312 * value back to the function. The slashes from this value will removed. 1313 * 1314 * @since 2.0.0 1315 * 1316 * @param array|string $value The array or string to be stripped. 1317 * @return array|string Stripped array (or string in the callback). 1318 */ 1319 function stripslashes_deep($value) { 1320 if ( is_array($value) ) { 1321 $value = array_map('stripslashes_deep', $value); 1322 } elseif ( is_object($value) ) { 1323 $vars = get_object_vars( $value ); 1324 foreach ($vars as $key=>$data) { 1325 $value->{$key} = stripslashes_deep( $data ); 1326 } 1327 } else { 1328 $value = stripslashes($value); 1329 } 1330 1331 return $value; 1332 } 1333 1334 /** 1335 * Navigates through an array and encodes the values to be used in a URL. 1336 * 1337 * Uses a callback to pass the value of the array back to the function as a 1338 * string. 1339 * 1340 * @since 2.2.0 1341 * 1342 * @param array|string $value The array or string to be encoded. 1343 * @return array|string $value The encoded array (or string from the callback). 1344 */ 1345 function urlencode_deep($value) { 1346 $value = is_array($value) ? array_map('urlencode_deep', $value) : urlencode($value); 1347 return $value; 1348 } 1349 1350 /** 1351 * Converts email addresses characters to HTML entities to block spam bots. 1352 * 1353 * @since 0.71 1354 * 1355 * @param string $emailaddy Email address. 1356 * @param int $mailto Optional. Range from 0 to 1. Used for encoding. 1357 * @return string Converted email address. 1358 */ 1359 function antispambot($emailaddy, $mailto=0) { 1360 $emailNOSPAMaddy = ''; 1361 srand ((float) microtime() * 1000000); 1362 for ($i = 0; $i < strlen($emailaddy); $i = $i + 1) { 1363 $j = floor(rand(0, 1+$mailto)); 1364 if ($j==0) { 1365 $emailNOSPAMaddy .= '&#'.ord(substr($emailaddy,$i,1)).';'; 1366 } elseif ($j==1) { 1367 $emailNOSPAMaddy .= substr($emailaddy,$i,1); 1368 } elseif ($j==2) { 1369 $emailNOSPAMaddy .= '%'.zeroise(dechex(ord(substr($emailaddy, $i, 1))), 2); 1370 } 1371 } 1372 $emailNOSPAMaddy = str_replace('@','@',$emailNOSPAMaddy); 1373 return $emailNOSPAMaddy; 1374 } 1375 1376 /** 1377 * Callback to convert URI match to HTML A element. 1378 * 1379 * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link 1380 * make_clickable()}. 1381 * 1382 * @since 2.3.2 1383 * @access private 1384 * 1385 * @param array $matches Single Regex Match. 1386 * @return string HTML A element with URI address. 1387 */ 1388 function _make_url_clickable_cb($matches) { 1389 $url = $matches[2]; 1390 $suffix = ''; 1391 1392 /** Include parentheses in the URL only if paired **/ 1393 while ( substr_count( $url, '(' ) < substr_count( $url, ')' ) ) { 1394 $suffix = strrchr( $url, ')' ) . $suffix; 1395 $url = substr( $url, 0, strrpos( $url, ')' ) ); 1396 } 1397 1398 $url = esc_url($url); 1399 if ( empty($url) ) 1400 return $matches[0]; 1401 1402 return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>" . $suffix; 1403 } 1404 1405 /** 1406 * Callback to convert URL match to HTML A element. 1407 * 1408 * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link 1409 * make_clickable()}. 1410 * 1411 * @since 2.3.2 1412 * @access private 1413 * 1414 * @param array $matches Single Regex Match. 1415 * @return string HTML A element with URL address. 1416 */ 1417 function _make_web_ftp_clickable_cb($matches) { 1418 $ret = ''; 1419 $dest = $matches[2]; 1420 $dest = 'http://' . $dest; 1421 $dest = esc_url($dest); 1422 if ( empty($dest) ) 1423 return $matches[0]; 1424 1425 // removed trailing [.,;:)] from URL 1426 if ( in_array( substr($dest, -1), array('.', ',', ';', ':', ')') ) === true ) { 1427 $ret = substr($dest, -1); 1428 $dest = substr($dest, 0, strlen($dest)-1); 1429 } 1430 return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>$ret"; 1431 } 1432 1433 /** 1434 * Callback to convert email address match to HTML A element. 1435 * 1436 * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link 1437 * make_clickable()}. 1438 * 1439 * @since 2.3.2 1440 * @access private 1441 * 1442 * @param array $matches Single Regex Match. 1443 * @return string HTML A element with email address. 1444 */ 1445 function _make_email_clickable_cb($matches) { 1446 $email = $matches[2] . '@' . $matches[3]; 1447 return $matches[1] . "<a href=\"mailto:$email\">$email</a>"; 1448 } 1449 1450 /** 1451 * Convert plaintext URI to HTML links. 1452 * 1453 * Converts URI, www and ftp, and email addresses. Finishes by fixing links 1454 * within links. 1455 * 1456 * @since 0.71 1457 * 1458 * @param string $ret Content to convert URIs. 1459 * @return string Content with converted URIs. 1460 */ 1461 function make_clickable($ret) { 1462 $ret = ' ' . $ret; 1463 // in testing, using arrays here was found to be faster 1464 $save = @ini_set('pcre.recursion_limit', 10000); 1465 $retval = preg_replace_callback('#(?<!=[\'"])(?<=[*\')+.,;:!&$\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#%~/?@\[\]-]{1,2000}|[\'*(+.,;:!=&$](?![\b\)]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:]|$)|\)))+)#is', '_make_url_clickable_cb', $ret); 1466 if (null !== $retval ) 1467 $ret = $retval; 1468 @ini_set('pcre.recursion_limit', $save); 1469 $ret = preg_replace_callback('#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret); 1470 $ret = preg_replace_callback('#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret); 1471 // this one is not in an array because we need it to run last, for cleanup of accidental links within links 1472 $ret = preg_replace("#(<a( [^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i", "$1$3</a>", $ret); 1473 $ret = trim($ret); 1474 return $ret; 1475 } 1476 1477 /** 1478 * Adds rel nofollow string to all HTML A elements in content. 1479 * 1480 * @since 1.5.0 1481 * 1482 * @param string $text Content that may contain HTML A elements. 1483 * @return string Converted content. 1484 */ 1485 function wp_rel_nofollow( $text ) { 1486 // This is a pre save filter, so text is already escaped. 1487 $text = stripslashes($text); 1488 $text = preg_replace_callback('|<a (.+?)>|i', 'wp_rel_nofollow_callback', $text); 1489 $text = esc_sql($text); 1490 return $text; 1491 } 1492 1493 /** 1494 * Callback to used to add rel=nofollow string to HTML A element. 1495 * 1496 * Will remove already existing rel="nofollow" and rel='nofollow' from the 1497 * string to prevent from invalidating (X)HTML. 1498 * 1499 * @since 2.3.0 1500 * 1501 * @param array $matches Single Match 1502 * @return string HTML A Element with rel nofollow. 1503 */ 1504 function wp_rel_nofollow_callback( $matches ) { 1505 $text = $matches[1]; 1506 $text = str_replace(array(' rel="nofollow"', " rel='nofollow'"), '', $text); 1507 return "<a $text rel=\"nofollow\">"; 1508 } 1509 1510 /** 1511 * Convert one smiley code to the icon graphic file equivalent. 1512 * 1513 * Looks up one smiley code in the $wpsmiliestrans global array and returns an 1514 * <img> string for that smiley. 1515 * 1516 * @global array $wpsmiliestrans 1517 * @since 2.8.0 1518 * 1519 * @param string $smiley Smiley code to convert to image. 1520 * @return string Image string for smiley. 1521 */ 1522 function translate_smiley($smiley) { 1523 global $wpsmiliestrans; 1524 1525 if (count($smiley) == 0) { 1526 return ''; 1527 } 1528 1529 $smiley = trim(reset($smiley)); 1530 $img = $wpsmiliestrans[$smiley]; 1531 $smiley_masked = esc_attr($smiley); 1532 1533 $srcurl = apply_filters('smilies_src', includes_url("images/smilies/$img"), $img, site_url()); 1534 1535 return " <img src='$srcurl' alt='$smiley_masked' class='wp-smiley' /> "; 1536 } 1537 1538 /** 1539 * Convert text equivalent of smilies to images. 1540 * 1541 * Will only convert smilies if the option 'use_smilies' is true and the global 1542 * used in the function isn't empty. 1543 * 1544 * @since 0.71 1545 * @uses $wp_smiliessearch 1546 * 1547 * @param string $text Content to convert smilies from text. 1548 * @return string Converted content with text smilies replaced with images. 1549 */ 1550 function convert_smilies($text) { 1551 global $wp_smiliessearch; 1552 $output = ''; 1553 if ( get_option('use_smilies') && !empty($wp_smiliessearch) ) { 1554 // HTML loop taken from texturize function, could possible be consolidated 1555 $textarr = preg_split("/(<.*>)/U", $text, -1, PREG_SPLIT_DELIM_CAPTURE); // capture the tags as well as in between 1556 $stop = count($textarr);// loop stuff 1557 for ($i = 0; $i < $stop; $i++) { 1558 $content = $textarr[$i]; 1559 if ((strlen($content) > 0) && ('<' != $content[0])) { // If it's not a tag 1560 $content = preg_replace_callback($wp_smiliessearch, 'translate_smiley', $content); 1561 } 1562 $output .= $content; 1563 } 1564 } else { 1565 // return default text. 1566 $output = $text; 1567 } 1568 return $output; 1569 } 1570 1571 /** 1572 * Verifies that an email is valid. 1573 * 1574 * Does not grok i18n domains. Not RFC compliant. 1575 * 1576 * @since 0.71 1577 * 1578 * @param string $email Email address to verify. 1579 * @param boolean $deprecated Deprecated. 1580 * @return string|bool Either false or the valid email address. 1581 */ 1582 function is_email( $email, $deprecated = false ) { 1583 if ( ! empty( $deprecated ) ) 1584 _deprecated_argument( __FUNCTION__, '3.0' ); 1585 1586 // Test for the minimum length the email can be 1587 if ( strlen( $email ) < 3 ) { 1588 return apply_filters( 'is_email', false, $email, 'email_too_short' ); 1589 } 1590 1591 // Test for an @ character after the first position 1592 if ( strpos( $email, '@', 1 ) === false ) { 1593 return apply_filters( 'is_email', false, $email, 'email_no_at' ); 1594 } 1595 1596 // Split out the local and domain parts 1597 list( $local, $domain ) = explode( '@', $email, 2 ); 1598 1599 // LOCAL PART 1600 // Test for invalid characters 1601 if ( !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local ) ) { 1602 return apply_filters( 'is_email', false, $email, 'local_invalid_chars' ); 1603 } 1604 1605 // DOMAIN PART 1606 // Test for sequences of periods 1607 if ( preg_match( '/\.{2,}/', $domain ) ) { 1608 return apply_filters( 'is_email', false, $email, 'domain_period_sequence' ); 1609 } 1610 1611 // Test for leading and trailing periods and whitespace 1612 if ( trim( $domain, " \t\n\r\0\x0B." ) !== $domain ) { 1613 return apply_filters( 'is_email', false, $email, 'domain_period_limits' ); 1614 } 1615 1616 // Split the domain into subs 1617 $subs = explode( '.', $domain ); 1618 1619 // Assume the domain will have at least two subs 1620 if ( 2 > count( $subs ) ) { 1621 return apply_filters( 'is_email', false, $email, 'domain_no_periods' ); 1622 } 1623 1624 // Loop through each sub 1625 foreach ( $subs as $sub ) { 1626 // Test for leading and trailing hyphens and whitespace 1627 if ( trim( $sub, " \t\n\r\0\x0B-" ) !== $sub ) { 1628 return apply_filters( 'is_email', false, $email, 'sub_hyphen_limits' ); 1629 } 1630 1631 // Test for invalid characters 1632 if ( !preg_match('/^[a-z0-9-]+$/i', $sub ) ) { 1633 return apply_filters( 'is_email', false, $email, 'sub_invalid_chars' ); 1634 } 1635 } 1636 1637 // Congratulations your email made it! 1638 return apply_filters( 'is_email', $email, $email, null ); 1639 } 1640 1641 /** 1642 * Convert to ASCII from email subjects. 1643 * 1644 * @since 1.2.0 1645 * @usedby wp_mail() handles charsets in email subjects 1646 * 1647 * @param string $string Subject line 1648 * @return string Converted string to ASCII 1649 */ 1650 function wp_iso_descrambler($string) { 1651 /* this may only work with iso-8859-1, I'm afraid */ 1652 if (!preg_match('#\=\?(.+)\?Q\?(.+)\?\=#i', $string, $matches)) { 1653 return $string; 1654 } else { 1655 $subject = str_replace('_', ' ', $matches[2]); 1656 $subject = preg_replace_callback('#\=([0-9a-f]{2})#i', '_wp_iso_convert', $subject); 1657 return $subject; 1658 } 1659 } 1660 1661 /** 1662 * Helper function to convert hex encoded chars to ascii 1663 * 1664 * @since 3.1.0 1665 * @access private 1666 * @param array $match the preg_replace_callback matches array 1667 */ 1668 function _wp_iso_convert( $match ) { 1669 return chr( hexdec( strtolower( $match[1] ) ) ); 1670 } 1671 1672 /** 1673 * Returns a date in the GMT equivalent. 1674 * 1675 * Requires and returns a date in the Y-m-d H:i:s format. Simply subtracts the 1676 * value of the 'gmt_offset' option. Return format can be overridden using the 1677 * $format parameter. The DateTime and DateTimeZone classes are used to respect 1678 * time zone differences in DST. 1679 * 1680 * @since 1.2.0 1681 * 1682 * @uses get_option() to retrieve the the value of 'gmt_offset'. 1683 * @param string $string The date to be converted. 1684 * @param string $format The format string for the returned date (default is Y-m-d H:i:s) 1685 * @return string GMT version of the date provided. 1686 */ 1687 function get_gmt_from_date($string, $format = 'Y-m-d H:i:s') { 1688 preg_match('#([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})#', $string, $matches); 1689 $tz = get_option('timezone_string'); 1690 if ( $tz ) { 1691 date_default_timezone_set( $tz ); 1692 $datetime = new DateTime( $string ); 1693 $datetime->setTimezone( new DateTimeZone('UTC') ); 1694 $offset = $datetime->getOffset(); 1695 $datetime->modify( '+' . $offset / 3600 . ' hours'); 1696 $string_gmt = gmdate($format, $datetime->format('U')); 1697 1698 date_default_timezone_set('UTC'); 1699 } else { 1700 $string_time = gmmktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 1701 $string_gmt = gmdate($format, $string_time - get_option('gmt_offset') * 3600); 1702 } 1703 return $string_gmt; 1704 } 1705 1706 /** 1707 * Converts a GMT date into the correct format for the blog. 1708 * 1709 * Requires and returns in the Y-m-d H:i:s format. Simply adds the value of 1710 * gmt_offset.Return format can be overridden using the $format parameter 1711 * 1712 * @since 1.2.0 1713 * 1714 * @param string $string The date to be converted. 1715 * @param string $format The format string for the returned date (default is Y-m-d H:i:s) 1716 * @return string Formatted date relative to the GMT offset. 1717 */ 1718 function get_date_from_gmt($string, $format = 'Y-m-d H:i:s') { 1719 preg_match('#([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})#', $string, $matches); 1720 $string_time = gmmktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 1721 $string_localtime = gmdate($format, $string_time + get_option('gmt_offset')*3600); 1722 return $string_localtime; 1723 } 1724 1725 /** 1726 * Computes an offset in seconds from an iso8601 timezone. 1727 * 1728 * @since 1.5.0 1729 * 1730 * @param string $timezone Either 'Z' for 0 offset or '±hhmm'. 1731 * @return int|float The offset in seconds. 1732 */ 1733 function iso8601_timezone_to_offset($timezone) { 1734 // $timezone is either 'Z' or '[+|-]hhmm' 1735 if ($timezone == 'Z') { 1736 $offset = 0; 1737 } else { 1738 $sign = (substr($timezone, 0, 1) == '+') ? 1 : -1; 1739 $hours = intval(substr($timezone, 1, 2)); 1740 $minutes = intval(substr($timezone, 3, 4)) / 60; 1741 $offset = $sign * 3600 * ($hours + $minutes); 1742 } 1743 return $offset; 1744 } 1745 1746 /** 1747 * Converts an iso8601 date to MySQL DateTime format used by post_date[_gmt]. 1748 * 1749 * @since 1.5.0 1750 * 1751 * @param string $date_string Date and time in ISO 8601 format {@link http://en.wikipedia.org/wiki/ISO_8601}. 1752 * @param string $timezone Optional. If set to GMT returns the time minus gmt_offset. Default is 'user'. 1753 * @return string The date and time in MySQL DateTime format - Y-m-d H:i:s. 1754 */ 1755 function iso8601_to_datetime($date_string, $timezone = 'user') { 1756 $timezone = strtolower($timezone); 1757 1758 if ($timezone == 'gmt') { 1759 1760 preg_match('#([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(Z|[\+|\-][0-9]{2,4}){0,1}#', $date_string, $date_bits); 1761 1762 if (!empty($date_bits[7])) { // we have a timezone, so let's compute an offset 1763 $offset = iso8601_timezone_to_offset($date_bits[7]); 1764 } else { // we don't have a timezone, so we assume user local timezone (not server's!) 1765 $offset = 3600 * get_option('gmt_offset'); 1766 } 1767 1768 $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]); 1769 $timestamp -= $offset; 1770 1771 return gmdate('Y-m-d H:i:s', $timestamp); 1772 1773 } else if ($timezone == 'user') { 1774 return preg_replace('#([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(Z|[\+|\-][0-9]{2,4}){0,1}#', '$1-$2-$3 $4:$5:$6', $date_string); 1775 } 1776 } 1777 1778 /** 1779 * Adds a element attributes to open links in new windows. 1780 * 1781 * Comment text in popup windows should be filtered through this. Right now it's 1782 * a moderately dumb function, ideally it would detect whether a target or rel 1783 * attribute was already there and adjust its actions accordingly. 1784 * 1785 * @since 0.71 1786 * 1787 * @param string $text Content to replace links to open in a new window. 1788 * @return string Content that has filtered links. 1789 */ 1790 function popuplinks($text) { 1791 $text = preg_replace('/<a (.+?)>/i', "<a $1 target='_blank' rel='external'>", $text); 1792 return $text; 1793 } 1794 1795 /** 1796 * Strips out all characters that are not allowable in an email. 1797 * 1798 * @since 1.5.0 1799 * 1800 * @param string $email Email address to filter. 1801 * @return string Filtered email address. 1802 */ 1803 function sanitize_email( $email ) { 1804 // Test for the minimum length the email can be 1805 if ( strlen( $email ) < 3 ) { 1806 return apply_filters( 'sanitize_email', '', $email, 'email_too_short' ); 1807 } 1808 1809 // Test for an @ character after the first position 1810 if ( strpos( $email, '@', 1 ) === false ) { 1811 return apply_filters( 'sanitize_email', '', $email, 'email_no_at' ); 1812 } 1813 1814 // Split out the local and domain parts 1815 list( $local, $domain ) = explode( '@', $email, 2 ); 1816 1817 // LOCAL PART 1818 // Test for invalid characters 1819 $local = preg_replace( '/[^a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]/', '', $local ); 1820 if ( '' === $local ) { 1821 return apply_filters( 'sanitize_email', '', $email, 'local_invalid_chars' ); 1822 } 1823 1824 // DOMAIN PART 1825 // Test for sequences of periods 1826 $domain = preg_replace( '/\.{2,}/', '', $domain ); 1827 if ( '' === $domain ) { 1828 return apply_filters( 'sanitize_email', '', $email, 'domain_period_sequence' ); 1829 } 1830 1831 // Test for leading and trailing periods and whitespace 1832 $domain = trim( $domain, " \t\n\r\0\x0B." ); 1833 if ( '' === $domain ) { 1834 return apply_filters( 'sanitize_email', '', $email, 'domain_period_limits' ); 1835 } 1836 1837 // Split the domain into subs 1838 $subs = explode( '.', $domain ); 1839 1840 // Assume the domain will have at least two subs 1841 if ( 2 > count( $subs ) ) { 1842 return apply_filters( 'sanitize_email', '', $email, 'domain_no_periods' ); 1843 } 1844 1845 // Create an array that will contain valid subs 1846 $new_subs = array(); 1847 1848 // Loop through each sub 1849 foreach ( $subs as $sub ) { 1850 // Test for leading and trailing hyphens 1851 $sub = trim( $sub, " \t\n\r\0\x0B-" ); 1852 1853 // Test for invalid characters 1854 $sub = preg_replace( '/[^a-z0-9-]+/i', '', $sub ); 1855 1856 // If there's anything left, add it to the valid subs 1857 if ( '' !== $sub ) { 1858 $new_subs[] = $sub; 1859 } 1860 } 1861 1862 // If there aren't 2 or more valid subs 1863 if ( 2 > count( $new_subs ) ) { 1864 return apply_filters( 'sanitize_email', '', $email, 'domain_no_valid_subs' ); 1865 } 1866 1867 // Join valid subs into the new domain 1868 $domain = join( '.', $new_subs ); 1869 1870 // Put the email back together 1871 $email = $local . '@' . $domain; 1872 1873 // Congratulations your email made it! 1874 return apply_filters( 'sanitize_email', $email, $email, null ); 1875 } 1876 1877 /** 1878 * Determines the difference between two timestamps. 1879 * 1880 * The difference is returned in a human readable format such as "1 hour", 1881 * "5 mins", "2 days". 1882 * 1883 * @since 1.5.0 1884 * 1885 * @param int $from Unix timestamp from which the difference begins. 1886 * @param int $to Optional. Unix timestamp to end the time difference. Default becomes time() if not set. 1887 * @return string Human readable time difference. 1888 */ 1889 function human_time_diff( $from, $to = '' ) { 1890 if ( empty($to) ) 1891 $to = time(); 1892 $diff = (int) abs($to - $from); 1893 if ($diff <= 3600) { 1894 $mins = round($diff / 60); 1895 if ($mins <= 1) { 1896 $mins = 1; 1897 } 1898 /* translators: min=minute */ 1899 $since = sprintf(_n('%s min', '%s mins', $mins), $mins); 1900 } else if (($diff <= 86400) && ($diff > 3600)) { 1901 $hours = round($diff / 3600); 1902 if ($hours <= 1) { 1903 $hours = 1; 1904 } 1905 $since = sprintf(_n('%s hour', '%s hours', $hours), $hours); 1906 } elseif ($diff >= 86400) { 1907 $days = round($diff / 86400); 1908 if ($days <= 1) { 1909 $days = 1; 1910 } 1911 $since = sprintf(_n('%s day', '%s days', $days), $days); 1912 } 1913 return $since; 1914 } 1915 1916 /** 1917 * Generates an excerpt from the content, if needed. 1918 * 1919 * The excerpt word amount will be 55 words and if the amount is greater than 1920 * that, then the string ' [...]' will be appended to the excerpt. If the string 1921 * is less than 55 words, then the content will be returned as is. 1922 * 1923 * The 55 word limit can be modified by plugins/themes using the excerpt_length filter 1924 * The ' [...]' string can be modified by plugins/themes using the excerpt_more filter 1925 * 1926 * @since 1.5.0 1927 * 1928 * @param string $text Optional. The excerpt. If set to empty, an excerpt is generated. 1929 * @return string The excerpt. 1930 */ 1931 function wp_trim_excerpt($text = '') { 1932 $raw_excerpt = $text; 1933 if ( '' == $text ) { 1934 $text = get_the_content(''); 1935 1936 $text = strip_shortcodes( $text ); 1937 1938 $text = apply_filters('the_content', $text); 1939 $text = str_replace(']]>', ']]>', $text); 1940 $excerpt_length = apply_filters('excerpt_length', 55); 1941 $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]'); 1942 $text = wp_trim_words( $text, $excerpt_length, $excerpt_more ); 1943 } 1944 return apply_filters('wp_trim_excerpt', $text, $raw_excerpt); 1945 } 1946 1947 /** 1948 * Trims text to a certain number of words. 1949 * 1950 * @since 3.3.0 1951 * 1952 * @param string $text Text to trim. 1953 * @param int $num_words Number of words. Default 55. 1954 * @param string $more What to append if $text needs to be trimmed. Default '…'. 1955 * @return string Trimmed text. 1956 */ 1957 function wp_trim_words( $text, $num_words = 55, $more = null ) { 1958 if ( null === $more ) 1959 $more = __( '…' ); 1960 $original_text = $text; 1961 $text = wp_strip_all_tags( $text ); 1962 $words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY ); 1963 if ( count( $words_array ) > $num_words ) { 1964 array_pop( $words_array ); 1965 $text = implode( ' ', $words_array ); 1966 $text = $text . $more; 1967 } else { 1968 $text = implode( ' ', $words_array ); 1969 } 1970 return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text ); 1971 } 1972 1973 /** 1974 * Converts named entities into numbered entities. 1975 * 1976 * @since 1.5.1 1977 * 1978 * @param string $text The text within which entities will be converted. 1979 * @return string Text with converted entities. 1980 */ 1981 function ent2ncr($text) { 1982 1983 // Allow a plugin to short-circuit and override the mappings. 1984 $filtered = apply_filters( 'pre_ent2ncr', null, $text ); 1985 if( null !== $filtered ) 1986 return $filtered; 1987 1988 $to_ncr = array( 1989 '"' => '"', 1990 '&' => '&', 1991 '⁄' => '/', 1992 '<' => '<', 1993 '>' => '>', 1994 '|' => '|', 1995 ' ' => ' ', 1996 '¡' => '¡', 1997 '¢' => '¢', 1998 '£' => '£', 1999 '¤' => '¤', 2000 '¥' => '¥', 2001 '¦' => '¦', 2002 '&brkbar;' => '¦', 2003 '§' => '§', 2004 '¨' => '¨', 2005 '¨' => '¨', 2006 '©' => '©', 2007 'ª' => 'ª', 2008 '«' => '«', 2009 '¬' => '¬', 2010 '­' => '­', 2011 '®' => '®', 2012 '¯' => '¯', 2013 '&hibar;' => '¯', 2014 '°' => '°', 2015 '±' => '±', 2016 '²' => '²', 2017 '³' => '³', 2018 '´' => '´', 2019 'µ' => 'µ', 2020 '¶' => '¶', 2021 '·' => '·', 2022 '¸' => '¸', 2023 '¹' => '¹', 2024 'º' => 'º', 2025 '»' => '»', 2026 '¼' => '¼', 2027 '½' => '½', 2028 '¾' => '¾', 2029 '¿' => '¿', 2030 'À' => 'À', 2031 'Á' => 'Á', 2032 'Â' => 'Â', 2033 'Ã' => 'Ã', 2034 'Ä' => 'Ä', 2035 'Å' => 'Å', 2036 'Æ' => 'Æ', 2037 'Ç' => 'Ç', 2038 'È' => 'È', 2039 'É' => 'É', 2040 'Ê' => 'Ê', 2041 'Ë' => 'Ë', 2042 'Ì' => 'Ì', 2043 'Í' => 'Í', 2044 'Î' => 'Î', 2045 'Ï' => 'Ï', 2046 'Ð' => 'Ð', 2047 'Ñ' => 'Ñ', 2048 'Ò' => 'Ò', 2049 'Ó' => 'Ó', 2050 'Ô' => 'Ô', 2051 'Õ' => 'Õ', 2052 'Ö' => 'Ö', 2053 '×' => '×', 2054 'Ø' => 'Ø', 2055 'Ù' => 'Ù', 2056 'Ú' => 'Ú', 2057 'Û' => 'Û', 2058 'Ü' => 'Ü', 2059 'Ý' => 'Ý', 2060 'Þ' => 'Þ', 2061 'ß' => 'ß', 2062 'à' => 'à', 2063 'á' => 'á', 2064 'â' => 'â', 2065 'ã' => 'ã', 2066 'ä' => 'ä', 2067 'å' => 'å', 2068 'æ' => 'æ', 2069 'ç' => 'ç', 2070 'è' => 'è', 2071 'é' => 'é', 2072 'ê' => 'ê', 2073 'ë' => 'ë', 2074 'ì' => 'ì', 2075 'í' => 'í', 2076 'î' => 'î', 2077 'ï' => 'ï', 2078 'ð' => 'ð', 2079 'ñ' => 'ñ', 2080 'ò' => 'ò', 2081 'ó' => 'ó', 2082 'ô' => 'ô', 2083 'õ' => 'õ', 2084 'ö' => 'ö', 2085 '÷' => '÷', 2086 'ø' => 'ø', 2087 'ù' => 'ù', 2088 'ú' => 'ú', 2089 'û' => 'û', 2090 'ü' => 'ü', 2091 'ý' => 'ý', 2092 'þ' => 'þ', 2093 'ÿ' => 'ÿ', 2094 'Œ' => 'Œ', 2095 'œ' => 'œ', 2096 'Š' => 'Š', 2097 'š' => 'š', 2098 'Ÿ' => 'Ÿ', 2099 'ƒ' => 'ƒ', 2100 'ˆ' => 'ˆ', 2101 '˜' => '˜', 2102 'Α' => 'Α', 2103 'Β' => 'Β', 2104 'Γ' => 'Γ', 2105 'Δ' => 'Δ', 2106 'Ε' => 'Ε', 2107 'Ζ' => 'Ζ', 2108 'Η' => 'Η', 2109 'Θ' => 'Θ', 2110 'Ι' => 'Ι', 2111 'Κ' => 'Κ', 2112 'Λ' => 'Λ', 2113 'Μ' => 'Μ', 2114 'Ν' => 'Ν', 2115 'Ξ' => 'Ξ', 2116 'Ο' => 'Ο', 2117 'Π' => 'Π', 2118 'Ρ' => 'Ρ', 2119 'Σ' => 'Σ', 2120 'Τ' => 'Τ', 2121 'Υ' => 'Υ', 2122 'Φ' => 'Φ', 2123 'Χ' => 'Χ', 2124 'Ψ' => 'Ψ', 2125 'Ω' => 'Ω', 2126 'α' => 'α', 2127 'β' => 'β', 2128 'γ' => 'γ', 2129 'δ' => 'δ', 2130 'ε' => 'ε', 2131 'ζ' => 'ζ', 2132 'η' => 'η', 2133 'θ' => 'θ', 2134 'ι' => 'ι', 2135 'κ' => 'κ', 2136 'λ' => 'λ', 2137 'μ' => 'μ', 2138 'ν' => 'ν', 2139 'ξ' => 'ξ', 2140 'ο' => 'ο', 2141 'π' => 'π', 2142 'ρ' => 'ρ', 2143 'ς' => 'ς', 2144 'σ' => 'σ', 2145 'τ' => 'τ', 2146 'υ' => 'υ', 2147 'φ' => 'φ', 2148 'χ' => 'χ', 2149 'ψ' => 'ψ', 2150 'ω' => 'ω', 2151 'ϑ' => 'ϑ', 2152 'ϒ' => 'ϒ', 2153 'ϖ' => 'ϖ', 2154 ' ' => ' ', 2155 ' ' => ' ', 2156 ' ' => ' ', 2157 '‌' => '‌', 2158 '‍' => '‍', 2159 '‎' => '‎', 2160 '‏' => '‏', 2161 '–' => '–', 2162 '—' => '—', 2163 '‘' => '‘', 2164 '’' => '’', 2165 '‚' => '‚', 2166 '“' => '“', 2167 '”' => '”',