[ Index ]

PHP Cross Reference of WordPress 3.0 beta 1

[ Index ]     [ Variables ]     [ Functions ]     [ Classes ]     [ Constants ]     [ Statistics ]

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_setup = false, $opening_quote, $closing_quote, $default_no_texturize_tags, $default_no_texturize_shortcodes, $static_characters, $static_replacements, $dynamic_characters, $dynamic_replacements;
  32      $output = '';
  33      $curl = '';
  34      $textarr = preg_split('/(<.*>|\[.*\])/Us', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
  35      $stop = count($textarr);
  36  
  37      // No need to set up these variables more than once
  38      if (!$static_setup) {
  39          /* translators: opening curly quote */
  40          $opening_quote = _x('&#8220;', 'opening curly quote');
  41          /* translators: closing curly quote */
  42          $closing_quote = _x('&#8221;', 'closing curly quote');
  43  
  44          $default_no_texturize_tags = array('pre', 'code', 'kbd', 'style', 'script', 'tt');
  45          $default_no_texturize_shortcodes = array('code');
  46  
  47          // if a plugin has provided an autocorrect array, use it
  48          if ( isset($wp_cockneyreplace) ) {
  49              $cockney = array_keys($wp_cockneyreplace);
  50              $cockneyreplace = array_values($wp_cockneyreplace);
  51          } else {
  52              $cockney = array("'tain't","'twere","'twas","'tis","'twill","'til","'bout","'nuff","'round","'cause");
  53              $cockneyreplace = array("&#8217;tain&#8217;t","&#8217;twere","&#8217;twas","&#8217;tis","&#8217;twill","&#8217;til","&#8217;bout","&#8217;nuff","&#8217;round","&#8217;cause");
  54          }
  55  
  56          $static_characters = array_merge(array('---', ' -- ', '--', ' - ', 'xn&#8211;', '...', '``', '\'s', '\'\'', ' (tm)'), $cockney);
  57          $static_replacements = array_merge(array('&#8212;', ' &#8212; ', '&#8211;', ' &#8211; ', 'xn--', '&#8230;', $opening_quote, '&#8217;s', $closing_quote, ' &#8482;'), $cockneyreplace);
  58  
  59          $dynamic_characters = array('/\'(\d\d(?:&#8217;|\')?s)/', '/(\s|\A|[([{<]|")\'/', '/(\d+)"/', '/(\d+)\'/', '/(\S)\'([^\'\s])/', '/(\s|\A|[([{<])"(?!\s)/', '/"(\s|\S|\Z)/', '/\'([\s.]|\Z)/', '/(\d+)x(\d+)/');
  60          $dynamic_replacements = array('&#8217;$1','$1&#8216;', '$1&#8243;', '$1&#8242;', '$1&#8217;$2', '$1' . $opening_quote . '$2', $closing_quote . '$1', '&#8217;$1', '$1&#215;$2');
  61  
  62          $static_setup = true;
  63      }
  64  
  65      // Transform into regexp sub-expression used in _wptexturize_pushpop_element
  66      // Must do this everytime in case plugins use these filters in a context sensitive manner
  67      $no_texturize_tags = '(' . implode('|', apply_filters('no_texturize_tags', $default_no_texturize_tags) ) . ')';
  68      $no_texturize_shortcodes = '(' . implode('|', apply_filters('no_texturize_shortcodes', $default_no_texturize_shortcodes) ) . ')';
  69  
  70      $no_texturize_tags_stack = array();
  71      $no_texturize_shortcodes_stack = array();
  72  
  73      for ( $i = 0; $i < $stop; $i++ ) {
  74          $curl = $textarr[$i];
  75  
  76          if ( !empty($curl) && '<' != $curl{0} && '[' != $curl{0}
  77                  && empty($no_texturize_shortcodes_stack) && empty($no_texturize_tags_stack)) {
  78              // This is not a tag, nor is the texturization disabled
  79              // static strings
  80              $curl = str_replace($static_characters, $static_replacements, $curl);
  81              // regular expressions
  82              $curl = preg_replace($dynamic_characters, $dynamic_replacements, $curl);
  83          } elseif (!empty($curl)) {
  84              /*
  85               * Only call _wptexturize_pushpop_element if first char is correct
  86               * tag opening
  87               */
  88              if ('<' == $curl{0})
  89                  _wptexturize_pushpop_element($curl, $no_texturize_tags_stack, $no_texturize_tags, '<', '>');
  90              elseif ('[' == $curl{0})
  91                  _wptexturize_pushpop_element($curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes, '[', ']');
  92          }
  93  
  94          $curl = preg_replace('/&([^#])(?![a-zA-Z1-4]{1,8};)/', '&#038;$1', $curl);
  95          $output .= $curl;
  96      }
  97  
  98      return $output;
  99  }
 100  
 101  /**
 102   * Search for disabled element tags. Push element to stack on tag open and pop
 103   * on tag close. Assumes first character of $text is tag opening.
 104   *
 105   * @access private
 106   * @since 2.9.0
 107   *
 108   * @param string $text Text to check. First character is assumed to be $opening
 109   * @param array $stack Array used as stack of opened tag elements
 110   * @param string $disabled_elements Tags to match against formatted as regexp sub-expression
 111   * @param string $opening Tag opening character, assumed to be 1 character long
 112   * @param string $opening Tag closing  character
 113   * @return object
 114   */
 115  function _wptexturize_pushpop_element($text, &$stack, $disabled_elements, $opening = '<', $closing = '>') {
 116      // Check if it is a closing tag -- otherwise assume opening tag
 117      if (strncmp($opening . '/', $text, 2)) {
 118          // Opening? Check $text+1 against disabled elements
 119          if (preg_match('/^' . $disabled_elements . '\b/', substr($text, 1), $matches)) {
 120              /*
 121               * This disables texturize until we find a closing tag of our type
 122               * (e.g. <pre>) even if there was invalid nesting before that
 123               *
 124               * Example: in the case <pre>sadsadasd</code>"baba"</pre>
 125               *          "baba" won't be texturize
 126               */
 127  
 128              array_push($stack, $matches[1]);
 129          }
 130      } else {
 131          // Closing? Check $text+2 against disabled elements
 132          $c = preg_quote($closing, '/');
 133          if (preg_match('/^' . $disabled_elements . $c . '/', substr($text, 2), $matches)) {
 134              $last = array_pop($stack);
 135  
 136              // Make sure it matches the opening tag
 137              if ($last != $matches[1])
 138                  array_push($stack, $last);
 139          }
 140      }
 141  }
 142  
 143  /**
 144   * Accepts matches array from preg_replace_callback in wpautop() or a string.
 145   *
 146   * Ensures that the contents of a <<pre>>...<</pre>> HTML block are not
 147   * converted into paragraphs or line-breaks.
 148   *
 149   * @since 1.2.0
 150   *
 151   * @param array|string $matches The array or string
 152   * @return string The pre block without paragraph/line-break conversion.
 153   */
 154  function clean_pre($matches) {
 155      if ( is_array($matches) )
 156          $text = $matches[1] . $matches[2] . "</pre>";
 157      else
 158          $text = $matches;
 159  
 160      $text = str_replace('<br />', '', $text);
 161      $text = str_replace('<p>', "\n", $text);
 162      $text = str_replace('</p>', '', $text);
 163  
 164      return $text;
 165  }
 166  
 167  /**
 168   * Replaces double line-breaks with paragraph elements.
 169   *
 170   * A group of regex replaces used to identify text formatted with newlines and
 171   * replace double line-breaks with HTML paragraph tags. The remaining
 172   * line-breaks after conversion become <<br />> tags, unless $br is set to '0'
 173   * or 'false'.
 174   *
 175   * @since 0.71
 176   *
 177   * @param string $pee The text which has to be formatted.
 178   * @param int|bool $br Optional. If set, this will convert all remaining line-breaks after paragraphing. Default true.
 179   * @return string Text which has been converted into correct paragraph tags.
 180   */
 181  function wpautop($pee, $br = 1) {
 182  
 183      if ( trim($pee) === '' )
 184          return '';
 185      $pee = $pee . "\n"; // just to make things a little easier, pad the end
 186      $pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee);
 187      // Space things out a little
 188      $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)';
 189      $pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
 190      $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
 191      $pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
 192      if ( strpos($pee, '<object') !== false ) {
 193          $pee = preg_replace('|\s*<param([^>]*)>\s*|', "<param$1>", $pee); // no pee inside object/embed
 194          $pee = preg_replace('|\s*</embed>\s*|', '</embed>', $pee);
 195      }
 196      $pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates
 197      // make paragraphs, including one at the end
 198      $pees = preg_split('/\n\s*\n/', $pee, -1, PREG_SPLIT_NO_EMPTY);
 199      $pee = '';
 200      foreach ( $pees as $tinkle )
 201          $pee .= '<p>' . trim($tinkle, "\n") . "</p>\n";
 202      $pee = preg_replace('|<p>\s*</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace
 203      $pee = preg_replace('!<p>([^<]+)</(div|address|form)>!', "<p>$1</p></$2>", $pee);
 204      $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag
 205      $pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists
 206      $pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
 207      $pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
 208      $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
 209      $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
 210      if ($br) {
 211          $pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', create_function('$matches', 'return str_replace("\n", "<WPPreserveNewline />", $matches[0]);'), $pee);
 212          $pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks
 213          $pee = str_replace('<WPPreserveNewline />', "\n", $pee);
 214      }
 215      $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee);
 216      $pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
 217      if (strpos($pee, '<pre') !== false)
 218          $pee = preg_replace_callback('!(<pre[^>]*>)(.*?)</pre>!is', 'clean_pre', $pee );
 219      $pee = preg_replace( "|\n</p>$|", '</p>', $pee );
 220  
 221      return $pee;
 222  }
 223  
 224  /**
 225   * Don't auto-p wrap shortcodes that stand alone
 226   *
 227   * Ensures that shortcodes are not wrapped in <<p>>...<</p>>.
 228   *
 229   * @since 2.9.0
 230   *
 231   * @param string $pee The content.
 232   * @return string The filtered content.
 233   */
 234  function shortcode_unautop($pee) {
 235      global $shortcode_tags;
 236  
 237      if ( !empty($shortcode_tags) && is_array($shortcode_tags) ) {
 238          $tagnames = array_keys($shortcode_tags);
 239          $tagregexp = join( '|', array_map('preg_quote', $tagnames) );
 240          $pee = preg_replace('/<p>\\s*?(\\[(' . $tagregexp . ')\\b.*?\\/?\\](?:.+?\\[\\/\\2\\])?)\\s*<\\/p>/s', '$1', $pee);
 241      }
 242  
 243      return $pee;
 244  }
 245  
 246  /**
 247   * Checks to see if a string is utf8 encoded.
 248   *
 249   * NOTE: This function checks for 5-Byte sequences, UTF8
 250   *       has Bytes Sequences with a maximum length of 4.
 251   *
 252   * @author bmorel at ssi dot fr (modified)
 253   * @since 1.2.1
 254   *
 255   * @param string $str The string to be checked
 256   * @return bool True if $str fits a UTF-8 model, false otherwise.
 257   */
 258  function seems_utf8($str) {
 259      $length = strlen($str);
 260      for ($i=0; $i < $length; $i++) {
 261          $c = ord($str[$i]);
 262          if ($c < 0x80) $n = 0; # 0bbbbbbb
 263          elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
 264          elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
 265          elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
 266          elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
 267          elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
 268          else return false; # Does not match any model
 269          for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
 270              if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
 271                  return false;
 272          }
 273      }
 274      return true;
 275  }
 276  
 277  /**
 278   * Converts a number of special characters into their HTML entities.
 279   *
 280   * Specifically deals with: &, <, >, ", and '.
 281   *
 282   * $quote_style can be set to ENT_COMPAT to encode " to
 283   * &quot;, or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded.
 284   *
 285   * @since 1.2.2
 286   *
 287   * @param string $string The text which is to be encoded.
 288   * @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.
 289   * @param string $charset Optional. The character encoding of the string. Default is false.
 290   * @param boolean $double_encode Optional. Whether to encode existing html entities. Default is false.
 291   * @return string The encoded text with HTML entities.
 292   */
 293  function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) {
 294      $string = (string) $string;
 295  
 296      if ( 0 === strlen( $string ) ) {
 297          return '';
 298      }
 299  
 300      // Don't bother if there are no specialchars - saves some processing
 301      if ( !preg_match( '/[&<>"\']/', $string ) ) {
 302          return $string;
 303      }
 304  
 305      // Account for the previous behaviour of the function when the $quote_style is not an accepted value
 306      if ( empty( $quote_style ) ) {
 307          $quote_style = ENT_NOQUOTES;
 308      } elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
 309          $quote_style = ENT_QUOTES;
 310      }
 311  
 312      // Store the site charset as a static to avoid multiple calls to wp_load_alloptions()
 313      if ( !$charset ) {
 314          static $_charset;
 315          if ( !isset( $_charset ) ) {
 316              $alloptions = wp_load_alloptions();
 317              $_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : '';
 318          }
 319          $charset = $_charset;
 320      }
 321      if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) ) {
 322          $charset = 'UTF-8';
 323      }
 324  
 325      $_quote_style = $quote_style;
 326  
 327      if ( $quote_style === 'double' ) {
 328          $quote_style = ENT_COMPAT;
 329          $_quote_style = ENT_COMPAT;
 330      } elseif ( $quote_style === 'single' ) {
 331          $quote_style = ENT_NOQUOTES;
 332      }
 333  
 334      // Handle double encoding ourselves
 335      if ( !$double_encode ) {
 336          $string = wp_specialchars_decode( $string, $_quote_style );
 337  
 338          /* Critical */
 339          // The previous line decodes &amp;phrase; into &phrase;  We must guarantee that &phrase; is valid before proceeding.
 340          $string = wp_kses_normalize_entities($string);
 341  
 342          // Now proceed with custom double-encoding silliness
 343          $string = preg_replace( '/&(#?x?[0-9a-z]+);/i', '|wp_entity|$1|/wp_entity|', $string );
 344      }
 345  
 346      $string = @htmlspecialchars( $string, $quote_style, $charset );
 347  
 348      // Handle double encoding ourselves
 349      if ( !$double_encode ) {
 350          $string = str_replace( array( '|wp_entity|', '|/wp_entity|' ), array( '&', ';' ), $string );
 351      }
 352  
 353      // Backwards compatibility
 354      if ( 'single' === $_quote_style ) {
 355          $string = str_replace( "'", '&#039;', $string );
 356      }
 357  
 358      return $string;
 359  }
 360  
 361  /**
 362   * Converts a number of HTML entities into their special characters.
 363   *
 364   * Specifically deals with: &, <, >, ", and '.
 365   *
 366   * $quote_style can be set to ENT_COMPAT to decode " entities,
 367   * or ENT_QUOTES to do both " and '. Default is ENT_NOQUOTES where no quotes are decoded.
 368   *
 369   * @since 2.8
 370   *
 371   * @param string $string The text which is to be decoded.
 372   * @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.
 373   * @return string The decoded text without HTML entities.
 374   */
 375  function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) {
 376      $string = (string) $string;
 377  
 378      if ( 0 === strlen( $string ) ) {
 379          return '';
 380      }
 381  
 382      // Don't bother if there are no entities - saves a lot of processing
 383      if ( strpos( $string, '&' ) === false ) {
 384          return $string;
 385      }
 386  
 387      // Match the previous behaviour of _wp_specialchars() when the $quote_style is not an accepted value
 388      if ( empty( $quote_style ) ) {
 389          $quote_style = ENT_NOQUOTES;
 390      } elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
 391          $quote_style = ENT_QUOTES;
 392      }
 393  
 394      // More complete than get_html_translation_table( HTML_SPECIALCHARS )
 395      $single = array( '&#039;'  => '\'', '&#x27;' => '\'' );
 396      $single_preg = array( '/&#0*39;/'  => '&#039;', '/&#x0*27;/i' => '&#x27;' );
 397      $double = array( '&quot;' => '"', '&#034;'  => '"', '&#x22;' => '"' );
 398      $double_preg = array( '/&#0*34;/'  => '&#034;', '/&#x0*22;/i' => '&#x22;' );
 399      $others = array( '&lt;'   => '<', '&#060;'  => '<', '&gt;'   => '>', '&#062;'  => '>', '&amp;'  => '&', '&#038;'  => '&', '&#x26;' => '&' );
 400      $others_preg = array( '/&#0*60;/'  => '&#060;', '/&#0*62;/'  => '&#062;', '/&#0*38;/'  => '&#038;', '/&#x0*26;/i' => '&#x26;' );
 401  
 402      if ( $quote_style === ENT_QUOTES ) {
 403          $translation = array_merge( $single, $double, $others );
 404          $translation_preg = array_merge( $single_preg, $double_preg, $others_preg );
 405      } elseif ( $quote_style === ENT_COMPAT || $quote_style === 'double' ) {
 406          $translation = array_merge( $double, $others );
 407          $translation_preg = array_merge( $double_preg, $others_preg );
 408      } elseif ( $quote_style === 'single' ) {
 409          $translation = array_merge( $single, $others );
 410          $translation_preg = array_merge( $single_preg, $others_preg );
 411      } elseif ( $quote_style === ENT_NOQUOTES ) {
 412          $translation = $others;
 413          $translation_preg = $others_preg;
 414      }
 415  
 416      // Remove zero padding on numeric entities
 417      $string = preg_replace( array_keys( $translation_preg ), array_values( $translation_preg ), $string );
 418  
 419      // Replace characters according to translation table
 420      return strtr( $string, $translation );
 421  }
 422  
 423  /**
 424   * Checks for invalid UTF8 in a string.
 425   *
 426   * @since 2.8
 427   *
 428   * @param string $string The text which is to be checked.
 429   * @param boolean $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false.
 430   * @return string The checked text.
 431   */
 432  function wp_check_invalid_utf8( $string, $strip = false ) {
 433      $string = (string) $string;
 434  
 435      if ( 0 === strlen( $string ) ) {
 436          return '';
 437      }
 438  
 439      // Store the site charset as a static to avoid multiple calls to get_option()
 440      static $is_utf8;
 441      if ( !isset( $is_utf8 ) ) {
 442          $is_utf8 = in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) );
 443      }
 444      if ( !$is_utf8 ) {
 445          return $string;
 446      }
 447  
 448      // Check for support for utf8 in the installed PCRE library once and store the result in a static
 449      static $utf8_pcre;
 450      if ( !isset( $utf8_pcre ) ) {
 451          $utf8_pcre = @preg_match( '/^./u', 'a' );
 452      }
 453      // We can't demand utf8 in the PCRE installation, so just return the string in those cases
 454      if ( !$utf8_pcre ) {
 455          return $string;
 456      }
 457  
 458      // preg_match fails when it encounters invalid UTF8 in $string
 459      if ( 1 === @preg_match( '/^./us', $string ) ) {
 460          return $string;
 461      }
 462  
 463      // Attempt to strip the bad chars if requested (not recommended)
 464      if ( $strip && function_exists( 'iconv' ) ) {
 465          return iconv( 'utf-8', 'utf-8', $string );
 466      }
 467  
 468      return '';
 469  }
 470  
 471  /**
 472   * Encode the Unicode values to be used in the URI.
 473   *
 474   * @since 1.5.0
 475   *
 476   * @param string $utf8_string
 477   * @param int $length Max length of the string
 478   * @return string String with Unicode encoded for URI.
 479   */
 480  function utf8_uri_encode( $utf8_string, $length = 0 ) {
 481      $unicode = '';
 482      $values = array();
 483      $num_octets = 1;
 484      $unicode_length = 0;
 485  
 486      $string_length = strlen( $utf8_string );
 487      for ($i = 0; $i < $string_length; $i++ ) {
 488  
 489          $value = ord( $utf8_string[ $i ] );
 490  
 491          if ( $value < 128 ) {
 492              if ( $length && ( $unicode_length >= $length ) )
 493                  break;
 494              $unicode .= chr($value);
 495              $unicode_length++;
 496          } else {
 497              if ( count( $values ) == 0 ) $num_octets = ( $value < 224 ) ? 2 : 3;
 498  
 499              $values[] = $value;
 500  
 501              if ( $length && ( $unicode_length + ($num_octets * 3) ) > $length )
 502                  break;
 503              if ( count( $values ) == $num_octets ) {
 504                  if ($num_octets == 3) {
 505                      $unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]) . '%' . dechex($values[2]);
 506                      $unicode_length += 9;
 507                  } else {
 508                      $unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]);
 509                      $unicode_length += 6;
 510                  }
 511  
 512                  $values = array();
 513                  $num_octets = 1;
 514              }
 515          }
 516      }
 517  
 518      return $unicode;
 519  }
 520  
 521  /**
 522   * Converts all accent characters to ASCII characters.
 523   *
 524   * If there are no accent characters, then the string given is just returned.
 525   *
 526   * @since 1.2.1
 527   *
 528   * @param string $string Text that might have accent characters
 529   * @return string Filtered string with replaced "nice" characters.
 530   */
 531  function remove_accents($string) {
 532      if ( !preg_match('/[\x80-\xff]/', $string) )
 533          return $string;
 534  
 535      if (seems_utf8($string)) {
 536          $chars = array(
 537          // Decompositions for Latin-1 Supplement
 538          chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
 539          chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
 540          chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
 541          chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
 542          chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
 543          chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
 544          chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
 545          chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
 546          chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
 547          chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
 548          chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
 549          chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
 550          chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
 551          chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
 552          chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
 553          chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
 554          chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
 555          chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
 556          chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
 557          chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
 558          chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
 559          chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
 560          chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
 561          chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
 562          chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
 563          chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
 564          chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
 565          chr(195).chr(191) => 'y',
 566          // Decompositions for Latin Extended-A
 567          chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
 568          chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
 569          chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
 570          chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
 571          chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
 572          chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
 573          chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
 574          chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
 575          chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
 576          chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
 577          chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
 578          chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
 579          chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
 580          chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
 581          chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
 582          chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
 583          chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
 584          chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
 585          chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
 586          chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
 587          chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
 588          chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
 589          chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
 590          chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
 591          chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
 592          chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
 593          chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
 594          chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
 595          chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
 596          chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
 597          chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
 598          chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
 599          chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
 600          chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
 601          chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
 602          chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
 603          chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
 604          chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
 605          chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
 606          chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
 607          chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
 608          chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
 609          chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
 610          chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
 611          chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
 612          chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
 613          chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
 614          chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
 615          chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
 616          chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
 617          chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
 618          chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
 619          chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
 620          chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
 621          chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
 622          chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
 623          chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
 624          chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
 625          chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
 626          chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
 627          chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
 628          chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
 629          chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
 630          chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
 631          // Euro Sign
 632          chr(226).chr(130).chr(172) => 'E',
 633          // GBP (Pound) Sign
 634          chr(194).chr(163) => '');
 635  
 636          $string = strtr($string, $chars);
 637      } else {
 638          // Assume ISO-8859-1 if not UTF-8
 639          $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
 640              .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
 641              .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
 642              .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
 643              .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
 644              .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
 645              .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
 646              .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
 647              .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
 648              .chr(252).chr(253).chr(255);
 649  
 650          $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
 651  
 652          $string = strtr($string, $chars['in'], $chars['out']);
 653          $double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
 654          $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
 655          $string = str_replace($double_chars['in'], $double_chars['out'], $string);
 656      }
 657  
 658      return $string;
 659  }
 660  
 661  /**
 662   * Sanitizes a filename replacing whitespace with dashes
 663   *
 664   * Removes special characters that are illegal in filenames on certain
 665   * operating systems and special characters requiring special escaping
 666   * to manipulate at the command line. Replaces spaces and consecutive
 667   * dashes with a single dash. Trim period, dash and underscore from beginning
 668   * and end of filename.
 669   *
 670   * @since 2.1.0
 671   *
 672   * @param string $filename The filename to be sanitized
 673   * @return string The sanitized filename
 674   */
 675  function sanitize_file_name( $filename ) {
 676      $filename_raw = $filename;
 677      $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", chr(0));
 678      $special_chars = apply_filters('sanitize_file_name_chars', $special_chars, $filename_raw);
 679      $filename = str_replace($special_chars, '', $filename);
 680      $filename = preg_replace('/[\s-]+/', '-', $filename);
 681      $filename = trim($filename, '.-_');
 682  
 683      // Split the filename into a base and extension[s]
 684      $parts = explode('.', $filename);
 685  
 686      // Return if only one extension
 687      if ( count($parts) <= 2 )
 688          return apply_filters('sanitize_file_name', $filename, $filename_raw);
 689  
 690      // Process multiple extensions
 691      $filename = array_shift($parts);
 692      $extension = array_pop($parts);
 693      $mimes = get_allowed_mime_types();
 694  
 695      // Loop over any intermediate extensions.  Munge them with a trailing underscore if they are a 2 - 5 character
 696      // long alpha string not in the extension whitelist.
 697      foreach ( (array) $parts as $part) {
 698          $filename .= '.' . $part;
 699  
 700          if ( preg_match("/^[a-zA-Z]{2,5}\d?$/", $part) ) {
 701              $allowed = false;
 702              foreach ( $mimes as $ext_preg => $mime_match ) {
 703                  $ext_preg = '!(^' . $ext_preg . ')$!i';
 704                  if ( preg_match( $ext_preg, $part ) ) {
 705                      $allowed = true;
 706                      break;
 707                  }
 708              }
 709              if ( !$allowed )
 710                  $filename .= '_';
 711          }
 712      }
 713      $filename .= '.' . $extension;
 714  
 715      return apply_filters('sanitize_file_name', $filename, $filename_raw);
 716  }
 717  
 718  /**
 719   * Sanitize username stripping out unsafe characters.
 720   *
 721   * If $strict is true, only alphanumeric characters (as well as _, space, ., -,
 722   * @) are returned.
 723   * Removes tags, octets, entities, and if strict is enabled, will remove all
 724   * non-ASCII characters. After sanitizing, it passes the username, raw username
 725   * (the username in the parameter), and the strict parameter as parameters for
 726   * the filter.
 727   *
 728   * @since 2.0.0
 729   * @uses apply_filters() Calls 'sanitize_user' hook on username, raw username,
 730   *        and $strict parameter.
 731   *
 732   * @param string $username The username to be sanitized.
 733   * @param bool $strict If set limits $username to specific characters. Default false.
 734   * @return string The sanitized username, after passing through filters.
 735   */
 736  function sanitize_user( $username, $strict = false ) {
 737      $raw_username = $username;
 738      $username = wp_strip_all_tags($username);
 739      // Kill octets
 740      $username = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '', $username);
 741      $username = preg_replace('/&.+?;/', '', $username); // Kill entities
 742  
 743      // If strict, reduce to ASCII for max portability.
 744      if ( $strict )
 745          $username = preg_replace('|[^a-z0-9 _.\-@]|i', '', $username);
 746  
 747      // Consolidate contiguous whitespace
 748      $username = preg_replace('|\s+|', ' ', $username);
 749  
 750      return apply_filters('sanitize_user', $username, $raw_username, $strict);
 751  }
 752  
 753  /**
 754   * Sanitize a string key.
 755   *
 756   * Keys are used as internal identifiers. They should be lowercase ASCII.  Dashes and underscores are allowed.
 757   *
 758   * @since 3.0.0
 759   *
 760   * @param string $key String key
 761   * @return string Sanitized key
 762   */
 763  function sanitize_key( $key ) {
 764      $raw_key = $key;
 765      $key = wp_strip_all_tags($key);
 766      // Kill octets
 767      $key = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '', $key);
 768      $key = preg_replace('/&.+?;/', '', $key); // Kill entities
 769  
 770      $key = preg_replace('|[^a-z0-9 _.\-@]|i', '', $key);
 771  
 772      // Consolidate contiguous whitespace
 773      $key = preg_replace('|\s+|', ' ', $key);
 774  
 775      return apply_filters('sanitize_key', $key, $raw_key);
 776  }
 777  
 778  /**
 779   * Sanitizes title or use fallback title.
 780   *
 781   * Specifically, HTML and PHP tags are stripped. Further actions can be added
 782   * via the plugin API. If $title is empty and $fallback_title is set, the latter
 783   * will be used.
 784   *
 785   * @since 1.0.0
 786   *
 787   * @param string $title The string to be sanitized.
 788   * @param string $fallback_title Optional. A title to use if $title is empty.
 789   * @return string The sanitized string.
 790   */
 791  function sanitize_title($title, $fallback_title = '') {
 792      $raw_title = $title;
 793      $title = strip_tags($title);
 794      $title = apply_filters('sanitize_title', $title, $raw_title);
 795  
 796      if ( '' === $title || false === $title )
 797          $title = $fallback_title;
 798  
 799      return $title;
 800  }
 801  
 802  /**
 803   * Sanitizes title, replacing whitespace with dashes.
 804   *
 805   * Limits the output to alphanumeric characters, underscore (_) and dash (-).
 806   * Whitespace becomes a dash.
 807   *
 808   * @since 1.2.0
 809   *
 810   * @param string $title The title to be sanitized.
 811   * @return string The sanitized title.
 812   */
 813  function sanitize_title_with_dashes($title) {
 814      $title = strip_tags($title);
 815      // Preserve escaped octets.
 816      $title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title);
 817      // Remove percent signs that are not part of an octet.
 818      $title = str_replace('%', '', $title);
 819      // Restore octets.
 820      $title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title);
 821  
 822      $title = remove_accents($title);
 823      if (seems_utf8($title)) {
 824          if (function_exists('mb_strtolower')) {
 825              $title = mb_strtolower($title, 'UTF-8');
 826          }
 827          $title = utf8_uri_encode($title, 200);
 828      }
 829  
 830      $title = strtolower($title);
 831      $title = preg_replace('/&.+?;/', '', $title); // kill entities
 832      $title = str_replace('.', '-', $title);
 833      $title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
 834      $title = preg_replace('/\s+/', '-', $title);
 835      $title = preg_replace('|-+|', '-', $title);
 836      $title = trim($title, '-');
 837  
 838      return $title;
 839  }
 840  
 841  /**
 842   * Ensures a string is a valid SQL order by clause.
 843   *
 844   * Accepts one or more columns, with or without ASC/DESC, and also accepts
 845   * RAND().
 846   *
 847   * @since 2.5.1
 848   *
 849   * @param string $orderby Order by string to be checked.
 850   * @return string|false Returns the order by clause if it is a match, false otherwise.
 851   */
 852  function sanitize_sql_orderby( $orderby ){
 853      preg_match('/^\s*([a-z0-9_]+(\s+(ASC|DESC))?(\s*,\s*|\s*$))+|^\s*RAND\(\s*\)\s*$/i', $orderby, $obmatches);
 854      if ( !$obmatches )
 855          return false;
 856      return $orderby;
 857  }
 858  
 859  /**
 860   * Santizes a html classname to ensure it only contains valid characters
 861   *
 862   * Strips the string down to A-Z,a-z,0-9,'-' if this results in an empty
 863   * string then it will return the alternative value supplied.
 864   *
 865   * @todo Expand to support the full range of CDATA that a class attribute can contain.
 866   *
 867   * @since 2.8.0
 868   *
 869   * @param string $class The classname to be sanitized
 870   * @param string $fallback Optional. The value to return if the sanitization end's up as an empty string.
 871   *     Defaults to an empty string.
 872   * @return string The sanitized value
 873   */
 874  function sanitize_html_class( $class, $fallback = '' ) {
 875      //Strip out any % encoded octets
 876      $sanitized = preg_replace('|%[a-fA-F0-9][a-fA-F0-9]|', '', $class);
 877  
 878      //Limit to A-Z,a-z,0-9,'-'
 879      $sanitized = preg_replace('/[^A-Za-z0-9-]/', '', $sanitized);
 880  
 881      if ( '' == $sanitized )
 882          $sanitized = $fallback;
 883  
 884      return apply_filters( 'sanitize_html_class', $sanitized, $class, $fallback );
 885  }
 886  
 887  /**
 888   * Converts a number of characters from a string.
 889   *
 890   * Metadata tags <<title>> and <<category>> are removed, <<br>> and <<hr>> are
 891   * converted into correct XHTML and Unicode characters are converted to the
 892   * valid range.
 893   *
 894   * @since 0.71
 895   *
 896   * @param string $content String of characters to be converted.
 897   * @param string $deprecated Not used.
 898   * @return string Converted string.
 899   */
 900  function convert_chars($content, $deprecated = '') {
 901      if ( !empty( $deprecated ) )
 902          _deprecated_argument( __FUNCTION__, '0.71' );
 903  
 904      // Translation of invalid Unicode references range to valid range
 905      $wp_htmltranswinuni = array(
 906      '&#128;' => '&#8364;', // the Euro sign
 907      '&#129;' => '',
 908      '&#130;' => '&#8218;', // these are Windows CP1252 specific characters
 909      '&#131;' => '&#402;',  // they would look weird on non-Windows browsers
 910      '&#132;' => '&#8222;',
 911      '&#133;' => '&#8230;',
 912      '&#134;' => '&#8224;',
 913      '&#135;' => '&#8225;',
 914      '&#136;' => '&#710;',
 915      '&#137;' => '&#8240;',
 916      '&#138;' => '&#352;',
 917      '&#139;' => '&#8249;',
 918      '&#140;' => '&#338;',
 919      '&#141;' => '',
 920      '&#142;' => '&#382;',
 921      '&#143;' => '',
 922      '&#144;' => '',
 923      '&#145;' => '&#8216;',
 924      '&#146;' => '&#8217;',
 925      '&#147;' => '&#8220;',
 926      '&#148;' => '&#8221;',
 927      '&#149;' => '&#8226;',
 928      '&#150;' => '&#8211;',
 929      '&#151;' => '&#8212;',
 930      '&#152;' => '&#732;',
 931      '&#153;' => '&#8482;',
 932      '&#154;' => '&#353;',
 933      '&#155;' => '&#8250;',
 934      '&#156;' => '&#339;',
 935      '&#157;' => '',
 936      '&#158;' => '',
 937      '&#159;' => '&#376;'
 938      );
 939  
 940      // Remove metadata tags
 941      $content = preg_replace('/<title>(.+?)<\/title>/','',$content);
 942      $content = preg_replace('/<category>(.+?)<\/category>/','',$content);
 943  
 944      // Converts lone & characters into &#38; (a.k.a. &amp;)
 945      $content = preg_replace('/&([^#])(?![a-z1-4]{1,8};)/i', '&#038;$1', $content);
 946  
 947      // Fix Word pasting
 948      $content = strtr($content, $wp_htmltranswinuni);
 949  
 950      // Just a little XHTML help
 951      $content = str_replace('<br>', '<br />', $content);
 952      $content = str_replace('<hr>', '<hr />', $content);
 953  
 954      return $content;
 955  }
 956  
 957  /**
 958   * Callback used to change %uXXXX to &#YYY; syntax
 959   *
 960   * @since 2.8?
 961   *
 962   * @param array $matches Single Match
 963   * @return string An HTML entity
 964   */
 965  function funky_javascript_callback($matches) {
 966      return "&#".base_convert($matches[1],16,10).";";
 967  }
 968  
 969  /**
 970   * Fixes javascript bugs in browsers.
 971   *
 972   * Converts unicode characters to HTML numbered entities.
 973   *
 974   * @since 1.5.0
 975   * @uses $is_macIE
 976   * @uses $is_winIE
 977   *
 978   * @param string $text Text to be made safe.
 979   * @return string Fixed text.
 980   */
 981  function funky_javascript_fix($text) {
 982      // Fixes for browsers' javascript bugs
 983      global $is_macIE, $is_winIE;
 984  
 985      if ( $is_winIE || $is_macIE )
 986          $text =  preg_replace_callback("/\%u([0-9A-F]{4,4})/",
 987                             "funky_javascript_callback",
 988                             $text);
 989  
 990      return $text;
 991  }
 992  
 993  /**
 994   * Will only balance the tags if forced to and the option is set to balance tags.
 995   *
 996   * The option 'use_balanceTags' is used for whether the tags will be balanced.
 997   * Both the $force parameter and 'use_balanceTags' option will have to be true
 998   * before the tags will be balanced.
 999   *
1000   * @since 0.71
1001   *
1002   * @param string $text Text to be balanced
1003   * @param bool $force Forces balancing, ignoring the value of the option. Default false.
1004   * @return string Balanced text
1005   */
1006  function balanceTags( $text, $force = false ) {
1007      if ( !$force && get_option('use_balanceTags') == 0 )
1008          return $text;
1009      return force_balance_tags( $text );
1010  }
1011  
1012  /**
1013   * Balances tags of string using a modified stack.
1014   *
1015   * @since 2.0.4
1016   *
1017   * @author Leonard Lin <leonard@acm.org>
1018   * @license GPL v2.0
1019   * @copyright November 4, 2001
1020   * @version 1.1
1021   * @todo Make better - change loop condition to $text in 1.2
1022   * @internal Modified by Scott Reilly (coffee2code) 02 Aug 2004
1023   *        1.1  Fixed handling of append/stack pop order of end text
1024   *             Added Cleaning Hooks
1025   *        1.0  First Version
1026   *
1027   * @param string $text Text to be balanced.
1028   * @return string Balanced text.
1029   */
1030  function force_balance_tags( $text ) {
1031      $tagstack = array();
1032      $stacksize = 0;
1033      $tagqueue = '';
1034      $newtext = '';
1035      $single_tags = array('br', 'hr', 'img', 'input'); // Known single-entity/self-closing tags
1036      $nestable_tags = array('blockquote', 'div', 'span'); // Tags that can be immediately nested within themselves
1037  
1038      // WP bug fix for comments - in case you REALLY meant to type '< !--'
1039      $text = str_replace('< !--', '<    !--', $text);
1040      // WP bug fix for LOVE <3 (and other situations with '<' before a number)
1041      $text = preg_replace('#<([0-9]{1})#', '&lt;$1', $text);
1042  
1043      while ( preg_match("/<(\/?[\w:]*)\s*([^>]*)>/", $text, $regex) ) {
1044          $newtext .= $tagqueue;
1045  
1046          $i = strpos($text, $regex[0]);
1047          $l = strlen($regex[0]);
1048  
1049          // clear the shifter
1050          $tagqueue = '';
1051          // Pop or Push
1052          if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag
1053              $tag = strtolower(substr($regex[1],1));
1054              // if too many closing tags
1055              if( $stacksize <= 0 ) {
1056                  $tag = '';
1057                  // or close to be safe $tag = '/' . $tag;
1058              }
1059              // if stacktop value = tag close value then pop
1060              else if ( $tagstack[$stacksize - 1] == $tag ) { // found closing tag
1061                  $tag = '</' . $tag . '>'; // Close Tag
1062                  // Pop
1063                  array_pop( $tagstack );
1064                  $stacksize--;
1065              } else { // closing tag not at top, search for it
1066                  for ( $j = $stacksize-1; $j >= 0; $j-- ) {
1067                      if ( $tagstack[$j] == $tag ) {
1068                      // add tag to tagqueue
1069                          for ( $k = $stacksize-1; $k >= $j; $k--) {
1070                              $tagqueue .= '</' . array_pop( $tagstack ) . '>';
1071                              $stacksize--;
1072                          }
1073                          break;
1074                      }
1075                  }
1076                  $tag = '';
1077              }
1078          } else { // Begin Tag
1079              $tag = strtolower($regex[1]);
1080  
1081              // Tag Cleaning
1082  
1083              // If self-closing or '', don't do anything.
1084              if ( substr($regex[2],-1) == '/' || $tag == '' ) {
1085                  // do nothing
1086              }
1087              // ElseIf it's a known single-entity tag but it doesn't close itself, do so
1088              elseif ( in_array($tag, $single_tags) ) {
1089                  $regex[2] .= '/';
1090              } else {    // Push the tag onto the stack
1091                  // If the top of the stack is the same as the tag we want to push, close previous tag
1092                  if ( $stacksize > 0 && !in_array($tag, $nestable_tags) && $tagstack[$stacksize - 1] == $tag ) {
1093                      $tagqueue = '</' . array_pop ($tagstack) . '>';
1094                      $stacksize--;
1095                  }
1096                  $stacksize = array_push ($tagstack, $tag);
1097              }
1098  
1099              // Attributes
1100              $attributes = $regex[2];
1101              if( !empty($attributes) )
1102                  $attributes = ' '.$attributes;
1103  
1104              $tag = '<' . $tag . $attributes . '>';
1105              //If already queuing a close tag, then put this tag on, too
1106              if ( !empty($tagqueue) ) {
1107                  $tagqueue .= $tag;
1108                  $tag = '';
1109              }
1110          }
1111          $newtext .= substr($text, 0, $i) . $tag;
1112          $text = substr($text, $i + $l);
1113      }
1114  
1115      // Clear Tag Queue
1116      $newtext .= $tagqueue;
1117  
1118      // Add Remaining text
1119      $newtext .= $text;
1120  
1121      // Empty Stack
1122      while( $x = array_pop($tagstack) )
1123          $newtext .= '</' . $x . '>'; // Add remaining tags to close
1124  
1125      // WP fix for the bug with HTML comments
1126      $newtext = str_replace("< !--","<!--",$newtext);
1127      $newtext = str_replace("<    !--","< !--",$newtext);
1128  
1129      return $newtext;
1130  }
1131  
1132  /**
1133   * Acts on text which is about to be edited.
1134   *
1135   * Unless $richedit is set, it is simply a holder for the 'format_to_edit'
1136   * filter. If $richedit is set true htmlspecialchars() will be run on the
1137   * content, converting special characters to HTMl entities.
1138   *
1139   * @since 0.71
1140   *
1141   * @param string $content The text about to be edited.
1142   * @param bool $richedit Whether the $content should pass through htmlspecialchars(). Default false.
1143   * @return string The text after the filter (and possibly htmlspecialchars()) has been run.
1144   */
1145  function format_to_edit($content, $richedit = false) {
1146      $content = apply_filters('format_to_edit', $content);
1147      if (! $richedit )
1148          $content = htmlspecialchars($content);
1149      return $content;
1150  }
1151  
1152  /**
1153   * Holder for the 'format_to_post' filter.
1154   *
1155   * @since 0.71
1156   *
1157   * @param string $content The text to pass through the filter.
1158   * @return string Text returned from the 'format_to_post' filter.
1159   */
1160  function format_to_post($content) {
1161      $content = apply_filters('format_to_post', $content);
1162      return $content;
1163  }
1164  
1165  /**
1166   * Add leading zeros when necessary.
1167   *
1168   * If you set the threshold to '4' and the number is '10', then you will get
1169   * back '0010'. If you set the number to '4' and the number is '5000', then you
1170   * will get back '5000'.
1171   *
1172   * Uses sprintf to append the amount of zeros based on the $threshold parameter
1173   * and the size of the number. If the number is large enough, then no zeros will
1174   * be appended.
1175   *
1176   * @since 0.71
1177   *
1178   * @param mixed $number Number to append zeros to if not greater than threshold.
1179   * @param int $threshold Digit places number needs to be to not have zeros added.
1180   * @return string Adds leading zeros to number if needed.
1181   */
1182  function zeroise($number, $threshold) {
1183      return sprintf('%0'.$threshold.'s', $number);
1184  }
1185  
1186  /**
1187   * Adds backslashes before letters and before a number at the start of a string.
1188   *
1189   * @since 0.71
1190   *
1191   * @param string $string Value to which backslashes will be added.
1192   * @return string String with backslashes inserted.
1193   */
1194  function backslashit($string) {
1195      $string = preg_replace('/^([0-9])/', '\\\\\\\\\1', $string);
1196      $string = preg_replace('/([a-z])/i', '\\\\\1', $string);
1197      return $string;
1198  }
1199  
1200  /**
1201   * Appends a trailing slash.
1202   *
1203   * Will remove trailing slash if it exists already before adding a trailing
1204   * slash. This prevents double slashing a string or path.
1205   *
1206   * The primary use of this is for paths and thus should be used for paths. It is
1207   * not restricted to paths and offers no specific path support.
1208   *
1209   * @since 1.2.0
1210   * @uses untrailingslashit() Unslashes string if it was slashed already.
1211   *
1212   * @param string $string What to add the trailing slash to.
1213   * @return string String with trailing slash added.
1214   */
1215  function trailingslashit($string) {
1216      return untrailingslashit($string) . '/';
1217  }
1218  
1219  /**
1220   * Removes trailing slash if it exists.
1221   *
1222   * The primary use of this is for paths and thus should be used for paths. It is
1223   * not restricted to paths and offers no specific path support.
1224   *
1225   * @since 2.2.0
1226   *
1227   * @param string $string What to remove the trailing slash from.
1228   * @return string String without the trailing slash.
1229   */
1230  function untrailingslashit($string) {
1231      return rtrim($string, '/');
1232  }
1233  
1234  /**
1235   * Adds slashes to escape strings.
1236   *
1237   * Slashes will first be removed if magic_quotes_gpc is set, see {@link
1238   * http://www.php.net/magic_quotes} for more details.
1239   *
1240   * @since 0.71
1241   *
1242   * @param string $gpc The string returned from HTTP request data.
1243   * @return string Returns a string escaped with slashes.
1244   */
1245  function addslashes_gpc($gpc) {
1246      if ( get_magic_quotes_gpc() )
1247          $gpc = stripslashes($gpc);
1248  
1249      return esc_sql($gpc);
1250  }
1251  
1252  /**
1253   * Navigates through an array and removes slashes from the values.
1254   *
1255   * If an array is passed, the array_map() function causes a callback to pass the
1256   * value back to the function. The slashes from this value will removed.
1257   *
1258   * @since 2.0.0
1259   *
1260   * @param array|string $value The array or string to be striped.
1261   * @return array|string Stripped array (or string in the callback).
1262   */
1263  function stripslashes_deep($value) {
1264      $value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
1265      return $value;
1266  }
1267  
1268  /**
1269   * Navigates through an array and encodes the values to be used in a URL.
1270   *
1271   * Uses a callback to pass the value of the array back to the function as a
1272   * string.
1273   *
1274   * @since 2.2.0
1275   *
1276   * @param array|string $value The array or string to be encoded.
1277   * @return array|string $value The encoded array (or string from the callback).
1278   */
1279  function urlencode_deep($value) {
1280      $value = is_array($value) ? array_map('urlencode_deep', $value) : urlencode($value);
1281      return $value;
1282  }
1283  
1284  /**
1285   * Converts email addresses characters to HTML entities to block spam bots.
1286   *
1287   * @since 0.71
1288   *
1289   * @param string $emailaddy Email address.
1290   * @param int $mailto Optional. Range from 0 to 1. Used for encoding.
1291   * @return string Converted email address.
1292   */
1293  function antispambot($emailaddy, $mailto=0) {
1294      $emailNOSPAMaddy = '';
1295      srand ((float) microtime() * 1000000);
1296      for ($i = 0; $i < strlen($emailaddy); $i = $i + 1) {
1297          $j = floor(rand(0, 1+$mailto));
1298          if ($j==0) {
1299              $emailNOSPAMaddy .= '&#'.ord(substr($emailaddy,$i,1)).';';
1300          } elseif ($j==1) {
1301              $emailNOSPAMaddy .= substr($emailaddy,$i,1);
1302          } elseif ($j==2) {
1303              $emailNOSPAMaddy .= '%'.zeroise(dechex(ord(substr($emailaddy, $i, 1))), 2);
1304          }
1305      }
1306      $emailNOSPAMaddy = str_replace('@','&#64;',$emailNOSPAMaddy);
1307      return $emailNOSPAMaddy;
1308  }
1309  
1310  /**
1311   * Callback to convert URI match to HTML A element.
1312   *
1313   * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
1314   * make_clickable()}.
1315   *
1316   * @since 2.3.2
1317   * @access private
1318   *
1319   * @param array $matches Single Regex Match.
1320   * @return string HTML A element with URI address.
1321   */
1322  function _make_url_clickable_cb($matches) {
1323      $url = $matches[2];
1324  
1325      $url = esc_url($url);
1326      if ( empty($url) )
1327          return $matches[0];
1328  
1329      return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>";
1330  }
1331  
1332  /**
1333   * Callback to convert URL match to HTML A element.
1334   *
1335   * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
1336   * make_clickable()}.
1337   *
1338   * @since 2.3.2
1339   * @access private
1340   *
1341   * @param array $matches Single Regex Match.
1342   * @return string HTML A element with URL address.
1343   */
1344  function _make_web_ftp_clickable_cb($matches) {
1345      $ret = '';
1346      $dest = $matches[2];
1347      $dest = 'http://' . $dest;
1348      $dest = esc_url($dest);
1349      if ( empty($dest) )
1350          return $matches[0];
1351  
1352      // removed trailing [.,;:)] from URL
1353      if ( in_array( substr($dest, -1), array('.', ',', ';', ':', ')') ) === true ) {
1354          $ret = substr($dest, -1);
1355          $dest = substr($dest, 0, strlen($dest)-1);
1356      }
1357      return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>$ret";
1358  }
1359  
1360  /**
1361   * Callback to convert email address match to HTML A element.
1362   *
1363   * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
1364   * make_clickable()}.
1365   *
1366   * @since 2.3.2
1367   * @access private
1368   *
1369   * @param array $matches Single Regex Match.
1370   * @return string HTML A element with email address.
1371   */
1372  function _make_email_clickable_cb($matches) {
1373      $email = $matches[2] . '@' . $matches[3];
1374      return $matches[1] . "<a href=\"mailto:$email\">$email</a>";
1375  }
1376  
1377  /**
1378   * Convert plaintext URI to HTML links.
1379   *
1380   * Converts URI, www and ftp, and email addresses. Finishes by fixing links
1381   * within links.
1382   *
1383   * @since 0.71
1384   *
1385   * @param string $ret Content to convert URIs.
1386   * @return string Content with converted URIs.
1387   */
1388  function make_clickable($ret) {
1389      $ret = ' ' . $ret;
1390      // in testing, using arrays here was found to be faster
1391      $ret = preg_replace_callback('#(?<=[\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#$%&~/=?@\[\](+-]|[.,;:](?![\s<]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:]|$)|\)))+)#is', '_make_url_clickable_cb', $ret);
1392      $ret = preg_replace_callback('#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret);
1393      $ret = preg_replace_callback('#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret);
1394      // this one is not in an array because we need it to run last, for cleanup of accidental links within links
1395      $ret = preg_replace("#(<a( [^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i", "$1$3</a>", $ret);
1396      $ret = trim($ret);
1397      return $ret;
1398  }
1399  
1400  /**
1401   * Adds rel nofollow string to all HTML A elements in content.
1402   *
1403   * @since 1.5.0
1404   *
1405   * @param string $text Content that may contain HTML A elements.
1406   * @return string Converted content.
1407   */
1408  function wp_rel_nofollow( $text ) {
1409      // This is a pre save filter, so text is already escaped.
1410      $text = stripslashes($text);
1411      $text = preg_replace_callback('|<a (.+?)>|i', 'wp_rel_nofollow_callback', $text);
1412      $text = esc_sql($text);
1413      return $text;
1414  }
1415  
1416  /**
1417   * Callback to used to add rel=nofollow string to HTML A element.
1418   *
1419   * Will remove already existing rel="nofollow" and rel='nofollow' from the
1420   * string to prevent from invalidating (X)HTML.
1421   *
1422   * @since 2.3.0
1423   *
1424   * @param array $matches Single Match
1425   * @return string HTML A Element with rel nofollow.
1426   */
1427  function wp_rel_nofollow_callback( $matches ) {
1428      $text = $matches[1];
1429      $text = str_replace(array(' rel="nofollow"', " rel='nofollow'"), '', $text);
1430      return "<a $text rel=\"nofollow\">";
1431  }
1432  
1433  /**
1434   * Convert one smiley code to the icon graphic file equivalent.
1435   *
1436   * Looks up one smiley code in the $wpsmiliestrans global array and returns an
1437   * <img> string for that smiley.
1438   *
1439   * @global array $wpsmiliestrans
1440   * @since 2.8.0
1441   *
1442   * @param string $smiley Smiley code to convert to image.
1443   * @return string Image string for smiley.
1444   */
1445  function translate_smiley($smiley) {
1446      global $wpsmiliestrans;
1447  
1448      if (count($smiley) == 0) {
1449          return '';
1450      }
1451  
1452      $siteurl = get_option( 'siteurl' );
1453  
1454      $smiley = trim(reset($smiley));
1455      $img = $wpsmiliestrans[$smiley];
1456      $smiley_masked = esc_attr($smiley);
1457  
1458      $srcurl = apply_filters('smilies_src', "$siteurl/wp-includes/images/smilies/$img", $img, $siteurl);
1459  
1460      return " <img src='$srcurl' alt='$smiley_masked' class='wp-smiley' /> ";
1461  }
1462  
1463  /**
1464   * Convert text equivalent of smilies to images.
1465   *
1466   * Will only convert smilies if the option 'use_smilies' is true and the global
1467   * used in the function isn't empty.
1468   *
1469   * @since 0.71
1470   * @uses $wp_smiliessearch
1471   *
1472   * @param string $text Content to convert smilies from text.
1473   * @return string Converted content with text smilies replaced with images.
1474   */
1475  function convert_smilies($text) {
1476      global $wp_smiliessearch;
1477      $output = '';
1478      if ( get_option('use_smilies') && !empty($wp_smiliessearch) ) {
1479          // HTML loop taken from texturize function, could possible be consolidated
1480          $textarr = preg_split("/(<.*>)/U", $text, -1, PREG_SPLIT_DELIM_CAPTURE); // capture the tags as well as in between
1481          $stop = count($textarr);// loop stuff
1482          for ($i = 0; $i < $stop; $i++) {
1483              $content = $textarr[$i];
1484              if ((strlen($content) > 0) && ('<' != $content{0})) { // If it's not a tag
1485                  $content = preg_replace_callback($wp_smiliessearch, 'translate_smiley', $content);
1486              }
1487              $output .= $content;
1488          }
1489      } else {
1490          // return default text.
1491          $output = $text;
1492      }
1493      return $output;
1494  }
1495  
1496  /**
1497   * Verifies that an email is valid.
1498   *
1499   * Does not grok i18n domains. Not RFC compliant.
1500   *
1501   * @since 0.71
1502   *
1503   * @param string $email Email address to verify.
1504   * @param boolean $check_dns Whether to check the DNS for the domain using checkdnsrr().
1505   * @return string|bool Either false or the valid email address.
1506   */
1507  function is_email( $email, $check_dns = false ) {
1508      // Test for the minimum length the email can be
1509      if ( strlen( $email ) < 3 ) {
1510          return apply_filters( 'is_email', false, $email, 'email_too_short' );
1511      }
1512  
1513      // Test for an @ character after the first position
1514      if ( strpos( $email, '@', 1 ) === false ) {
1515          return apply_filters( 'is_email', false, $email, 'email_no_at' );
1516      }
1517  
1518      // Split out the local and domain parts
1519      list( $local, $domain ) = explode( '@', $email, 2 );
1520  
1521      // LOCAL PART
1522      // Test for invalid characters
1523      if ( !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local ) ) {
1524          return apply_filters( 'is_email', false, $email, 'local_invalid_chars' );
1525      }
1526  
1527      // DOMAIN PART
1528      // Test for sequences of periods
1529      if ( preg_match( '/\.{2,}/', $domain ) ) {
1530          return apply_filters( 'is_email', false, $email, 'domain_period_sequence' );
1531      }
1532  
1533      // Test for leading and trailing periods and whitespace
1534      if ( trim( $domain, " \t\n\r\0\x0B." ) !== $domain ) {
1535          return apply_filters( 'is_email', false, $email, 'domain_period_limits' );
1536      }
1537  
1538      // Split the domain into subs
1539      $subs = explode( '.', $domain );
1540  
1541      // Assume the domain will have at least two subs
1542      if ( 2 > count( $subs ) ) {
1543          return apply_filters( 'is_email', false, $email, 'domain_no_periods' );
1544      }
1545  
1546      // Loop through each sub
1547      foreach ( $subs as $sub ) {
1548          // Test for leading and trailing hyphens and whitespace
1549          if ( trim( $sub, " \t\n\r\0\x0B-" ) !== $sub ) {
1550              return apply_filters( 'is_email', false, $email, 'sub_hyphen_limits' );
1551          }
1552  
1553          // Test for invalid characters
1554          if ( !preg_match('/^[a-z0-9-]+$/i', $sub ) ) {
1555              return apply_filters( 'is_email', false, $email, 'sub_invalid_chars' );
1556          }
1557      }
1558  
1559      // DNS
1560      // Check the domain has a valid MX and A resource record
1561      if ( $check_dns && function_exists( 'checkdnsrr' ) && !( checkdnsrr( $domain . '.', 'MX' ) || checkdnsrr( $domain . '.', 'A' ) ) ) {
1562          return apply_filters( 'is_email', false, $email, 'dns_no_rr' );
1563      }
1564  
1565      // Congratulations your email made it!
1566      return apply_filters( 'is_email', $email, $email, null );
1567  }
1568  
1569  /**
1570   * Convert to ASCII from email subjects.
1571   *
1572   * @since 1.2.0
1573   * @usedby wp_mail() handles charsets in email subjects
1574   *
1575   * @param string $string Subject line
1576   * @return string Converted string to ASCII
1577   */
1578  function wp_iso_descrambler($string) {
1579      /* this may only work with iso-8859-1, I'm afraid */
1580      if (!preg_match('#\=\?(.+)\?Q\?(.+)\?\=#i', $string, $matches)) {
1581          return $string;
1582      } else {
1583          $subject = str_replace('_', ' ', $matches[2]);
1584          $subject = preg_replace_callback('#\=([0-9a-f]{2})#i', create_function('$match', 'return chr(hexdec(strtolower($match[1])));'), $subject);
1585          return $subject;
1586      }
1587  }
1588  
1589  /**
1590   * Returns a date in the GMT equivalent.
1591   *
1592   * Requires and returns a date in the Y-m-d H:i:s format. Simply subtracts the
1593   * value of the 'gmt_offset' option. Return format can be overridden using the
1594   * $format parameter
1595   *
1596   * @since 1.2.0
1597   *
1598   * @uses get_option() to retrieve the the value of 'gmt_offset'.
1599   * @param string $string The date to be converted.
1600   * @param string $format The format string for the returned date (default is Y-m-d H:i:s)
1601   * @return string GMT version of the date provided.
1602   */
1603  function get_gmt_from_date($string, $format = 'Y-m-d H:i:s') {
1604      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);
1605      $string_time = gmmktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
1606      $string_gmt = gmdate($format, $string_time - get_option('gmt_offset') * 3600);
1607      return $string_gmt;
1608  }
1609  
1610  /**
1611   * Converts a GMT date into the correct format for the blog.
1612   *
1613   * Requires and returns in the Y-m-d H:i:s format. Simply adds the value of
1614   * gmt_offset.Return format can be overridden using the $format parameter
1615   *
1616   * @since 1.2.0
1617   *
1618   * @param string $string The date to be converted.
1619   * @param string $format The format string for the returned date (default is Y-m-d H:i:s)
1620   * @return string Formatted date relative to the GMT offset.
1621   */
1622  function get_date_from_gmt($string, $format = 'Y-m-d H:i:s') {
1623      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);
1624      $string_time = gmmktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
1625      $string_localtime = gmdate($format, $string_time + get_option('gmt_offset')*3600);
1626      return $string_localtime;
1627  }
1628  
1629  /**
1630   * Computes an offset in seconds from an iso8601 timezone.
1631   *
1632   * @since 1.5.0
1633   *
1634   * @param string $timezone Either 'Z' for 0 offset or '±hhmm'.
1635   * @return int|float The offset in seconds.
1636   */
1637  function iso8601_timezone_to_offset($timezone) {
1638      // $timezone is either 'Z' or '[+|-]hhmm'
1639      if ($timezone == 'Z') {
1640          $offset = 0;
1641      } else {
1642          $sign    = (substr($timezone, 0, 1) == '+') ? 1 : -1;
1643          $hours   = intval(substr($timezone, 1, 2));
1644          $minutes = intval(substr($timezone, 3, 4)) / 60;
1645          $offset  = $sign * 3600 * ($hours + $minutes);
1646      }
1647      return $offset;
1648  }
1649  
1650  /**
1651   * Converts an iso8601 date to MySQL DateTime format used by post_date[_gmt].
1652   *
1653   * @since 1.5.0
1654   *
1655   * @param string $date_string Date and time in ISO 8601 format {@link http://en.wikipedia.org/wiki/ISO_8601}.
1656   * @param string $timezone Optional. If set to GMT returns the time minus gmt_offset. Default is 'user'.
1657   * @return string The date and time in MySQL DateTime format - Y-m-d H:i:s.
1658   */
1659  function iso8601_to_datetime($date_string, $timezone = 'user') {
1660      $timezone = strtolower($timezone);
1661  
1662      if ($timezone == 'gmt') {
1663  
1664          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);
1665  
1666          if (!empty($date_bits[7])) { // we have a timezone, so let's compute an offset
1667              $offset = iso8601_timezone_to_offset($date_bits[7]);
1668          } else { // we don't have a timezone, so we assume user local timezone (not server's!)
1669              $offset = 3600 * get_option('gmt_offset');
1670          }
1671  
1672          $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]);
1673          $timestamp -= $offset;
1674  
1675          return gmdate('Y-m-d H:i:s', $timestamp);
1676  
1677      } else if ($timezone == 'user') {
1678          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);
1679      }
1680  }
1681  
1682  /**
1683   * Adds a element attributes to open links in new windows.
1684   *
1685   * Comment text in popup windows should be filtered through this. Right now it's
1686   * a moderately dumb function, ideally it would detect whether a target or rel
1687   * attribute was already there and adjust its actions accordingly.
1688   *
1689   * @since 0.71
1690   *
1691   * @param string $text Content to replace links to open in a new window.
1692   * @return string Content that has filtered links.
1693   */
1694  function popuplinks($text) {
1695      $text = preg_replace('/<a (.+?)>/i', "<a $1 target='_blank' rel='external'>", $text);
1696      return $text;
1697  }
1698  
1699  /**
1700   * Strips out all characters that are not allowable in an email.
1701   *
1702   * @since 1.5.0
1703   *
1704   * @param string $email Email address to filter.
1705   * @return string Filtered email address.
1706   */
1707  function sanitize_email( $email ) {
1708      // Test for the minimum length the email can be
1709      if ( strlen( $email ) < 3 ) {
1710          return apply_filters( 'sanitize_email', '', $email, 'email_too_short' );
1711      }
1712  
1713      // Test for an @ character after the first position
1714      if ( strpos( $email, '@', 1 ) === false ) {
1715          return apply_filters( 'sanitize_email', '', $email, 'email_no_at' );
1716      }
1717  
1718      // Split out the local and domain parts
1719      list( $local, $domain ) = explode( '@', $email, 2 );
1720  
1721      // LOCAL PART
1722      // Test for invalid characters
1723      $local = preg_replace( '/[^a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]/', '', $local );
1724      if ( '' === $local ) {
1725          return apply_filters( 'sanitize_email', '', $email, 'local_invalid_chars' );
1726      }
1727  
1728      // DOMAIN PART
1729      // Test for sequences of periods
1730      $domain = preg_replace( '/\.{2,}/', '', $domain );
1731      if ( '' === $domain ) {
1732          return apply_filters( 'sanitize_email', '', $email, 'domain_period_sequence' );
1733      }
1734  
1735      // Test for leading and trailing periods and whitespace
1736      $domain = trim( $domain, " \t\n\r\0\x0B." );
1737      if ( '' === $domain ) {
1738          return apply_filters( 'sanitize_email', '', $email, 'domain_period_limits' );
1739      }
1740  
1741      // Split the domain into subs
1742      $subs = explode( '.', $domain );
1743  
1744      // Assume the domain will have at least two subs
1745      if ( 2 > count( $subs ) ) {
1746          return apply_filters( 'sanitize_email', '', $email, 'domain_no_periods' );
1747      }
1748  
1749      // Create an array that will contain valid subs
1750      $new_subs = array();
1751  
1752      // Loop through each sub
1753      foreach ( $subs as $sub ) {
1754          // Test for leading and trailing hyphens
1755          $sub = trim( $sub, " \t\n\r\0\x0B-" );
1756  
1757          // Test for invalid characters
1758          $sub = preg_replace( '/^[^a-z0-9-]+$/i', '', $sub );
1759  
1760          // If there's anything left, add it to the valid subs
1761          if ( '' !== $sub ) {
1762              $new_subs[] = $sub;
1763          }
1764      }
1765  
1766      // If there aren't 2 or more valid subs
1767      if ( 2 > count( $new_subs ) ) {
1768          return apply_filters( 'sanitize_email', '', $email, 'domain_no_valid_subs' );
1769      }
1770  
1771      // Join valid subs into the new domain
1772      $domain = join( '.', $new_subs );
1773  
1774      // Put the email back together
1775      $email = $local . '@' . $domain;
1776  
1777      // Congratulations your email made it!
1778      return apply_filters( 'sanitize_email', $email, $email, null );
1779  }
1780  
1781  /**
1782   * Determines the difference between two timestamps.
1783   *
1784   * The difference is returned in a human readable format such as "1 hour",
1785   * "5 mins", "2 days".
1786   *
1787   * @since 1.5.0
1788   *
1789   * @param int $from Unix timestamp from which the difference begins.
1790   * @param int $to Optional. Unix timestamp to end the time difference. Default becomes time() if not set.
1791   * @return string Human readable time difference.
1792   */
1793  function human_time_diff( $from, $to = '' ) {
1794      if ( empty($to) )
1795          $to = time();
1796      $diff = (int) abs($to - $from);
1797      if ($diff <= 3600) {
1798          $mins = round($diff / 60);
1799          if ($mins <= 1) {
1800              $mins = 1;
1801          }
1802          /* translators: min=minute */
1803          $since = sprintf(_n('%s min', '%s mins', $mins), $mins);
1804      } else if (($diff <= 86400) && ($diff > 3600)) {
1805          $hours = round($diff / 3600);
1806          if ($hours <= 1) {
1807              $hours = 1;
1808          }
1809          $since = sprintf(_n('%s hour', '%s hours', $hours), $hours);
1810      } elseif ($diff >= 86400) {
1811          $days = round($diff / 86400);
1812          if ($days <= 1) {
1813              $days = 1;
1814          }
1815          $since = sprintf(_n('%s day', '%s days', $days), $days);
1816      }
1817      return $since;
1818  }
1819  
1820  /**
1821   * Generates an excerpt from the content, if needed.
1822   *
1823   * The excerpt word amount will be 55 words and if the amount is greater than
1824   * that, then the string ' [...]' will be appended to the excerpt. If the string
1825   * is less than 55 words, then the content will be returned as is.
1826   *
1827   * The 55 word limit can be modified by plugins/themes using the excerpt_length filter
1828   * The ' [...]' string can be modified by plugins/themes using the excerpt_more filter
1829   *
1830   * @since 1.5.0
1831   *
1832   * @param string $text The excerpt. If set to empty an excerpt is generated.
1833   * @return string The excerpt.
1834   */
1835  function wp_trim_excerpt($text) {
1836      $raw_excerpt = $text;
1837      if ( '' == $text ) {
1838          $text = get_the_content('');
1839  
1840          $text = strip_shortcodes( $text );
1841  
1842          $text = apply_filters('the_content', $text);
1843          $text = str_replace(']]>', ']]&gt;', $text);
1844          $text = strip_tags($text);
1845          $excerpt_length = apply_filters('excerpt_length', 55);
1846          $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
1847          $words = preg_split("/[\n\r\t ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
1848          if ( count($words) > $excerpt_length ) {
1849              array_pop($words);
1850              $text = implode(' ', $words);
1851              $text = $text . $excerpt_more;
1852          } else {
1853              $text = implode(' ', $words);
1854          }
1855      }
1856      return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
1857  }
1858  
1859  /**
1860   * Converts named entities into numbered entities.
1861   *
1862   * @since 1.5.1
1863   *
1864   * @param string $text The text within which entities will be converted.
1865   * @return string Text with converted entities.
1866   */
1867  function ent2ncr($text) {
1868      $to_ncr = array(
1869          '&quot;' => '&#34;',
1870          '&amp;' => '&#38;',
1871          '&frasl;' => '&#47;',
1872          '&lt;' => '&#60;',
1873          '&gt;' => '&#62;',
1874          '|' => '&#124;',
1875          '&nbsp;' => '&#160;',
1876          '&iexcl;' => '&#161;',
1877          '&cent;' => '&#162;',
1878          '&pound;' => '&#163;',
1879          '&curren;' => '&#164;',
1880          '&yen;' => '&#165;',
1881          '&brvbar;' => '&#166;',
1882          '&brkbar;' => '&#166;',
1883          '&sect;' => '&#167;',
1884          '&uml;' => '&#168;',
1885          '&die;' => '&#168;',
1886          '&copy;' => '&#169;',
1887          '&ordf;' => '&#170;',
1888          '&laquo;' => '&#171;',
1889          '&not;' => '&#172;',
1890          '&shy;' => '&#173;',
1891          '&reg;' => '&#174;',
1892          '&macr;' => '&#175;',
1893          '&hibar;' => '&#175;',
1894          '&deg;' => '&#176;',
1895          '&plusmn;' => '&#177;',
1896          '&sup2;' => '&#178;',
1897          '&sup3;' => '&#179;',
1898          '&acute;' => '&#180;',
1899          '&micro;' => '&#181;',
1900          '&para;' => '&#182;',
1901          '&middot;' => '&#183;',
1902          '&cedil;' => '&#184;',
1903          '&sup1;' => '&#185;',
1904          '&ordm;' => '&#186;',
1905          '&raquo;' => '&#187;',
1906          '&frac14;' => '&#188;',
1907          '&frac12;' => '&#189;',
1908          '&frac34;' => '&#190;',
1909          '&iquest;' => '&#191;',
1910          '&Agrave;' => '&#192;',
1911          '&Aacute;' => '&#193;',
1912          '&Acirc;' => '&#194;',
1913          '&Atilde;' => '&#195;',
1914          '&Auml;' => '&#196;',
1915          '&Aring;' => '&#197;',
1916          '&AElig;' => '&#198;',
1917          '&Ccedil;' => '&#199;',
1918          '&Egrave;' => '&#200;',
1919          '&Eacute;' => '&#201;',
1920          '&Ecirc;' => '&#202;',
1921          '&Euml;' => '&#203;',
1922          '&Igrave;' => '&#204;',
1923          '&Iacute;' => '&#205;',
1924          '&Icirc;' => '&#206;',
1925          '&Iuml;' => '&#207;',
1926          '&ETH;' => '&#208;',
1927          '&Ntilde;' => '&#209;',
1928          '&Ograve;' => '&#210;',
1929          '&Oacute;' => '&#211;',
1930          '&Ocirc;' => '&#212;',
1931          '&Otilde;' => '&#213;',
1932          '&Ouml;' => '&#214;',
1933          '&times;' => '&#215;',
1934          '&Oslash;' => '&#216;',
1935          '&Ugrave;' => '&#217;',
1936          '&Uacute;' => '&#218;',
1937          '&Ucirc;' => '&#219;',
1938          '&Uuml;' => '&#220;',
1939          '&Yacute;' => '&#221;',
1940          '&THORN;' => '&#222;',
1941          '&szlig;' => '&#223;',
1942          '&agrave;' => '&#224;',
1943          '&aacute;' => '&#225;',
1944          '&acirc;' => '&#226;',
1945          '&atilde;' => '&#227;',
1946          '&auml;' => '&#228;',
1947          '&aring;' => '&#229;',
1948          '&aelig;' => '&#230;',
1949          '&ccedil;' => '&#231;',
1950          '&egrave;' => '&#232;',
1951          '&eacute;' => '&#233;',
1952          '&ecirc;' => '&#234;',
1953          '&euml;' => '&#235;',
1954          '&igrave;' => '&#236;',
1955          '&iacute;' => '&#237;',
1956          '&icirc;' => '&#238;',
1957          '&iuml;' => '&#239;',
1958          '&eth;' => '&#240;',
1959          '&ntilde;' => '&#241;',
1960          '&ograve;' => '&#242;',
1961          '&oacute;' => '&#243;',
1962          '&ocirc;' => '&#244;',
1963          '&otilde;' => '&#245;',
1964          '&ouml;' => '&#246;',
1965          '&divide;' => '&#247;',
1966          '&oslash;' => '&#248;',
1967          '&ugrave;' => '&#249;',
1968          '&uacute;' => '&#250;',
1969          '&ucirc;' => '&#251;',
1970          '&uuml;' => '&#252;',
1971          '&yacute;' => '&#253;',
1972          '&thorn;' => '&#254;',
1973          '&yuml;' => '&#255;',
1974          '&OElig;' => '&#338;',
1975          '&oelig;' => '&#339;',
1976          '&Scaron;' => '&#352;',
1977          '&scaron;' => '&#353;',
1978          '&Yuml;' => '&#376;',
1979          '&fnof;' => '&#402;',
1980          '&circ;' => '&#710;',
1981          '&tilde;' => '&#732;',
1982          '&Alpha;' => '&#913;',
1983          '&Beta;' => '&#914;',
1984          '&Gamma;' => '&#915;',
1985          '&Delta;' => '&#916;',
1986          '&Epsilon;' => '&#917;',
1987          '&Zeta;' => '&#918;',
1988          '&Eta;' => '&#919;',
1989          '&Theta;' => '&#920;',
1990          '&Iota;' => '&#921;',
1991          '&Kappa;' => '&#922;',
1992          '&Lambda;' => '&#923;',
1993          '&Mu;' => '&#924;',
1994          '&Nu;' => '&#925;',
1995          '&Xi;' => '&#926;',
1996          '&Omicron;' => '&#927;',
1997          '&Pi;' => '&#928;',
1998          '&Rho;' => '&#929;',
1999          '&Sigma;' => '&#931;',
2000          '&Tau;' => '&#932;',
2001          '&Upsilon;' => '&#933;',
2002          '&Phi;' => '&#934;',
2003          '&Chi;' => '&#935;',
2004          '&Psi;' => '&#936;',
2005          '&Omega;' => '&#937;',
2006          '&alpha;' => '&#945;',
2007          '&beta;' => '&#946;',
2008          '&gamma;' => '&#947;',
2009          '&delta;' => '&#948;',
2010          '&epsilon;' => '&#949;',
2011          '&zeta;' => '&#950;',
2012          '&eta;' => '&#951;',
2013          '&theta;' => '&#952;',
2014          '&iota;' => '&#953;',
2015          '&kappa;' => '&#954;',
2016          '&lambda;' => '&#955;',
2017          '&mu;' => '&#956;',
2018          '&nu;' => '&#957;',
2019          '&xi;' => '&#958;',
2020          '&omicron;' => '&#959;',
2021          '&pi;' => '&#960;',
2022          '&rho;' => '&#961;',
2023          '&sigmaf;' => '&#962;',
2024          '&sigma;' => '&#963;',
2025          '&tau;' => '&#964;',
2026          '&upsilon;' => '&#965;',
2027          '&phi;' => '&#966;',
2028          '&chi;' => '&#967;',
2029          '&psi;' => '&#968;',
2030          '&omega;' => '&#969;',
2031          '&thetasym;' => '&#977;',
2032          '&upsih;' => '&#978;',
2033          '&piv;' => '&#982;',
2034          '&ensp;' => '&#8194;',
2035          '&emsp;' => '&#8195;',
2036          '&thinsp;' => '&#8201;',
2037          '&zwnj;' => '&#8204;',
2038          '&zwj;' => '&#8205;',
2039          '&lrm;' => '&#8206;',
2040          '&rlm;' => '&#8207;',
2041          '&ndash;' => '&#8211;',
2042          '&mdash;' => '&#8212;',
2043          '&lsquo;' => '&#8216;',
2044          '&rsquo;' => '&#8217;',
2045          '&sbquo;' => '&#8218;',
2046          '&ldquo;' => '&#8220;',
2047          '&rdquo;' => '&#8221;',
2048          '&bdquo;' => '&#8222;',
2049          '&dagger;' => '&#8224;',
2050          '&Dagger;' => '&#8225;',
2051          '&bull;' => '&#8226;',
2052          '&hellip;' => '&#8230;',
2053          '&permil;' => '&#8240;',
2054          '&prime;' => '&#8242;',
2055          '&Prime;' => '&#8243;',
2056          '&lsaquo;' => '&#8249;',
2057          '&rsaquo;' => '&#8250;',
2058          '&oline;' => '&#8254;',
2059          '&frasl;' => '&#8260;',
2060          '&euro;' => '&#8364;',
2061          '&image;' => '&#8465;',
2062          '&weierp;' => '&#8472;',
2063          '&real;' => '&#8476;',
2064          '&trade;' => '&#8482;',
2065          '&alefsym;' => '&#8501;',
2066          '&crarr;' => '&#8629;',
2067          '&lArr;' => '&#8656;',
2068          '&uArr;' => '&#8657;',
2069          '&rArr;' => '&#8658;',
2070          '&dArr;' => '&#8659;',
2071          '&hArr;' => '&#8660;',
2072          '&forall;' => '&#8704;',
2073          '&part;' => '&#8706;',
2074          '&exist;' => '&#8707;',
2075          '&empty;' => '&#8709;',
2076          '&nabla;' => '&#8711;',
2077          '&isin;' => '&#8712;',
2078          '&notin;' => '&#8713;',
2079          '&ni;' => '&#8715;',
2080          '&prod;' => '&#8719;',
2081          '&sum;' => '&#8721;',
2082          '&minus;' => '&#8722;',
2083          '&lowast;' => '&#8727;',
2084          '&radic;' => '&#8730;',
2085          '&prop;' => '&#8733;',
2086          '&infin;' => '&#8734;',
2087          '&ang;' => '&#8736;',
2088          '&and;' => '&#8743;',
2089          '&or;' => '&#8744;',
2090          '&cap;' => '&#8745;',
2091          '&cup;' => '&#8746;',
2092          '&int;' => '&#8747;',
2093          '&there4;' => '&#8756;',
2094          '&sim;' => '&#8764;',
2095          '&cong;' => '&#8773;',
2096          '&asymp;' => '&#8776;',
2097          '&ne;' => '&#8800;',
2098          '&equiv;' => '&#8801;',
2099          '&le;' => '&#8804;',
2100          '&ge;' => '&#8805;',
2101          '&sub;' => '&#8834;',
2102          '&sup;' => '&#8835;',
2103          '&nsub;' => '&#8836;',
2104          '&sube;' => '&#8838;',
2105          '&supe;' => '&#8839;',
2106          '&oplus;' => '&#8853;',
2107          '&otimes;' => '&#8855;',
2108          '&perp;' => '&#8869;',
2109          '&sdot;' => '&#8901;',
2110          '&lceil;' => '&#8968;',
2111          '&rceil;' => '&#8969;',
2112          '&lfloor;' => '&#8970;',
2113          '&rfloor;' => '&#8971;',
2114          '&lang;' => '&#9001;',
2115          '&rang;' => '&#9002;',
2116          '&larr;' => '&#8592;',
2117          '&uarr;' => '&#8593;',
2118          '&rarr;' => '&#8594;',
2119          '&darr;' => '&#8595;',
2120          '&harr;' => '&#8596;',
2121          '&loz;' => '&#9674;',
2122          '&spades;' => '&#9824;',
2123          '&clubs;' => '&#9827;',
2124          '&hearts;' => '&#9829;',
2125          '&diams;' => '&#9830;'
2126      );
2127  
2128      return str_replace( array_keys($to_ncr), array_values($to_ncr), $text );
2129  }
2130  
2131  /**
2132   * Formats text for the rich text editor.
2133   *
2134   * The filter 'richedit_pre' is applied here. If $text is empty the filter will
2135   * be applied to an empty string.
2136   *
2137   * @since 2.0.0
2138   *
2139   * @param string $text The text to be formatted.
2140   * @return string The formatted text after filter is applied.
2141   */
2142  function wp_richedit_pre($text) {
2143      // Filtering a blank results in an annoying <br />\n
2144      if ( empty($text) ) return apply_filters('richedit_pre', '');
2145  
2146      $output = convert_chars($text);
2147      $output = wpautop($output);
2148      $output = htmlspecialchars($output, ENT_NOQUOTES);
2149  
2150      return apply_filters('richedit_pre', $output);
2151  }
2152  
2153  /**
2154   * Formats text for the HTML editor.
2155   *
2156   * Unless $output is empty it will pass through htmlspecialchars before the
2157   * 'htmledit_pre' filter is applied.
2158   *
2159   * @since 2.5.0
2160   *
2161   * @param string $output The text to be formatted.
2162   * @return string Formatted text after filter applied.
2163   */
2164  function wp_htmledit_pre($output) {
2165      if ( !empty($output) )
2166          $output = htmlspecialchars($output, ENT_NOQUOTES); // convert only < > &
2167  
2168      return apply_filters('htmledit_pre', $output);
2169  }
2170  
2171  /**
2172   * Perform a deep string replace operation to ensure the values in $search are no longer present
2173   *
2174   * Repeats the replacement operation until it no longer replaces anything so as to remove "nested" values
2175   * e.g. $subject = '%0%0%0DDD', $search ='%0D', $result ='' rather than the '%0%0DD' that
2176   * str_replace would return
2177   *
2178   * @since 2.8.1
2179   * @access private
2180   *
2181   * @param string|array $search
2182   * @param string $subject
2183   * @return string The processed string
2184   */
2185  function _deep_replace($search, $subject){
2186      $found = true;
2187      while($found) {
2188          $found = false;
2189          foreach( (array) $search as $val ) {
2190              while(strpos($subject, $val) !== false) {
2191                  $found = true;
2192                  $subject = str_replace($val, '', $subject);
2193              }
2194          }
2195      }
2196  
2197      return $subject;
2198  }
2199  
2200  /**
2201   * Escapes data for use in a MySQL query
2202   *
2203   * This is just a handy shortcut for $wpdb->escape(), for completeness' sake
2204   *
2205   * @since 2.8.0
2206   * @param string $sql Unescaped SQL data
2207   * @return string The cleaned $sql
2208   */
2209  function esc_sql( $sql ) {
2210      global $wpdb;
2211      return $wpdb->escape( $sql );
2212  }
2213  
2214  /**
2215   * Checks and cleans a URL.
2216   *
2217   * A number of characters are removed from the URL. If the URL is for displaying
2218   * (the default behaviour) amperstands are also replaced. The 'clean_url' filter
2219   * is applied to the returned cleaned URL.
2220   *
2221   * @since 2.8.0
2222   * @uses wp_kses_bad_protocol() To only permit protocols in the URL set
2223   *        via $protocols or the common ones set in the function.
2224   *
2225   * @param string $url The URL to be cleaned.
2226   * @param array $protocols Optional. An array of acceptable protocols.
2227   *        Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet' if not set.
2228   * @param string $_context Private. Use esc_url_raw() for database usage.
2229   * @return string The cleaned $url after the 'clean_url' filter is applied.
2230   */
2231  function esc_url( $url, $protocols = null, $_context = 'display' ) {
2232      $original_url = $url;
2233  
2234      if ('' == $url) return $url;
2235      $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url);
2236      $strip = array('%0d', '%0a', '%0D', '%0A');
2237      $url = _deep_replace($strip, $url);
2238      $url = str_replace(';//', '://', $url);
2239      /* If the URL doesn't appear to contain a scheme, we
2240       * presume it needs http:// appended (unless a relative
2241       * link starting with / or a php file).
2242       */
2243      if ( strpos($url, ':') === false &&
2244          substr( $url, 0, 1 ) != '/' && substr( $url, 0, 1 ) != '#' && !preg_match('/^[a-z0-9-]+?\.php/i', $url) )
2245          $url = 'http://' . $url;
2246  
2247      // Replace ampersands and single quotes only when displaying.
2248      if ( 'display' == $_context ) {
2249          $url = preg_replace('/&([^#])(?![a-z]{2,8};)/', '&#038;$1', $url);
2250          $url = str_replace( "'", '&#039;', $url );
2251      }
2252  
2253      if ( !is_array($protocols) )
2254          $protocols = array('http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet');
2255      if ( wp_kses_bad_protocol( $url, $protocols ) != $url )
2256          return '';
2257  
2258      return apply_filters('clean_url', $url, $original_url, $_context);
2259  }
2260  
2261  /**
2262   * Performs esc_url() for database usage.
2263   *
2264   * @since 2.8.0
2265   * @uses esc_url()
2266   *
2267   * @param string $url The URL to be cleaned.
2268   * @param array $protocols An array of acceptable protocols.
2269   * @return string The cleaned URL.
2270   */
2271  function esc_url_raw( $url, $protocols = null ) {
2272      return esc_url( $url, $protocols, 'db' );
2273  }
2274  
2275  /**
2276   * Convert entities, while preserving already-encoded entities.
2277   *
2278   * @link http://www.php.net/htmlentities Borrowed from the PHP Manual user notes.
2279   *
2280   * @since 1.2.2
2281   *
2282   * @param string $myHTML The text to be converted.
2283   * @return string Converted text.
2284   */
2285  function htmlentities2($myHTML) {
2286      $translation_table = get_html_translation_table( HTML_ENTITIES, ENT_QUOTES );
2287      $translation_table[chr(38)] = '&';
2288      return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/", "&amp;", strtr($myHTML, $translation_table) );
2289  }
2290  
2291  /**
2292   * Escape single quotes, htmlspecialchar " < > &, and fix line endings.
2293   *
2294   * Escapes text strings for echoing in JS. It is intended to be used for inline JS
2295   * (in a tag attribute, for example onclick="..."). Note that the strings have to
2296   * be in single quotes. The filter 'js_escape' is also applied here.
2297   *
2298   * @since 2.8.0
2299   *
2300   * @param string $text The text to be escaped.
2301   * @return string Escaped text.
2302   */
2303  function esc_js( $text ) {
2304      $safe_text = wp_check_invalid_utf8( $text );
2305      $safe_text = _wp_specialchars( $safe_text, ENT_COMPAT );
2306      $safe_text = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", stripslashes( $safe_text ) );
2307      $safe_text = str_replace( "\r", '', $safe_text );
2308      $safe_text = str_replace( "\n", '\\n', addslashes( $safe_text ) );
2309      return apply_filters( 'js_escape', $safe_text, $text );
2310  }
2311  
2312  /**
2313   * Escaping for HTML blocks.
2314   *
2315   * @since 2.8.0
2316   *
2317   * @param string $text
2318   * @return string
2319   */
2320  function esc_html( $text ) {
2321      $safe_text = wp_check_invalid_utf8( $text );
2322      $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
2323      return apply_filters( 'esc_html', $safe_text, $text );
2324  }
2325  
2326  /**
2327   * Escaping for HTML attributes.
2328   *
2329   * @since 2.8.0
2330   *
2331   * @param string $text
2332   * @return string
2333   */
2334  function esc_attr( $text ) {
2335      $safe_text = wp_check_invalid_utf8( $text );
2336      $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
2337      return apply_filters( 'attribute_escape', $safe_text, $text );
2338  }
2339  
2340  /**
2341   * Escape a HTML tag name.
2342   *
2343   * @since 2.5.0
2344   *
2345   * @param string $tag_name
2346   * @return string
2347   */
2348  function tag_escape($tag_name) {
2349      $safe_tag = strtolower( preg_replace('/[^a-zA-Z_:]/', '', $tag_name) );
2350      return apply_filters('tag_escape', $safe_tag, $tag_name);
2351  }
2352  
2353  /**
2354   * Escapes text for SQL LIKE special characters % and _.
2355   *
2356   * @since 2.5.0
2357   *
2358   * @param string $text The text to be escaped.
2359   * @return string text, safe for inclusion in LIKE query.
2360   */
2361  function like_escape($text) {
2362      return str_replace(array("%", "_"), array("\\%", "\\_"), $text);
2363  }
2364  
2365  /**
2366   * Convert full URL paths to absolute paths.
2367   *
2368   * Removes the http or https protocols and the domain. Keeps the path '/' at the
2369   * beginning, so it isn't a true relative link, but from the web root base.
2370   *
2371   * @since 2.1.0
2372   *
2373   * @param string $link Full URL path.
2374   * @return string Absolute path.
2375   */
2376  function wp_make_link_relative( $link ) {
2377      return preg_replace( '|https?://[^/]+(/.*)|i', '$1', $link );
2378  }
2379  
2380  /**
2381   * Sanitises various option values based on the nature of the option.
2382   *
2383   * This is basically a switch statement which will pass $value through a number
2384   * of functions depending on the $option.
2385   *
2386   * @since 2.0.5
2387   *
2388   * @param string $option The name of the option.
2389   * @param string $value The unsanitised value.
2390   * @return string Sanitized value.
2391   */
2392  function sanitize_option($option, $value) {
2393  
2394      switch ($option) {
2395          case 'admin_email':
2396              $value = sanitize_email($value);
2397              if ( !$value && function_exists('add_settings_error') )
2398                  add_settings_error('admin_email', 'invalid_admin_email', __('The E-Mail Address submitted was not in the right format. Please enter a valid Email Address'));
2399              break;
2400  
2401          case 'thumbnail_size_w':
2402          case 'thumbnail_size_h':
2403          case 'medium_size_w':
2404          case 'medium_size_h':
2405          case 'large_size_w':
2406          case 'large_size_h':
2407          case 'embed_size_h':
2408          case 'default_post_edit_rows':
2409          case 'mailserver_port':
2410          case 'comment_max_links':
2411          case 'page_on_front':
2412          case 'rss_excerpt_length':
2413          case 'default_category':
2414          case 'default_email_category':
2415          case 'default_link_category':
2416          case 'close_comments_days_old':
2417          case 'comments_per_page':
2418          case 'thread_comments_depth':
2419          case 'users_can_register':
2420          case 'start_of_week':
2421              $value = absint( $value );
2422              break;
2423  
2424          case 'embed_size_w':
2425              if ( '' !== $value )
2426                  $value = absint( $value );
2427              break;
2428  
2429          case 'posts_per_page':
2430          case 'posts_per_rss':
2431              $value = (int) $value;
2432              if ( empty($value) ) $value = 1;
2433              if ( $value < -1 ) $value = abs($value);
2434              break;
2435  
2436          case 'default_ping_status':
2437          case 'default_comment_status':
2438              // Options that if not there have 0 value but need to be something like "closed"
2439              if ( $value == '0' || $value == '')
2440                  $value = 'closed';
2441              break;
2442  
2443          case 'blogdescription':
2444          case 'blogname':
2445              $value = addslashes($value);
2446              $value = wp_filter_post_kses( $value ); // calls stripslashes then addslashes
2447              $value = stripslashes($value);
2448              $value = esc_html( $value );
2449              break;
2450  
2451          case 'blog_charset':
2452              $value = preg_replace('/[^a-zA-Z0-9_-]/', '', $value); // strips slashes
2453              break;
2454  
2455          case 'date_format':
2456          case 'time_format':
2457          case 'mailserver_url':
2458          case 'mailserver_login':
2459          case 'mailserver_pass':
2460          case 'ping_sites':
2461          case 'upload_path':
2462              $value = strip_tags($value);
2463              $value = addslashes($value);
2464              $value = wp_filter_kses($value); // calls stripslashes then addslashes
2465              $value = stripslashes($value);
2466              break;
2467  
2468          case 'gmt_offset':
2469              $value = preg_replace('/[^0-9:.-]/', '', $value); // strips slashes
2470              break;
2471  
2472          case 'siteurl':
2473          case 'home':
2474              $value = esc_url_raw($value);
2475              break;
2476          default :
2477              $value = apply_filters("sanitize_option_{$option}", $value, $option);
2478              break;
2479      }
2480  
2481      return $value;
2482  }
2483  
2484  /**
2485   * Parses a string into variables to be stored in an array.
2486   *
2487   * Uses {@link http://www.php.net/parse_str parse_str()} and stripslashes if
2488   * {@link http://www.php.net/magic_quotes magic_quotes_gpc} is on.
2489   *
2490   * @since 2.2.1
2491   * @uses apply_filters() for the 'wp_parse_str' filter.
2492   *
2493   * @param string $string The string to be parsed.
2494   * @param array $array Variables will be stored in this array.
2495   */
2496  function wp_parse_str( $string, &$array ) {
2497      parse_str( $string, $array );
2498      if ( get_magic_quotes_gpc() )
2499          $array = stripslashes_deep( $array );
2500      $array = apply_filters( 'wp_parse_str', $array );
2501  }
2502  
2503  /**
2504   * Convert lone less than signs.
2505   *
2506   * KSES already converts lone greater than signs.
2507   *
2508   * @uses wp_pre_kses_less_than_callback in the callback function.
2509   * @since 2.3.0
2510   *
2511   * @param string $text Text to be converted.
2512   * @return string Converted text.
2513   */
2514  function wp_pre_kses_less_than( $text ) {
2515      return preg_replace_callback('%<[^>]*?((?=<)|>|$)%', 'wp_pre_kses_less_than_callback', $text);
2516  }
2517  
2518  /**
2519   * Callback function used by preg_replace.
2520   *
2521   * @uses esc_html to format the $matches text.
2522   * @since 2.3.0
2523   *
2524   * @param array $matches Populated by matches to preg_replace.
2525   * @return string The text returned after esc_html if needed.
2526   */
2527  function wp_pre_kses_less_than_callback( $matches ) {
2528      if ( false === strpos($matches[0], '>') )
2529          return esc_html($matches[0]);
2530      return $matches[0];
2531  }
2532  
2533  /**
2534   * WordPress implementation of PHP sprintf() with filters.
2535   *
2536   * @since 2.5.0
2537   * @link http://www.php.net/sprintf
2538   *
2539   * @param string $pattern The string which formatted args are inserted.
2540   * @param mixed $args,... Arguments to be formatted into the $pattern string.
2541   * @return string The formatted string.
2542   */
2543  function wp_sprintf( $pattern ) {
2544      $args = func_get_args( );
2545      $len = strlen($pattern);
2546      $start = 0;
2547      $result = '';
2548      $arg_index = 0;
2549      while ( $len > $start ) {
2550          // Last character: append and break
2551          if ( strlen($pattern) - 1 == $start ) {
2552              $result .= substr($pattern, -1);
2553              break;
2554          }
2555  
2556          // Literal %: append and continue
2557          if ( substr($pattern, $start, 2) == '%%' ) {
2558              $start += 2;
2559              $result .= '%';
2560              continue;
2561          }
2562  
2563          // Get fragment before next %
2564          $end = strpos($pattern, '%', $start + 1);
2565          if ( false === $end )
2566              $end = $len;
2567          $fragment = substr($pattern, $start, $end - $start);
2568  
2569          // Fragment has a specifier
2570          if ( $pattern{$start} == '%' ) {
2571              // Find numbered arguments or take the next one in order
2572              if ( preg_match('/^%(\d+)\$/', $fragment, $matches) ) {
2573                  $arg = isset($args[$matches[1]]) ? $args[$matches[1]] : '';
2574                  $fragment = str_replace("%{$matches[1]}$", '%', $fragment);
2575              } else {
2576                  ++$arg_index;
2577                  $arg = isset($args[$arg_index]) ? $args[$arg_index] : '';
2578              }
2579  
2580              // Apply filters OR sprintf
2581              $_fragment = apply_filters( 'wp_sprintf', $fragment, $arg );
2582              if ( $_fragment != $fragment )
2583                  $fragment = $_fragment;
2584              else
2585                  $fragment = sprintf($fragment, strval($arg) );
2586          }
2587  
2588          // Append to result and move to next fragment
2589          $result .= $fragment;
2590          $start = $end;
2591      }
2592      return $result;
2593  }
2594  
2595  /**
2596   * Localize list items before the rest of the content.
2597   *
2598   * The '%l' must be at the first characters can then contain the rest of the
2599   * content. The list items will have ', ', ', and', and ' and ' added depending
2600   * on the amount of list items in the $args parameter.
2601   *
2602   * @since 2.5.0
2603   *
2604   * @param string $pattern Content containing '%l' at the beginning.
2605   * @param array $args List items to prepend to the content and replace '%l'.
2606   * @return string Localized list items and rest of the content.
2607   */
2608  function wp_sprintf_l($pattern, $args) {
2609      // Not a match
2610      if ( substr($pattern, 0, 2) != '%l' )
2611          return $pattern;
2612  
2613      // Nothing to work with
2614      if ( empty($args) )
2615          return '';
2616  
2617      // Translate and filter the delimiter set (avoid ampersands and entities here)
2618      $l = apply_filters('wp_sprintf_l', array(
2619          /* translators: used between list items, there is a space after the coma */
2620          'between'          => __(', '),
2621          /* translators: used between list items, there is a space after the and */
2622          'between_last_two' => __(', and '),
2623          /* translators: used between only two list items, there is a space after the and */
2624          'between_only_two' => __(' and '),
2625          ));
2626  
2627      $args = (array) $args;
2628      $result = array_shift($args);
2629      if ( count($args) == 1 )
2630          $result .= $l['between_only_two'] . array_shift($args);
2631      // Loop when more than two args
2632      $i = count($args);
2633      while ( $i ) {
2634          $arg = array_shift($args);
2635          $i--;
2636          if ( 0 == $i )
2637              $result .= $l['between_last_two'] . $arg;
2638          else
2639              $result .= $l['between'] . $arg;
2640      }
2641      return $result . substr($pattern, 2);
2642  }
2643  
2644  /**
2645   * Safely extracts not more than the first $count characters from html string.
2646   *
2647   * UTF-8, tags and entities safe prefix extraction. Entities inside will *NOT*
2648   * be counted as one character. For example &amp; will be counted as 4, &lt; as
2649   * 3, etc.
2650   *
2651   * @since 2.5.0
2652   *
2653   * @param integer $str String to get the excerpt from.
2654   * @param integer $count Maximum number of characters to take.
2655   * @return string The excerpt.
2656   */
2657  function wp_html_excerpt( $str, $count ) {
2658      $str = wp_strip_all_tags( $str, true );
2659      $str = mb_substr( $str, 0, $count );
2660      // remove part of an entity at the end
2661      $str = preg_replace( '/&[^;\s]{0,6}$/', '', $str );
2662      return $str;
2663  }
2664  
2665  /**
2666   * Add a Base url to relative links in passed content.
2667   *
2668   * By default it supports the 'src' and 'href' attributes. However this can be
2669   * changed via the 3rd param.
2670   *
2671   * @since 2.7.0
2672   *
2673   * @param string $content String to search for links in.
2674   * @param string $base The base URL to prefix to links.
2675   * @param array $attrs The attributes which should be processed.
2676   * @return string The processed content.
2677   */
2678  function links_add_base_url( $content, $base, $attrs = array('src', 'href') ) {
2679      $attrs = implode('|', (array)$attrs);
2680      return preg_replace_callback("!($attrs)=(['\"])(.+?)\\2!i",
2681              create_function('$m', 'return _links_add_base($m, "' . $base . '");'),
2682              $content);
2683  }
2684  
2685  /**
2686   * Callback to add a base url to relative links in passed content.
2687   *
2688   * @since 2.7.0
2689   * @access private
2690   *
2691   * @param string $m The matched link.
2692   * @param string $base The base URL to prefix to links.
2693   * @return string The processed link.
2694   */
2695  function _links_add_base($m, $base) {
2696      //1 = attribute name  2 = quotation mark  3 = URL
2697      return $m[1] . '=' . $m[2] .
2698          (strpos($m[3], 'http://') === false ?
2699              path_join($base, $m[3]) :
2700              $m[3])
2701          . $m[2];
2702  }
2703  
2704  /**
2705   * Adds a Target attribute to all links in passed content.
2706   *
2707   * This function by default only applies to <a> tags, however this can be
2708   * modified by the 3rd param.
2709   *
2710   * <b>NOTE:</b> Any current target attributed will be striped and replaced.
2711   *
2712   * @since 2.7.0
2713   *
2714   * @param string $content String to search for links in.
2715   * @param string $target The Target to add to the links.
2716   * @param array $tags An array of tags to apply to.
2717   * @return string The processed content.
2718   */
2719  function links_add_target( $content, $target = '_blank', $tags = array('a') ) {
2720      $tags = implode('|', (array)$tags);
2721      return preg_replace_callback("!<($tags)(.+?)>!i",
2722              create_function('$m', 'return _links_add_target($m, "' . $target . '");'),
2723              $content);
2724  }
2725  
2726  /**
2727   * Callback to add a target attribute to all links in passed content.
2728   *
2729   * @since 2.7.0
2730   * @access private
2731   *
2732   * @param string $m The matched link.
2733   * @param string $target The Target to add to the links.
2734   * @return string The processed link.
2735   */
2736  function _links_add_target( $m, $target ) {
2737      $tag = $m[1];
2738      $link = preg_replace('|(target=[\'"](.*?)[\'"])|i', '', $m[2]);
2739      return '<' . $tag . $link . ' target="' . $target . '">';
2740  }
2741  
2742  // normalize EOL characters and strip duplicate whitespace
2743  function normalize_whitespace( $str ) {
2744      $str  = trim($str);
2745      $str  = str_replace("\r", "\n", $str);
2746      $str  = preg_replace( array( '/\n+/', '/[ \t]+/' ), array( "\n", ' ' ), $str );
2747      return $str;
2748  }
2749  
2750  /**
2751   * Properly strip all HTML tags including script and style
2752   *
2753   * @since 2.9.0
2754   *
2755   * @param string $string String containing HTML tags
2756   * @param bool $remove_breaks optional Whether to remove left over line breaks and white space chars
2757   * @return string The processed string.
2758   */
2759  function wp_strip_all_tags($string, $remove_breaks = false) {
2760      $string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string );
2761      $string = strip_tags($string);
2762  
2763      if ( $remove_breaks )
2764          $string = preg_replace('/[\r\n\t ]+/', ' ', $string);
2765  
2766      return trim($string);
2767  }
2768  
2769  /**
2770   * Sanitize a string from user input or from the db
2771   *
2772   * check for invalid UTF-8,
2773   * Convert single < characters to entity,
2774   * strip all tags,
2775   * remove line breaks, tabs and extra white space,
2776   * strip octets.
2777   *
2778   * @since 2.9.0
2779   *
2780   * @param string $str
2781   * @return string
2782   */
2783  function sanitize_text_field($str) {
2784      $filtered = wp_check_invalid_utf8( $str );
2785  
2786      if ( strpos($filtered, '<') !== false ) {
2787          $filtered = wp_pre_kses_less_than( $filtered );
2788          // This will strip extra whitespace for us.
2789          $filtered = wp_strip_all_tags( $filtered, true );
2790      } else {
2791          $filtered = trim( preg_replace('/[\r\n\t ]+/', ' ', $filtered) );
2792      }
2793  
2794      $match = array();
2795      $found = false;
2796      while ( preg_match('/%[a-f0-9]{2}/i', $filtered, $match) ) {
2797          $filtered = str_replace($match[0], '', $filtered);
2798          $found = true;
2799      }
2800  
2801      if ( $found ) {
2802          // Strip out the whitespace that may now exist after removing the octets.
2803          $filtered = trim( preg_replace('/ +/', ' ', $filtered) );
2804      }
2805  
2806      return apply_filters('sanitize_text_field', $filtered, $str);
2807  }
2808  
2809  ?>


Generated: Mon Apr 5 14:26:09 2010 Cross-referenced by PHPXref 0.7