[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> formatting.php (source)

   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   * &#8217;cause today&#8217;s effort makes it worth tomorrow&#8217;s &#8220;holiday&#8221;&#8230;
  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( '&#8220;', 'opening curly double quote' );
  38          /* translators: closing curly double quote */
  39          $closing_quote = _x( '&#8221;', 'closing curly double quote' );
  40  
  41          /* translators: apostrophe, for example in 'cause or can't */
  42          $apos = _x( '&#8217;', 'apostrophe' );
  43  
  44          /* translators: prime, for example in 9' (nine feet) */
  45          $prime = _x( '&#8242;', 'prime' );
  46          /* translators: double prime, for example in 9" (nine inches) */
  47          $double_prime = _x( '&#8243;', 'double prime' );
  48  
  49          /* translators: opening curly single quote */
  50          $opening_single_quote = _x( '&#8216;', 'opening curly single quote' );
  51          /* translators: closing curly single quote */
  52          $closing_single_quote = _x( '&#8217;', 'closing curly single quote' );
  53  
  54          /* translators: en dash */
  55          $en_dash = _x( '&#8211;', 'en dash' );
  56          /* translators: em dash */
  57          $em_dash = _x( '&#8212;', '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&#8211;', '...', '``', '\'\'', ' (tm)' ), $cockney );
  74          $static_replacements = array_merge( array( $em_dash, ' ' . $em_dash . ' ', $en_dash, ' ' . $en_dash . ' ', 'xn--', '&#8230;', $opening_quote, $closing_quote, ' &#8482;' ), $cockneyreplace );
  75  
  76          $dynamic = array();
  77          if ( "'" != $apos ) {
  78              $dynamic[ '/\'(\d\d(?:&#8217;|\')?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&#215;$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};)/', '&#038;$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   * &quot;, 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 &amp; 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( "'", '&#039;', $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( '&#039;'  => '\'', '&#x27;' => '\'' );
 468      $single_preg = array( '/&#0*39;/'  => '&#039;', '/&#x0*27;/i' => '&#x27;' );
 469      $double = array( '&quot;' => '"', '&#034;'  => '"', '&#x22;' => '"' );
 470      $double_preg = array( '/&#0*34;/'  => '&#034;', '/&#x0*22;/i' => '&#x22;' );
 471      $others = array( '&lt;'   => '<', '&#060;'  => '<', '&gt;'   => '>', '&#062;'  => '>', '&amp;'  => '&', '&#038;'  => '&', '&#x26;' => '&' );
 472      $others_preg = array( '/&#0*60;/'  => '&#060;', '/&#0*62;/'  => '&#062;', '/&#0*38;/'  => '&#038;', '/&#x0*26;/i' => '&#x26;' );
 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      '&#128;' => '&#8364;', // the Euro sign
1001      '&#129;' => '',
1002      '&#130;' => '&#8218;', // these are Windows CP1252 specific characters
1003      '&#131;' => '&#402;',  // they would look weird on non-Windows browsers
1004      '&#132;' => '&#8222;',
1005      '&#133;' => '&#8230;',
1006      '&#134;' => '&#8224;',
1007      '&#135;' => '&#8225;',
1008      '&#136;' => '&#710;',
1009      '&#137;' => '&#8240;',
1010      '&#138;' => '&#352;',
1011      '&#139;' => '&#8249;',
1012      '&#140;' => '&#338;',
1013      '&#141;' => '',
1014      '&#142;' => '&#382;',
1015      '&#143;' => '',
1016      '&#144;' => '',
1017      '&#145;' => '&#8216;',
1018      '&#146;' => '&#8217;',
1019      '&#147;' => '&#8220;',
1020      '&#148;' => '&#8221;',
1021      '&#149;' => '&#8226;',
1022      '&#150;' => '&#8211;',
1023      '&#151;' => '&#8212;',
1024      '&#152;' => '&#732;',
1025      '&#153;' => '&#8482;',
1026      '&#154;' => '&#353;',
1027      '&#155;' => '&#8250;',
1028      '&#156;' => '&#339;',
1029      '&#157;' => '',
1030      '&#158;' => '',
1031      '&#159;' => '&#376;'
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 &#38; (a.k.a. &amp;)
1039      $content = preg_replace('/&([^#])(?![a-z1-4]{1,8};)/i', '&#038;$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})#', '&lt;$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('@','&#64;',$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(']]>', ']]&gt;', $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 '&hellip;'.
1955   * @return string Trimmed text.
1956   */
1957  function wp_trim_words( $text, $num_words = 55, $more = null ) {
1958      if ( null === $more )
1959          $more = __( '&hellip;' );
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          '&quot;' => '&#34;',
1990          '&amp;' => '&#38;',
1991          '&frasl;' => '&#47;',
1992          '&lt;' => '&#60;',
1993          '&gt;' => '&#62;',
1994          '|' => '&#124;',
1995          '&nbsp;' => '&#160;',
1996          '&iexcl;' => '&#161;',
1997          '&cent;' => '&#162;',
1998          '&pound;' => '&#163;',
1999          '&curren;' => '&#164;',
2000          '&yen;' => '&#165;',
2001          '&brvbar;' => '&#166;',
2002          '&brkbar;' => '&#166;',
2003          '&sect;' => '&#167;',
2004          '&uml;' => '&#168;',
2005          '&die;' => '&#168;',
2006          '&copy;' => '&#169;',
2007          '&ordf;' => '&#170;',
2008          '&laquo;' => '&#171;',
2009          '&not;' => '&#172;',
2010          '&shy;' => '&#173;',
2011          '&reg;' => '&#174;',
2012          '&macr;' => '&#175;',
2013          '&hibar;' => '&#175;',
2014          '&deg;' => '&#176;',
2015          '&plusmn;' => '&#177;',
2016          '&sup2;' => '&#178;',
2017          '&sup3;' => '&#179;',
2018          '&acute;' => '&#180;',
2019          '&micro;' => '&#181;',
2020          '&para;' => '&#182;',
2021          '&middot;' => '&#183;',
2022          '&cedil;' => '&#184;',
2023          '&sup1;' => '&#185;',
2024          '&ordm;' => '&#186;',
2025          '&raquo;' => '&#187;',
2026          '&frac14;' => '&#188;',
2027          '&frac12;' => '&#189;',
2028          '&frac34;' => '&#190;',
2029          '&iquest;' => '&#191;',
2030          '&Agrave;' => '&#192;',
2031          '&Aacute;' => '&#193;',
2032          '&Acirc;' => '&#194;',
2033          '&Atilde;' => '&#195;',
2034          '&Auml;' => '&#196;',
2035          '&Aring;' => '&#197;',
2036          '&AElig;' => '&#198;',
2037          '&Ccedil;' => '&#199;',
2038          '&Egrave;' => '&#200;',
2039          '&Eacute;' => '&#201;',
2040          '&Ecirc;' => '&#202;',
2041          '&Euml;' => '&#203;',
2042          '&Igrave;' => '&#204;',
2043          '&Iacute;' => '&#205;',
2044          '&Icirc;' => '&#206;',
2045          '&Iuml;' => '&#207;',
2046          '&ETH;' => '&#208;',
2047          '&Ntilde;' => '&#209;',
2048          '&Ograve;' => '&#210;',
2049          '&Oacute;' => '&#211;',
2050          '&Ocirc;' => '&#212;',
2051          '&Otilde;' => '&#213;',
2052          '&Ouml;' => '&#214;',
2053          '&times;' => '&#215;',
2054          '&Oslash;' => '&#216;',
2055          '&Ugrave;' => '&#217;',
2056          '&Uacute;' => '&#218;',
2057          '&Ucirc;' => '&#219;',
2058          '&Uuml;' => '&#220;',
2059          '&Yacute;' => '&#221;',
2060          '&THORN;' => '&#222;',
2061          '&szlig;' => '&#223;',
2062          '&agrave;' => '&#224;',
2063          '&aacute;' => '&#225;',
2064          '&acirc;' => '&#226;',
2065          '&atilde;' => '&#227;',
2066          '&auml;' => '&#228;',
2067          '&aring;' => '&#229;',
2068          '&aelig;' => '&#230;',
2069          '&ccedil;' => '&#231;',
2070          '&egrave;' => '&#232;',
2071          '&eacute;' => '&#233;',
2072          '&ecirc;' => '&#234;',
2073          '&euml;' => '&#235;',
2074          '&igrave;' => '&#236;',
2075          '&iacute;' => '&#237;',
2076          '&icirc;' => '&#238;',
2077          '&iuml;' => '&#239;',
2078          '&eth;' => '&#240;',
2079          '&ntilde;' => '&#241;',
2080          '&ograve;' => '&#242;',
2081          '&oacute;' => '&#243;',
2082          '&ocirc;' => '&#244;',
2083          '&otilde;' => '&#245;',
2084          '&ouml;' => '&#246;',
2085          '&divide;' => '&#247;',
2086          '&oslash;' => '&#248;',
2087          '&ugrave;' => '&#249;',
2088          '&uacute;' => '&#250;',
2089          '&ucirc;' => '&#251;',
2090          '&uuml;' => '&#252;',
2091          '&yacute;' => '&#253;',
2092          '&thorn;' => '&#254;',
2093          '&yuml;' => '&#255;',
2094          '&OElig;' => '&#338;',
2095          '&oelig;' => '&#339;',
2096          '&Scaron;' => '&#352;',
2097          '&scaron;' => '&#353;',
2098          '&Yuml;' => '&#376;',
2099          '&fnof;' => '&#402;',
2100          '&circ;' => '&#710;',
2101          '&tilde;' => '&#732;',
2102          '&Alpha;' => '&#913;',
2103          '&Beta;' => '&#914;',
2104          '&Gamma;' => '&#915;',
2105          '&Delta;' => '&#916;',
2106          '&Epsilon;' => '&#917;',
2107          '&Zeta;' => '&#918;',
2108          '&Eta;' => '&#919;',
2109          '&Theta;' => '&#920;',
2110          '&Iota;' => '&#921;',
2111          '&Kappa;' => '&#922;',
2112          '&Lambda;' => '&#923;',
2113          '&Mu;' => '&#924;',
2114          '&Nu;' => '&#925;',
2115          '&Xi;' => '&#926;',
2116          '&Omicron;' => '&#927;',
2117          '&Pi;' => '&#928;',
2118          '&Rho;' => '&#929;',
2119          '&Sigma;' => '&#931;',
2120          '&Tau;' => '&#932;',
2121          '&Upsilon;' => '&#933;',
2122          '&Phi;' => '&#934;',
2123          '&Chi;' => '&#935;',
2124          '&Psi;' => '&#936;',
2125          '&Omega;' => '&#937;',
2126          '&alpha;' => '&#945;',
2127          '&beta;' => '&#946;',
2128          '&gamma;' => '&#947;',
2129          '&delta;' => '&#948;',
2130          '&epsilon;' => '&#949;',
2131          '&zeta;' => '&#950;',
2132          '&eta;' => '&#951;',
2133          '&theta;' => '&#952;',
2134          '&iota;' => '&#953;',
2135          '&kappa;' => '&#954;',
2136          '&lambda;' => '&#955;',
2137          '&mu;' => '&#956;',
2138          '&nu;' => '&#957;',
2139          '&xi;' => '&#958;',
2140          '&omicron;' => '&#959;',
2141          '&pi;' => '&#960;',
2142          '&rho;' => '&#961;',
2143          '&sigmaf;' => '&#962;',
2144          '&sigma;' => '&#963;',
2145          '&tau;' => '&#964;',
2146          '&upsilon;' => '&#965;',
2147          '&phi;' => '&#966;',
2148          '&chi;' => '&#967;',
2149          '&psi;' => '&#968;',
2150          '&omega;' => '&#969;',
2151          '&thetasym;' => '&#977;',
2152          '&upsih;' => '&#978;',
2153          '&piv;' => '&#982;',
2154          '&ensp;' => '&#8194;',
2155          '&emsp;' => '&#8195;',
2156          '&thinsp;' => '&#8201;',
2157          '&zwnj;' => '&#8204;',
2158          '&zwj;' => '&#8205;',
2159          '&lrm;' => '&#8206;',
2160          '&rlm;' => '&#8207;',
2161          '&ndash;' => '&#8211;',
2162          '&mdash;' => '&#8212;',
2163          '&lsquo;' => '&#8216;',
2164          '&rsquo;' => '&#8217;',
2165          '&sbquo;' => '&#8218;',
2166          '&ldquo;' => '&#8220;',
2167          '&rdquo;' => '&#8221;',