PageRenderTime 292ms CodeModel.GetById 121ms app.highlight 107ms RepoModel.GetById 13ms app.codeStats 2ms

/wp-includes/formatting.php

https://gitlab.com/geyson/geyson
PHP | 4609 lines | 2709 code | 402 blank | 1498 comment | 403 complexity | 491b3be7278d4d351c58be9615a5432f MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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 *
  15 *     'cause today's effort makes it worth tomorrow's "holiday" ...
  16 *
  17 * Becomes:
  18 *
  19 *     &#8217;cause today&#8217;s effort makes it worth tomorrow&#8217;s &#8220;holiday&#8221; &#8230;
  20 *
  21 * Code within certain html blocks are skipped.
  22 *
  23 * Do not use this function before the 'init' action hook; everything will break.
  24 *
  25 * @since 0.71
  26 *
  27 * @global array $wp_cockneyreplace Array of formatted entities for certain common phrases
  28 * @global array $shortcode_tags
  29 * @staticvar array $static_characters
  30 * @staticvar array $static_replacements
  31 * @staticvar array $dynamic_characters
  32 * @staticvar array $dynamic_replacements
  33 * @staticvar array $default_no_texturize_tags
  34 * @staticvar array $default_no_texturize_shortcodes
  35 * @staticvar bool  $run_texturize
  36 *
  37 * @param string $text The text to be formatted
  38 * @param bool   $reset Set to true for unit testing. Translated patterns will reset.
  39 * @return string The string replaced with html entities
  40 */
  41function wptexturize( $text, $reset = false ) {
  42	global $wp_cockneyreplace, $shortcode_tags;
  43	static $static_characters = null,
  44		$static_replacements = null,
  45		$dynamic_characters = null,
  46		$dynamic_replacements = null,
  47		$default_no_texturize_tags = null,
  48		$default_no_texturize_shortcodes = null,
  49		$run_texturize = true,
  50		$apos = null,
  51		$prime = null,
  52		$double_prime = null,
  53		$opening_quote = null,
  54		$closing_quote = null,
  55		$opening_single_quote = null,
  56		$closing_single_quote = null,
  57		$open_q_flag = '<!--oq-->',
  58		$open_sq_flag = '<!--osq-->',
  59		$apos_flag = '<!--apos-->';
  60
  61	// If there's nothing to do, just stop.
  62	if ( empty( $text ) || false === $run_texturize ) {
  63		return $text;
  64	}
  65
  66	// Set up static variables. Run once only.
  67	if ( $reset || ! isset( $static_characters ) ) {
  68		/**
  69		 * Filter whether to skip running wptexturize().
  70		 *
  71		 * Passing false to the filter will effectively short-circuit wptexturize().
  72		 * returning the original text passed to the function instead.
  73		 *
  74		 * The filter runs only once, the first time wptexturize() is called.
  75		 *
  76		 * @since 4.0.0
  77		 *
  78		 * @see wptexturize()
  79		 *
  80		 * @param bool $run_texturize Whether to short-circuit wptexturize().
  81		 */
  82		$run_texturize = apply_filters( 'run_wptexturize', $run_texturize );
  83		if ( false === $run_texturize ) {
  84			return $text;
  85		}
  86
  87		/* translators: opening curly double quote */
  88		$opening_quote = _x( '&#8220;', 'opening curly double quote' );
  89		/* translators: closing curly double quote */
  90		$closing_quote = _x( '&#8221;', 'closing curly double quote' );
  91
  92		/* translators: apostrophe, for example in 'cause or can't */
  93		$apos = _x( '&#8217;', 'apostrophe' );
  94
  95		/* translators: prime, for example in 9' (nine feet) */
  96		$prime = _x( '&#8242;', 'prime' );
  97		/* translators: double prime, for example in 9" (nine inches) */
  98		$double_prime = _x( '&#8243;', 'double prime' );
  99
 100		/* translators: opening curly single quote */
 101		$opening_single_quote = _x( '&#8216;', 'opening curly single quote' );
 102		/* translators: closing curly single quote */
 103		$closing_single_quote = _x( '&#8217;', 'closing curly single quote' );
 104
 105		/* translators: en dash */
 106		$en_dash = _x( '&#8211;', 'en dash' );
 107		/* translators: em dash */
 108		$em_dash = _x( '&#8212;', 'em dash' );
 109
 110		$default_no_texturize_tags = array('pre', 'code', 'kbd', 'style', 'script', 'tt');
 111		$default_no_texturize_shortcodes = array('code');
 112
 113		// if a plugin has provided an autocorrect array, use it
 114		if ( isset($wp_cockneyreplace) ) {
 115			$cockney = array_keys( $wp_cockneyreplace );
 116			$cockneyreplace = array_values( $wp_cockneyreplace );
 117		} else {
 118			/* translators: This is a comma-separated list of words that defy the syntax of quotations in normal use,
 119			 * for example...  'We do not have enough words yet' ... is a typical quoted phrase.  But when we write
 120			 * lines of code 'til we have enough of 'em, then we need to insert apostrophes instead of quotes.
 121			 */
 122			$cockney = explode( ',', _x( "'tain't,'twere,'twas,'tis,'twill,'til,'bout,'nuff,'round,'cause,'em",
 123				'Comma-separated list of words to texturize in your language' ) );
 124
 125			$cockneyreplace = explode( ',', _x( '&#8217;tain&#8217;t,&#8217;twere,&#8217;twas,&#8217;tis,&#8217;twill,&#8217;til,&#8217;bout,&#8217;nuff,&#8217;round,&#8217;cause,&#8217;em',
 126				'Comma-separated list of replacement words in your language' ) );
 127		}
 128
 129		$static_characters = array_merge( array( '...', '``', '\'\'', ' (tm)' ), $cockney );
 130		$static_replacements = array_merge( array( '&#8230;', $opening_quote, $closing_quote, ' &#8482;' ), $cockneyreplace );
 131
 132
 133		// Pattern-based replacements of characters.
 134		// Sort the remaining patterns into several arrays for performance tuning.
 135		$dynamic_characters = array( 'apos' => array(), 'quote' => array(), 'dash' => array() );
 136		$dynamic_replacements = array( 'apos' => array(), 'quote' => array(), 'dash' => array() );
 137		$dynamic = array();
 138		$spaces = wp_spaces_regexp();
 139
 140		// '99' and '99" are ambiguous among other patterns; assume it's an abbreviated year at the end of a quotation.
 141		if ( "'" !== $apos || "'" !== $closing_single_quote ) {
 142			$dynamic[ '/\'(\d\d)\'(?=\Z|[.,:;!?)}\-\]]|&gt;|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_single_quote;
 143		}
 144		if ( "'" !== $apos || '"' !== $closing_quote ) {
 145			$dynamic[ '/\'(\d\d)"(?=\Z|[.,:;!?)}\-\]]|&gt;|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_quote;
 146		}
 147
 148		// '99 '99s '99's (apostrophe)  But never '9 or '99% or '999 or '99.0.
 149		if ( "'" !== $apos ) {
 150			$dynamic[ '/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/' ] = $apos_flag;
 151		}
 152
 153		// Quoted Numbers like '0.42'
 154		if ( "'" !== $opening_single_quote && "'" !== $closing_single_quote ) {
 155			$dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $open_sq_flag . '$1' . $closing_single_quote;
 156		}
 157
 158		// Single quote at start, or preceded by (, {, <, [, ", -, or spaces.
 159		if ( "'" !== $opening_single_quote ) {
 160			$dynamic[ '/(?<=\A|[([{"\-]|&lt;|' . $spaces . ')\'/' ] = $open_sq_flag;
 161		}
 162
 163		// Apostrophe in a word.  No spaces, double apostrophes, or other punctuation.
 164		if ( "'" !== $apos ) {
 165			$dynamic[ '/(?<!' . $spaces . ')\'(?!\Z|[.,:;!?"\'(){}[\]\-]|&[lg]t;|' . $spaces . ')/' ] = $apos_flag;
 166		}
 167
 168		$dynamic_characters['apos'] = array_keys( $dynamic );
 169		$dynamic_replacements['apos'] = array_values( $dynamic );
 170		$dynamic = array();
 171
 172		// Quoted Numbers like "42"
 173		if ( '"' !== $opening_quote && '"' !== $closing_quote ) {
 174			$dynamic[ '/(?<=\A|' . $spaces . ')"(\d[.,\d]*)"/' ] = $open_q_flag . '$1' . $closing_quote;
 175		}
 176
 177		// Double quote at start, or preceded by (, {, <, [, -, or spaces, and not followed by spaces.
 178		if ( '"' !== $opening_quote ) {
 179			$dynamic[ '/(?<=\A|[([{\-]|&lt;|' . $spaces . ')"(?!' . $spaces . ')/' ] = $open_q_flag;
 180		}
 181
 182		$dynamic_characters['quote'] = array_keys( $dynamic );
 183		$dynamic_replacements['quote'] = array_values( $dynamic );
 184		$dynamic = array();
 185
 186		// Dashes and spaces
 187		$dynamic[ '/---/' ] = $em_dash;
 188		$dynamic[ '/(?<=^|' . $spaces . ')--(?=$|' . $spaces . ')/' ] = $em_dash;
 189		$dynamic[ '/(?<!xn)--/' ] = $en_dash;
 190		$dynamic[ '/(?<=^|' . $spaces . ')-(?=$|' . $spaces . ')/' ] = $en_dash;
 191
 192		$dynamic_characters['dash'] = array_keys( $dynamic );
 193		$dynamic_replacements['dash'] = array_values( $dynamic );
 194	}
 195
 196	// Must do this every time in case plugins use these filters in a context sensitive manner
 197	/**
 198	 * Filter the list of HTML elements not to texturize.
 199	 *
 200	 * @since 2.8.0
 201	 *
 202	 * @param array $default_no_texturize_tags An array of HTML element names.
 203	 */
 204	$no_texturize_tags = apply_filters( 'no_texturize_tags', $default_no_texturize_tags );
 205	/**
 206	 * Filter the list of shortcodes not to texturize.
 207	 *
 208	 * @since 2.8.0
 209	 *
 210	 * @param array $default_no_texturize_shortcodes An array of shortcode names.
 211	 */
 212	$no_texturize_shortcodes = apply_filters( 'no_texturize_shortcodes', $default_no_texturize_shortcodes );
 213
 214	$no_texturize_tags_stack = array();
 215	$no_texturize_shortcodes_stack = array();
 216
 217	// Look for shortcodes and HTML elements.
 218
 219	$tagnames = array_keys( $shortcode_tags );
 220	$tagregexp = join( '|', array_map( 'preg_quote', $tagnames ) );
 221	$tagregexp = "(?:$tagregexp)(?![\\w-])"; // Excerpt of get_shortcode_regex().
 222
 223	$comment_regex =
 224		  '!'           // Start of comment, after the <.
 225		. '(?:'         // Unroll the loop: Consume everything until --> is found.
 226		.     '-(?!->)' // Dash not followed by end of comment.
 227		.     '[^\-]*+' // Consume non-dashes.
 228		. ')*+'         // Loop possessively.
 229		. '(?:-->)?';   // End of comment. If not found, match all input.
 230
 231	$shortcode_regex =
 232		  '\['              // Find start of shortcode.
 233		. '[\/\[]?'         // Shortcodes may begin with [/ or [[
 234		. $tagregexp        // Only match registered shortcodes, because performance.
 235		. '(?:'
 236		.     '[^\[\]<>]+'  // Shortcodes do not contain other shortcodes. Quantifier critical.
 237		. '|'
 238		.     '<[^\[\]>]*>' // HTML elements permitted. Prevents matching ] before >.
 239		. ')*+'             // Possessive critical.
 240		. '\]'              // Find end of shortcode.
 241		. '\]?';            // Shortcodes may end with ]]
 242
 243	$regex =
 244		  '/('                   // Capture the entire match.
 245		.     '<'                // Find start of element.
 246		.     '(?(?=!--)'        // Is this a comment?
 247		.         $comment_regex // Find end of comment.
 248		.     '|'
 249		.         '[^>]*>'       // Find end of element.
 250		.     ')'
 251		. '|'
 252		.     $shortcode_regex   // Find shortcodes.
 253		. ')/s';
 254
 255	$textarr = preg_split( $regex, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
 256
 257	foreach ( $textarr as &$curl ) {
 258		// Only call _wptexturize_pushpop_element if $curl is a delimiter.
 259		$first = $curl[0];
 260		if ( '<' === $first && '<!--' === substr( $curl, 0, 4 ) ) {
 261			// This is an HTML comment delimeter.
 262
 263			continue;
 264
 265		} elseif ( '<' === $first && '>' === substr( $curl, -1 ) ) {
 266			// This is an HTML element delimiter.
 267
 268			_wptexturize_pushpop_element( $curl, $no_texturize_tags_stack, $no_texturize_tags );
 269
 270		} elseif ( '' === trim( $curl ) ) {
 271			// This is a newline between delimiters.  Performance improves when we check this.
 272
 273			continue;
 274
 275		} elseif ( '[' === $first && 1 === preg_match( '/^' . $shortcode_regex . '$/', $curl ) ) {
 276			// This is a shortcode delimiter.
 277
 278			if ( '[[' !== substr( $curl, 0, 2 ) && ']]' !== substr( $curl, -2 ) ) {
 279				// Looks like a normal shortcode.
 280				_wptexturize_pushpop_element( $curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes );
 281			} else {
 282				// Looks like an escaped shortcode.
 283				continue;
 284			}
 285
 286		} elseif ( empty( $no_texturize_shortcodes_stack ) && empty( $no_texturize_tags_stack ) ) {
 287			// This is neither a delimiter, nor is this content inside of no_texturize pairs.  Do texturize.
 288
 289			$curl = str_replace( $static_characters, $static_replacements, $curl );
 290
 291			if ( false !== strpos( $curl, "'" ) ) {
 292				$curl = preg_replace( $dynamic_characters['apos'], $dynamic_replacements['apos'], $curl );
 293				$curl = wptexturize_primes( $curl, "'", $prime, $open_sq_flag, $closing_single_quote );
 294				$curl = str_replace( $apos_flag, $apos, $curl );
 295				$curl = str_replace( $open_sq_flag, $opening_single_quote, $curl );
 296			}
 297			if ( false !== strpos( $curl, '"' ) ) {
 298				$curl = preg_replace( $dynamic_characters['quote'], $dynamic_replacements['quote'], $curl );
 299				$curl = wptexturize_primes( $curl, '"', $double_prime, $open_q_flag, $closing_quote );
 300				$curl = str_replace( $open_q_flag, $opening_quote, $curl );
 301			}
 302			if ( false !== strpos( $curl, '-' ) ) {
 303				$curl = preg_replace( $dynamic_characters['dash'], $dynamic_replacements['dash'], $curl );
 304			}
 305
 306			// 9x9 (times), but never 0x9999
 307			if ( 1 === preg_match( '/(?<=\d)x\d/', $curl ) ) {
 308				// Searching for a digit is 10 times more expensive than for the x, so we avoid doing this one!
 309				$curl = preg_replace( '/\b(\d(?(?<=0)[\d\.,]+|[\d\.,]*))x(\d[\d\.,]*)\b/', '$1&#215;$2', $curl );
 310			}
 311		}
 312	}
 313	$text = implode( '', $textarr );
 314
 315	// Replace each & with &#038; unless it already looks like an entity.
 316	return preg_replace( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', '&#038;', $text );
 317}
 318
 319/**
 320 * Implements a logic tree to determine whether or not "7'." represents seven feet,
 321 * then converts the special char into either a prime char or a closing quote char.
 322 *
 323 * @since 4.3.0
 324 *
 325 * @param string $haystack    The plain text to be searched.
 326 * @param string $needle      The character to search for such as ' or ".
 327 * @param string $prime       The prime char to use for replacement.
 328 * @param string $open_quote  The opening quote char. Opening quote replacement must be
 329 *                            accomplished already.
 330 * @param string $close_quote The closing quote char to use for replacement.
 331 * @return string The $haystack value after primes and quotes replacements.
 332 */
 333function wptexturize_primes( $haystack, $needle, $prime, $open_quote, $close_quote ) {
 334	$spaces = wp_spaces_regexp();
 335	$flag = '<!--wp-prime-or-quote-->';
 336	$quote_pattern = "/$needle(?=\\Z|[.,:;!?)}\\-\\]]|&gt;|" . $spaces . ")/";
 337	$prime_pattern    = "/(?<=\\d)$needle/";
 338	$flag_after_digit = "/(?<=\\d)$flag/";
 339	$flag_no_digit    = "/(?<!\\d)$flag/";
 340
 341	$sentences = explode( $open_quote, $haystack );
 342
 343	foreach( $sentences as $key => &$sentence ) {
 344		if ( false === strpos( $sentence, $needle ) ) {
 345			continue;
 346		} elseif ( 0 !== $key && 0 === substr_count( $sentence, $close_quote ) ) {
 347			$sentence = preg_replace( $quote_pattern, $flag, $sentence, -1, $count );
 348			if ( $count > 1 ) {
 349				// This sentence appears to have multiple closing quotes.  Attempt Vulcan logic.
 350				$sentence = preg_replace( $flag_no_digit, $close_quote, $sentence, -1, $count2 );
 351				if ( 0 === $count2 ) {
 352					// Try looking for a quote followed by a period.
 353					$count2 = substr_count( $sentence, "$flag." );
 354					if ( $count2 > 0 ) {
 355						// Assume the rightmost quote-period match is the end of quotation.
 356						$pos = strrpos( $sentence, "$flag." );
 357					} else {
 358						// When all else fails, make the rightmost candidate a closing quote.
 359						// This is most likely to be problematic in the context of bug #18549.
 360						$pos = strrpos( $sentence, $flag );
 361					}
 362					$sentence = substr_replace( $sentence, $close_quote, $pos, strlen( $flag ) );
 363				}
 364				// Use conventional replacement on any remaining primes and quotes.
 365				$sentence = preg_replace( $prime_pattern, $prime, $sentence );
 366				$sentence = preg_replace( $flag_after_digit, $prime, $sentence );
 367				$sentence = str_replace( $flag, $close_quote, $sentence );
 368			} elseif ( 1 == $count ) {
 369				// Found only one closing quote candidate, so give it priority over primes.
 370				$sentence = str_replace( $flag, $close_quote, $sentence );
 371				$sentence = preg_replace( $prime_pattern, $prime, $sentence );
 372			} else {
 373				// No closing quotes found.  Just run primes pattern.
 374				$sentence = preg_replace( $prime_pattern, $prime, $sentence );
 375			}
 376		} else {
 377			$sentence = preg_replace( $prime_pattern, $prime, $sentence );
 378			$sentence = preg_replace( $quote_pattern, $close_quote, $sentence );
 379		}
 380		if ( '"' == $needle && false !== strpos( $sentence, '"' ) ) {
 381			$sentence = str_replace( '"', $close_quote, $sentence );
 382		}
 383	}
 384
 385	return implode( $open_quote, $sentences );
 386}
 387
 388/**
 389 * Search for disabled element tags. Push element to stack on tag open and pop
 390 * on tag close.
 391 *
 392 * Assumes first char of $text is tag opening and last char is tag closing.
 393 * Assumes second char of $text is optionally '/' to indicate closing as in </html>.
 394 *
 395 * @since 2.9.0
 396 * @access private
 397 *
 398 * @param string $text Text to check. Must be a tag like `<html>` or `[shortcode]`.
 399 * @param array  $stack List of open tag elements.
 400 * @param array  $disabled_elements The tag names to match against. Spaces are not allowed in tag names.
 401 */
 402function _wptexturize_pushpop_element( $text, &$stack, $disabled_elements ) {
 403	// Is it an opening tag or closing tag?
 404	if ( '/' !== $text[1] ) {
 405		$opening_tag = true;
 406		$name_offset = 1;
 407	} elseif ( 0 == count( $stack ) ) {
 408		// Stack is empty. Just stop.
 409		return;
 410	} else {
 411		$opening_tag = false;
 412		$name_offset = 2;
 413	}
 414
 415	// Parse out the tag name.
 416	$space = strpos( $text, ' ' );
 417	if ( false === $space ) {
 418		$space = -1;
 419	} else {
 420		$space -= $name_offset;
 421	}
 422	$tag = substr( $text, $name_offset, $space );
 423
 424	// Handle disabled tags.
 425	if ( in_array( $tag, $disabled_elements ) ) {
 426		if ( $opening_tag ) {
 427			/*
 428			 * This disables texturize until we find a closing tag of our type
 429			 * (e.g. <pre>) even if there was invalid nesting before that
 430			 *
 431			 * Example: in the case <pre>sadsadasd</code>"baba"</pre>
 432			 *          "baba" won't be texturize
 433			 */
 434
 435			array_push( $stack, $tag );
 436		} elseif ( end( $stack ) == $tag ) {
 437			array_pop( $stack );
 438		}
 439	}
 440}
 441
 442/**
 443 * Replaces double line-breaks with paragraph elements.
 444 *
 445 * A group of regex replaces used to identify text formatted with newlines and
 446 * replace double line-breaks with HTML paragraph tags. The remaining line-breaks
 447 * after conversion become <<br />> tags, unless $br is set to '0' or 'false'.
 448 *
 449 * @since 0.71
 450 *
 451 * @param string $pee The text which has to be formatted.
 452 * @param bool   $br  Optional. If set, this will convert all remaining line-breaks
 453 *                    after paragraphing. Default true.
 454 * @return string Text which has been converted into correct paragraph tags.
 455 */
 456function wpautop( $pee, $br = true ) {
 457	$pre_tags = array();
 458
 459	if ( trim($pee) === '' )
 460		return '';
 461
 462	// Just to make things a little easier, pad the end.
 463	$pee = $pee . "\n";
 464
 465	/*
 466	 * Pre tags shouldn't be touched by autop.
 467	 * Replace pre tags with placeholders and bring them back after autop.
 468	 */
 469	if ( strpos($pee, '<pre') !== false ) {
 470		$pee_parts = explode( '</pre>', $pee );
 471		$last_pee = array_pop($pee_parts);
 472		$pee = '';
 473		$i = 0;
 474
 475		foreach ( $pee_parts as $pee_part ) {
 476			$start = strpos($pee_part, '<pre');
 477
 478			// Malformed html?
 479			if ( $start === false ) {
 480				$pee .= $pee_part;
 481				continue;
 482			}
 483
 484			$name = "<pre wp-pre-tag-$i></pre>";
 485			$pre_tags[$name] = substr( $pee_part, $start ) . '</pre>';
 486
 487			$pee .= substr( $pee_part, 0, $start ) . $name;
 488			$i++;
 489		}
 490
 491		$pee .= $last_pee;
 492	}
 493	// Change multiple <br>s into two line breaks, which will turn into paragraphs.
 494	$pee = preg_replace('|<br\s*/?>\s*<br\s*/?>|', "\n\n", $pee);
 495
 496	$allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)';
 497
 498	// Add a single line break above block-level opening tags.
 499	$pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
 500
 501	// Add a double line break below block-level closing tags.
 502	$pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
 503
 504	// Standardize newline characters to "\n".
 505	$pee = str_replace(array("\r\n", "\r"), "\n", $pee);
 506
 507	// Find newlines in all elements and add placeholders.
 508	$pee = wp_replace_in_html_tags( $pee, array( "\n" => " <!-- wpnl --> " ) );
 509
 510	// Collapse line breaks before and after <option> elements so they don't get autop'd.
 511	if ( strpos( $pee, '<option' ) !== false ) {
 512		$pee = preg_replace( '|\s*<option|', '<option', $pee );
 513		$pee = preg_replace( '|</option>\s*|', '</option>', $pee );
 514	}
 515
 516	/*
 517	 * Collapse line breaks inside <object> elements, before <param> and <embed> elements
 518	 * so they don't get autop'd.
 519	 */
 520	if ( strpos( $pee, '</object>' ) !== false ) {
 521		$pee = preg_replace( '|(<object[^>]*>)\s*|', '$1', $pee );
 522		$pee = preg_replace( '|\s*</object>|', '</object>', $pee );
 523		$pee = preg_replace( '%\s*(</?(?:param|embed)[^>]*>)\s*%', '$1', $pee );
 524	}
 525
 526	/*
 527	 * Collapse line breaks inside <audio> and <video> elements,
 528	 * before and after <source> and <track> elements.
 529	 */
 530	if ( strpos( $pee, '<source' ) !== false || strpos( $pee, '<track' ) !== false ) {
 531		$pee = preg_replace( '%([<\[](?:audio|video)[^>\]]*[>\]])\s*%', '$1', $pee );
 532		$pee = preg_replace( '%\s*([<\[]/(?:audio|video)[>\]])%', '$1', $pee );
 533		$pee = preg_replace( '%\s*(<(?:source|track)[^>]*>)\s*%', '$1', $pee );
 534	}
 535
 536	// Remove more than two contiguous line breaks.
 537	$pee = preg_replace("/\n\n+/", "\n\n", $pee);
 538
 539	// Split up the contents into an array of strings, separated by double line breaks.
 540	$pees = preg_split('/\n\s*\n/', $pee, -1, PREG_SPLIT_NO_EMPTY);
 541
 542	// Reset $pee prior to rebuilding.
 543	$pee = '';
 544
 545	// Rebuild the content as a string, wrapping every bit with a <p>.
 546	foreach ( $pees as $tinkle ) {
 547		$pee .= '<p>' . trim($tinkle, "\n") . "</p>\n";
 548	}
 549
 550	// Under certain strange conditions it could create a P of entirely whitespace.
 551	$pee = preg_replace('|<p>\s*</p>|', '', $pee);
 552
 553	// Add a closing <p> inside <div>, <address>, or <form> tag if missing.
 554	$pee = preg_replace('!<p>([^<]+)</(div|address|form)>!', "<p>$1</p></$2>", $pee);
 555
 556	// If an opening or closing block element tag is wrapped in a <p>, unwrap it.
 557	$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
 558
 559	// In some cases <li> may get wrapped in <p>, fix them.
 560	$pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee);
 561
 562	// If a <blockquote> is wrapped with a <p>, move it inside the <blockquote>.
 563	$pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
 564	$pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
 565
 566	// If an opening or closing block element tag is preceded by an opening <p> tag, remove it.
 567	$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
 568
 569	// If an opening or closing block element tag is followed by a closing <p> tag, remove it.
 570	$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
 571
 572	// Optionally insert line breaks.
 573	if ( $br ) {
 574		// Replace newlines that shouldn't be touched with a placeholder.
 575		$pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', '_autop_newline_preservation_helper', $pee);
 576
 577		// Normalize <br>
 578		$pee = str_replace( array( '<br>', '<br/>' ), '<br />', $pee );
 579
 580		// Replace any new line characters that aren't preceded by a <br /> with a <br />.
 581		$pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee);
 582
 583		// Replace newline placeholders with newlines.
 584		$pee = str_replace('<WPPreserveNewline />', "\n", $pee);
 585	}
 586
 587	// If a <br /> tag is after an opening or closing block tag, remove it.
 588	$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee);
 589
 590	// If a <br /> tag is before a subset of opening or closing block tags, remove it.
 591	$pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
 592	$pee = preg_replace( "|\n</p>$|", '</p>', $pee );
 593
 594	// Replace placeholder <pre> tags with their original content.
 595	if ( !empty($pre_tags) )
 596		$pee = str_replace(array_keys($pre_tags), array_values($pre_tags), $pee);
 597
 598	// Restore newlines in all elements.
 599	if ( false !== strpos( $pee, '<!-- wpnl -->' ) ) {
 600		$pee = str_replace( array( ' <!-- wpnl --> ', '<!-- wpnl -->' ), "\n", $pee );
 601	}
 602
 603	return $pee;
 604}
 605
 606/**
 607 * Separate HTML elements and comments from the text.
 608 *
 609 * @since 4.2.4
 610 *
 611 * @param string $input The text which has to be formatted.
 612 * @return array The formatted text.
 613 */
 614function wp_html_split( $input ) {
 615	static $regex;
 616
 617	if ( ! isset( $regex ) ) {
 618		$comments =
 619			  '!'           // Start of comment, after the <.
 620			. '(?:'         // Unroll the loop: Consume everything until --> is found.
 621			.     '-(?!->)' // Dash not followed by end of comment.
 622			.     '[^\-]*+' // Consume non-dashes.
 623			. ')*+'         // Loop possessively.
 624			. '(?:-->)?';   // End of comment. If not found, match all input.
 625
 626		$cdata =
 627			  '!\[CDATA\['  // Start of comment, after the <.
 628			. '[^\]]*+'     // Consume non-].
 629			. '(?:'         // Unroll the loop: Consume everything until ]]> is found.
 630			.     '](?!]>)' // One ] not followed by end of comment.
 631			.     '[^\]]*+' // Consume non-].
 632			. ')*+'         // Loop possessively.
 633			. '(?:]]>)?';   // End of comment. If not found, match all input.
 634
 635		$regex =
 636			  '/('              // Capture the entire match.
 637			.     '<'           // Find start of element.
 638			.     '(?(?=!--)'   // Is this a comment?
 639			.         $comments // Find end of comment.
 640			.     '|'
 641			.         '(?(?=!\[CDATA\[)' // Is this a comment?
 642			.             $cdata // Find end of comment.
 643			.         '|'
 644			.             '[^>]*>?' // Find end of element. If not found, match all input.
 645			.         ')'
 646			.     ')'
 647			. ')/s';
 648	}
 649
 650	return preg_split( $regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE );
 651}
 652
 653/**
 654 * Replace characters or phrases within HTML elements only.
 655 *
 656 * @since 4.2.3
 657 *
 658 * @param string $haystack The text which has to be formatted.
 659 * @param array $replace_pairs In the form array('from' => 'to', ...).
 660 * @return string The formatted text.
 661 */
 662function wp_replace_in_html_tags( $haystack, $replace_pairs ) {
 663	// Find all elements.
 664	$textarr = wp_html_split( $haystack );
 665	$changed = false;
 666
 667	// Optimize when searching for one item.
 668	if ( 1 === count( $replace_pairs ) ) {
 669		// Extract $needle and $replace.
 670		foreach ( $replace_pairs as $needle => $replace );
 671
 672		// Loop through delimeters (elements) only.
 673		for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
 674			if ( false !== strpos( $textarr[$i], $needle ) ) {
 675				$textarr[$i] = str_replace( $needle, $replace, $textarr[$i] );
 676				$changed = true;
 677			}
 678		}
 679	} else {
 680		// Extract all $needles.
 681		$needles = array_keys( $replace_pairs );
 682
 683		// Loop through delimeters (elements) only.
 684		for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
 685			foreach ( $needles as $needle ) {
 686				if ( false !== strpos( $textarr[$i], $needle ) ) {
 687					$textarr[$i] = strtr( $textarr[$i], $replace_pairs );
 688					$changed = true;
 689					// After one strtr() break out of the foreach loop and look at next element.
 690					break;
 691				}
 692			}
 693		}
 694	}
 695
 696	if ( $changed ) {
 697		$haystack = implode( $textarr );
 698	}
 699
 700	return $haystack;
 701}
 702
 703/**
 704 * Newline preservation help function for wpautop
 705 *
 706 * @since 3.1.0
 707 * @access private
 708 *
 709 * @param array $matches preg_replace_callback matches array
 710 * @return string
 711 */
 712function _autop_newline_preservation_helper( $matches ) {
 713	return str_replace( "\n", "<WPPreserveNewline />", $matches[0] );
 714}
 715
 716/**
 717 * Don't auto-p wrap shortcodes that stand alone
 718 *
 719 * Ensures that shortcodes are not wrapped in `<p>...</p>`.
 720 *
 721 * @since 2.9.0
 722 *
 723 * @global array $shortcode_tags
 724 *
 725 * @param string $pee The content.
 726 * @return string The filtered content.
 727 */
 728function shortcode_unautop( $pee ) {
 729	global $shortcode_tags;
 730
 731	if ( empty( $shortcode_tags ) || !is_array( $shortcode_tags ) ) {
 732		return $pee;
 733	}
 734
 735	$tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) );
 736	$spaces = wp_spaces_regexp();
 737
 738	$pattern =
 739		  '/'
 740		. '<p>'                              // Opening paragraph
 741		. '(?:' . $spaces . ')*+'            // Optional leading whitespace
 742		. '('                                // 1: The shortcode
 743		.     '\\['                          // Opening bracket
 744		.     "($tagregexp)"                 // 2: Shortcode name
 745		.     '(?![\\w-])'                   // Not followed by word character or hyphen
 746		                                     // Unroll the loop: Inside the opening shortcode tag
 747		.     '[^\\]\\/]*'                   // Not a closing bracket or forward slash
 748		.     '(?:'
 749		.         '\\/(?!\\])'               // A forward slash not followed by a closing bracket
 750		.         '[^\\]\\/]*'               // Not a closing bracket or forward slash
 751		.     ')*?'
 752		.     '(?:'
 753		.         '\\/\\]'                   // Self closing tag and closing bracket
 754		.     '|'
 755		.         '\\]'                      // Closing bracket
 756		.         '(?:'                      // Unroll the loop: Optionally, anything between the opening and closing shortcode tags
 757		.             '[^\\[]*+'             // Not an opening bracket
 758		.             '(?:'
 759		.                 '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag
 760		.                 '[^\\[]*+'         // Not an opening bracket
 761		.             ')*+'
 762		.             '\\[\\/\\2\\]'         // Closing shortcode tag
 763		.         ')?'
 764		.     ')'
 765		. ')'
 766		. '(?:' . $spaces . ')*+'            // optional trailing whitespace
 767		. '<\\/p>'                           // closing paragraph
 768		. '/s';
 769
 770	return preg_replace( $pattern, '$1', $pee );
 771}
 772
 773/**
 774 * Checks to see if a string is utf8 encoded.
 775 *
 776 * NOTE: This function checks for 5-Byte sequences, UTF8
 777 *       has Bytes Sequences with a maximum length of 4.
 778 *
 779 * @author bmorel at ssi dot fr (modified)
 780 * @since 1.2.1
 781 *
 782 * @param string $str The string to be checked
 783 * @return bool True if $str fits a UTF-8 model, false otherwise.
 784 */
 785function seems_utf8( $str ) {
 786	mbstring_binary_safe_encoding();
 787	$length = strlen($str);
 788	reset_mbstring_encoding();
 789	for ($i=0; $i < $length; $i++) {
 790		$c = ord($str[$i]);
 791		if ($c < 0x80) $n = 0; // 0bbbbbbb
 792		elseif (($c & 0xE0) == 0xC0) $n=1; // 110bbbbb
 793		elseif (($c & 0xF0) == 0xE0) $n=2; // 1110bbbb
 794		elseif (($c & 0xF8) == 0xF0) $n=3; // 11110bbb
 795		elseif (($c & 0xFC) == 0xF8) $n=4; // 111110bb
 796		elseif (($c & 0xFE) == 0xFC) $n=5; // 1111110b
 797		else return false; // Does not match any model
 798		for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
 799			if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
 800				return false;
 801		}
 802	}
 803	return true;
 804}
 805
 806/**
 807 * Converts a number of special characters into their HTML entities.
 808 *
 809 * Specifically deals with: &, <, >, ", and '.
 810 *
 811 * $quote_style can be set to ENT_COMPAT to encode " to
 812 * &quot;, or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded.
 813 *
 814 * @since 1.2.2
 815 * @access private
 816 *
 817 * @staticvar string $_charset
 818 *
 819 * @param string $string         The text which is to be encoded.
 820 * @param int    $quote_style    Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES.
 821 * @param string $charset        Optional. The character encoding of the string. Default is false.
 822 * @param bool   $double_encode  Optional. Whether to encode existing html entities. Default is false.
 823 * @return string The encoded text with HTML entities.
 824 */
 825function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) {
 826	$string = (string) $string;
 827
 828	if ( 0 === strlen( $string ) )
 829		return '';
 830
 831	// Don't bother if there are no specialchars - saves some processing
 832	if ( ! preg_match( '/[&<>"\']/', $string ) )
 833		return $string;
 834
 835	// Account for the previous behaviour of the function when the $quote_style is not an accepted value
 836	if ( empty( $quote_style ) )
 837		$quote_style = ENT_NOQUOTES;
 838	elseif ( ! in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) )
 839		$quote_style = ENT_QUOTES;
 840
 841	// Store the site charset as a static to avoid multiple calls to wp_load_alloptions()
 842	if ( ! $charset ) {
 843		static $_charset = null;
 844		if ( ! isset( $_charset ) ) {
 845			$alloptions = wp_load_alloptions();
 846			$_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : '';
 847		}
 848		$charset = $_charset;
 849	}
 850
 851	if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) )
 852		$charset = 'UTF-8';
 853
 854	$_quote_style = $quote_style;
 855
 856	if ( $quote_style === 'double' ) {
 857		$quote_style = ENT_COMPAT;
 858		$_quote_style = ENT_COMPAT;
 859	} elseif ( $quote_style === 'single' ) {
 860		$quote_style = ENT_NOQUOTES;
 861	}
 862
 863	if ( ! $double_encode ) {
 864		// Guarantee every &entity; is valid, convert &garbage; into &amp;garbage;
 865		// This is required for PHP < 5.4.0 because ENT_HTML401 flag is unavailable.
 866		$string = wp_kses_normalize_entities( $string );
 867	}
 868
 869	$string = @htmlspecialchars( $string, $quote_style, $charset, $double_encode );
 870
 871	// Backwards compatibility
 872	if ( 'single' === $_quote_style )
 873		$string = str_replace( "'", '&#039;', $string );
 874
 875	return $string;
 876}
 877
 878/**
 879 * Converts a number of HTML entities into their special characters.
 880 *
 881 * Specifically deals with: &, <, >, ", and '.
 882 *
 883 * $quote_style can be set to ENT_COMPAT to decode " entities,
 884 * or ENT_QUOTES to do both " and '. Default is ENT_NOQUOTES where no quotes are decoded.
 885 *
 886 * @since 2.8.0
 887 *
 888 * @param string     $string The text which is to be decoded.
 889 * @param string|int $quote_style Optional. Converts double quotes if set to ENT_COMPAT,
 890 *                                both single and double if set to ENT_QUOTES or
 891 *                                none if set to ENT_NOQUOTES.
 892 *                                Also compatible with old _wp_specialchars() values;
 893 *                                converting single quotes if set to 'single',
 894 *                                double if set to 'double' or both if otherwise set.
 895 *                                Default is ENT_NOQUOTES.
 896 * @return string The decoded text without HTML entities.
 897 */
 898function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) {
 899	$string = (string) $string;
 900
 901	if ( 0 === strlen( $string ) ) {
 902		return '';
 903	}
 904
 905	// Don't bother if there are no entities - saves a lot of processing
 906	if ( strpos( $string, '&' ) === false ) {
 907		return $string;
 908	}
 909
 910	// Match the previous behaviour of _wp_specialchars() when the $quote_style is not an accepted value
 911	if ( empty( $quote_style ) ) {
 912		$quote_style = ENT_NOQUOTES;
 913	} elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
 914		$quote_style = ENT_QUOTES;
 915	}
 916
 917	// More complete than get_html_translation_table( HTML_SPECIALCHARS )
 918	$single = array( '&#039;'  => '\'', '&#x27;' => '\'' );
 919	$single_preg = array( '/&#0*39;/'  => '&#039;', '/&#x0*27;/i' => '&#x27;' );
 920	$double = array( '&quot;' => '"', '&#034;'  => '"', '&#x22;' => '"' );
 921	$double_preg = array( '/&#0*34;/'  => '&#034;', '/&#x0*22;/i' => '&#x22;' );
 922	$others = array( '&lt;'   => '<', '&#060;'  => '<', '&gt;'   => '>', '&#062;'  => '>', '&amp;'  => '&', '&#038;'  => '&', '&#x26;' => '&' );
 923	$others_preg = array( '/&#0*60;/'  => '&#060;', '/&#0*62;/'  => '&#062;', '/&#0*38;/'  => '&#038;', '/&#x0*26;/i' => '&#x26;' );
 924
 925	if ( $quote_style === ENT_QUOTES ) {
 926		$translation = array_merge( $single, $double, $others );
 927		$translation_preg = array_merge( $single_preg, $double_preg, $others_preg );
 928	} elseif ( $quote_style === ENT_COMPAT || $quote_style === 'double' ) {
 929		$translation = array_merge( $double, $others );
 930		$translation_preg = array_merge( $double_preg, $others_preg );
 931	} elseif ( $quote_style === 'single' ) {
 932		$translation = array_merge( $single, $others );
 933		$translation_preg = array_merge( $single_preg, $others_preg );
 934	} elseif ( $quote_style === ENT_NOQUOTES ) {
 935		$translation = $others;
 936		$translation_preg = $others_preg;
 937	}
 938
 939	// Remove zero padding on numeric entities
 940	$string = preg_replace( array_keys( $translation_preg ), array_values( $translation_preg ), $string );
 941
 942	// Replace characters according to translation table
 943	return strtr( $string, $translation );
 944}
 945
 946/**
 947 * Checks for invalid UTF8 in a string.
 948 *
 949 * @since 2.8.0
 950 *
 951 * @staticvar bool $is_utf8
 952 * @staticvar bool $utf8_pcre
 953 *
 954 * @param string  $string The text which is to be checked.
 955 * @param bool    $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false.
 956 * @return string The checked text.
 957 */
 958function wp_check_invalid_utf8( $string, $strip = false ) {
 959	$string = (string) $string;
 960
 961	if ( 0 === strlen( $string ) ) {
 962		return '';
 963	}
 964
 965	// Store the site charset as a static to avoid multiple calls to get_option()
 966	static $is_utf8 = null;
 967	if ( ! isset( $is_utf8 ) ) {
 968		$is_utf8 = in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) );
 969	}
 970	if ( ! $is_utf8 ) {
 971		return $string;
 972	}
 973
 974	// Check for support for utf8 in the installed PCRE library once and store the result in a static
 975	static $utf8_pcre = null;
 976	if ( ! isset( $utf8_pcre ) ) {
 977		$utf8_pcre = @preg_match( '/^./u', 'a' );
 978	}
 979	// We can't demand utf8 in the PCRE installation, so just return the string in those cases
 980	if ( !$utf8_pcre ) {
 981		return $string;
 982	}
 983
 984	// preg_match fails when it encounters invalid UTF8 in $string
 985	if ( 1 === @preg_match( '/^./us', $string ) ) {
 986		return $string;
 987	}
 988
 989	// Attempt to strip the bad chars if requested (not recommended)
 990	if ( $strip && function_exists( 'iconv' ) ) {
 991		return iconv( 'utf-8', 'utf-8', $string );
 992	}
 993
 994	return '';
 995}
 996
 997/**
 998 * Encode the Unicode values to be used in the URI.
 999 *
1000 * @since 1.5.0
1001 *
1002 * @param string $utf8_string
1003 * @param int    $length Max  length of the string
1004 * @return string String with Unicode encoded for URI.
1005 */
1006function utf8_uri_encode( $utf8_string, $length = 0 ) {
1007	$unicode = '';
1008	$values = array();
1009	$num_octets = 1;
1010	$unicode_length = 0;
1011
1012	mbstring_binary_safe_encoding();
1013	$string_length = strlen( $utf8_string );
1014	reset_mbstring_encoding();
1015
1016	for ($i = 0; $i < $string_length; $i++ ) {
1017
1018		$value = ord( $utf8_string[ $i ] );
1019
1020		if ( $value < 128 ) {
1021			if ( $length && ( $unicode_length >= $length ) )
1022				break;
1023			$unicode .= chr($value);
1024			$unicode_length++;
1025		} else {
1026			if ( count( $values ) == 0 ) {
1027				if ( $value < 224 ) {
1028					$num_octets = 2;
1029				} elseif ( $value < 240 ) {
1030					$num_octets = 3;
1031				} else {
1032					$num_octets = 4;
1033				}
1034			}
1035
1036			$values[] = $value;
1037
1038			if ( $length && ( $unicode_length + ($num_octets * 3) ) > $length )
1039				break;
1040			if ( count( $values ) == $num_octets ) {
1041				for ( $j = 0; $j < $num_octets; $j++ ) {
1042					$unicode .= '%' . dechex( $values[ $j ] );
1043				}
1044
1045				$unicode_length += $num_octets * 3;
1046
1047				$values = array();
1048				$num_octets = 1;
1049			}
1050		}
1051	}
1052
1053	return $unicode;
1054}
1055
1056/**
1057 * Converts all accent characters to ASCII characters.
1058 *
1059 * If there are no accent characters, then the string given is just returned.
1060 *
1061 * @since 1.2.1
1062 *
1063 * @param string $string Text that might have accent characters
1064 * @return string Filtered string with replaced "nice" characters.
1065 */
1066function remove_accents( $string ) {
1067	if ( !preg_match('/[\x80-\xff]/', $string) )
1068		return $string;
1069
1070	if (seems_utf8($string)) {
1071		$chars = array(
1072		// Decompositions for Latin-1 Supplement
1073		chr(194).chr(170) => 'a', chr(194).chr(186) => 'o',
1074		chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
1075		chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
1076		chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
1077		chr(195).chr(134) => 'AE',chr(195).chr(135) => 'C',
1078		chr(195).chr(136) => 'E', chr(195).chr(137) => 'E',
1079		chr(195).chr(138) => 'E', chr(195).chr(139) => 'E',
1080		chr(195).chr(140) => 'I', chr(195).chr(141) => 'I',
1081		chr(195).chr(142) => 'I', chr(195).chr(143) => 'I',
1082		chr(195).chr(144) => 'D', chr(195).chr(145) => 'N',
1083		chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
1084		chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
1085		chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
1086		chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
1087		chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
1088		chr(195).chr(158) => 'TH',chr(195).chr(159) => 's',
1089		chr(195).chr(160) => 'a', chr(195).chr(161) => 'a',
1090		chr(195).chr(162) => 'a', chr(195).chr(163) => 'a',
1091		chr(195).chr(164) => 'a', chr(195).chr(165) => 'a',
1092		chr(195).chr(166) => 'ae',chr(195).chr(167) => 'c',
1093		chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
1094		chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
1095		chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
1096		chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
1097		chr(195).chr(176) => 'd', chr(195).chr(177) => 'n',
1098		chr(195).chr(178) => 'o', chr(195).chr(179) => 'o',
1099		chr(195).chr(180) => 'o', chr(195).chr(181) => 'o',
1100		chr(195).chr(182) => 'o', chr(195).chr(184) => 'o',
1101		chr(195).chr(185) => 'u', chr(195).chr(186) => 'u',
1102		chr(195).chr(187) => 'u', chr(195).chr(188) => 'u',
1103		chr(195).chr(189) => 'y', chr(195).chr(190) => 'th',
1104		chr(195).chr(191) => 'y', chr(195).chr(152) => 'O',
1105		// Decompositions for Latin Extended-A
1106		chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
1107		chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
1108		chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
1109		chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
1110		chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
1111		chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
1112		chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
1113		chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
1114		chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
1115		chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
1116		chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
1117		chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
1118		chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
1119		chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
1120		chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
1121		chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
1122		chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
1123		chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
1124		chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
1125		chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
1126		chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
1127		chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
1128		chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
1129		chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
1130		chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
1131		chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
1132		chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
1133		chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
1134		chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
1135		chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
1136		chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
1137		chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
1138		chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
1139		chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
1140		chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
1141		chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
1142		chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
1143		chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
1144		chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
1145		chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
1146		chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
1147		chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
1148		chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
1149		chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
1150		chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
1151		chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
1152		chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
1153		chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
1154		chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
1155		chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
1156		chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
1157		chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
1158		chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
1159		chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
1160		chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
1161		chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
1162		chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
1163		chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
1164		chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
1165		chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
1166		chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
1167		chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
1168		chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
1169		chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
1170		// Decompositions for Latin Extended-B
1171		chr(200).chr(152) => 'S', chr(200).chr(153) => 's',
1172		chr(200).chr(154) => 'T', chr(200).chr(155) => 't',
1173		// Euro Sign
1174		chr(226).chr(130).chr(172) => 'E',
1175		// GBP (Pound) Sign
1176		chr(194).chr(163) => '',
1177		// Vowels with diacritic (Vietnamese)
1178		// unmarked
1179		chr(198).chr(160) => 'O', chr(198).chr(161) => 'o',
1180		chr(198).chr(175) => 'U', chr(198).chr(176) => 'u',
1181		// grave accent
1182		chr(225).chr(186).chr(166) => 'A', chr(225).chr(186).chr(167) => 'a',
1183		chr(225).chr(186).chr(176) => 'A', chr(225).chr(186).chr(177) => 'a',
1184		chr(225).chr(187).chr(128) => 'E', chr(225).chr(187).chr(129) => 'e',
1185		chr(225).chr(187).chr(146) => 'O', chr(225).chr(187).chr(147) => 'o',
1186		chr(225).chr(187).chr(156) => 'O', chr(225).chr(187).chr(157) => 'o',
1187		chr(225).chr(187).chr(170) => 'U', chr(225).chr(187).chr(171) => 'u',
1188		chr(225).chr(187).chr(178) => 'Y', chr(225).chr(187).chr(179) => 'y',
1189		// hook
1190		chr(225).chr(186).chr(162) => 'A', chr(225).chr(186).chr(163) => 'a',
1191		chr(225).chr(186).chr(168) => 'A', chr(225).chr(186).chr(169) => 'a',
1192		chr(225).chr(186).chr(178) => 'A', chr(225).chr(186).chr(179) => 'a',
1193		chr(225).chr(186).chr(186) => 'E', chr(225).chr(186).chr(187) => 'e',
1194		chr(225).chr(187).chr(130) => 'E', chr(225).chr(187).chr(131) => 'e',
1195		chr(225).chr(187).chr(136) => 'I', chr(225).chr(187).chr(137) => 'i',
1196		chr(225).chr(187).chr(142) => 'O', chr(225).chr(187).chr(143) => 'o',
1197		chr(225).chr(187).chr(148) => 'O', chr(225).chr(187).chr(149) => 'o',
1198		chr(225).chr(187).chr(158) => 'O', chr(225).chr(187).chr(159) => 'o',
1199		chr(225).chr(187).chr(166) => 'U', chr(225).chr(187).chr(167) => 'u',
1200		chr(225).chr(187).chr(172) => 'U', chr(225).chr(187).chr(173) => 'u',
1201		chr(225).chr(187).chr(182) => 'Y', chr(225).chr(187).chr(183) => 'y',
1202		// tilde
1203		chr(225).chr(186).chr(170) => 'A', chr(225).chr(186).chr(171) => 'a',
1204		chr(225).chr(186).chr(180) => 'A', chr(225).chr(186).chr(181) => 'a',
1205		chr(225).chr(186).chr(188) => 'E', chr(225).chr(186).chr(189) => 'e',
1206		chr(225).chr(187).chr(132) => 'E', chr(225).chr(187).chr(133) => 'e',
1207		chr(225).chr(187).chr(150) => 'O', chr(225).chr(187).chr(151) => 'o',
1208		chr(225).chr(187).chr(160) => 'O', chr(225).chr(187).chr(161) => 'o',
1209		chr(225).chr(187).chr(174) => 'U', chr(225).chr(187).chr(175) => 'u',
1210		chr(225).chr(187).chr(184) => 'Y', chr(225).chr(187).chr(185) => 'y',
1211		// acute accent
1212		chr(225).chr(186).chr(164) => 'A', chr(225).chr(186).chr(165) => 'a',
1213		chr(225).chr(186).chr(174) => 'A', chr(225).chr(186).chr(175) => 'a',
1214		chr(225).chr(186).chr(190) => 'E', chr(225).chr(186).chr(191) => 'e',
1215		chr(225).chr(187).chr(144) => 'O', chr(225).chr(187).chr(145) => 'o',
1216		chr(225).chr(187).chr(154) => 'O', chr(225).chr(187).chr(155) => 'o',
1217		chr(225).chr(187).chr(168) => 'U', chr(225).chr(187).chr(169) => 'u',
1218		// dot below
1219		chr(225).chr(186).chr(160) => 'A', chr(225).chr(186).chr(161) => 'a',
1220		chr(225).chr(186).chr(172) => 'A', chr(225).chr(186).chr(173) => 'a',
1221		chr(225).chr(186).chr(182) => 'A', chr(225).chr(186).chr(183) => 'a',
1222		chr(225).chr(186).chr(184) => 'E', chr(225).chr(186).chr(185) => 'e',
1223		chr(225).chr(187).chr(134) => 'E', chr(225).chr(187).chr(135) => 'e',
1224		chr(225).chr(187).chr(138) => 'I', chr(225).chr(187).chr(139) => 'i',
1225		chr(225).chr(187).chr(140) => 'O', chr(225).chr(187).chr(141) => 'o',
1226		chr(225).chr(187).chr(152) => 'O', chr(225).chr(187).chr(153) => 'o',
1227		chr(225).chr(187).chr(162) => 'O', chr(225).chr(187).chr(163) => 'o',
1228		chr(225).chr(187).chr(164) => 'U', chr(225).chr(187).chr(165) => 'u',
1229		chr(225).chr(187).chr(176) => 'U', chr(225).chr(187).chr(177) => 'u',
1230		chr(225).chr(187).chr(180) => 'Y', chr(225).chr(187).chr(181) => 'y',
1231		// Vowels with diacritic (Chinese, Hanyu Pinyin)
1232		chr(201).chr(145) => 'a',
1233		// macron
1234		chr(199).chr(149) => 'U', chr(199).chr(150) => 'u',
1235		// acute accent
1236		chr(199).chr(151) => 'U', chr(199).chr(152) => 'u',
1237		// caron
1238		chr(199).chr(141) => 'A', chr(199).chr(142) => 'a',
1239		chr(199).chr(143) => 'I', chr(199).chr(144) => 'i',
1240		chr(199).chr(145) => 'O', chr(199).chr(146) => 'o',
1241		chr(199).chr(147) => 'U', chr(199).chr(148) => 'u',
1242		chr(199).chr(153) => 'U', chr(199).chr(154) => 'u',
1243		// grave accent
1244		chr(199).chr(155) => 'U', chr(199).chr(156) => 'u',
1245		);
1246
1247		// Used for locale-specific rules
1248		$locale = get_locale();
1249
1250		if ( 'de_DE' == $locale || 'de_DE_formal' == $locale ) {
1251			$chars[ chr(195).chr(132) ] = 'Ae';
1252			$chars[ chr(195).chr(164) ] = 'ae';
1253			$chars[ chr(195).chr(150) ] = 'Oe';
1254			$chars[ chr(195).chr(182) ] = 'oe';
1255			$chars[ chr(195).chr(156) ] = 'Ue';
1256			$chars[ chr(195).chr(188) ] = 'ue';
1257			$chars[ chr(195).chr(159) ] = 'ss';
1258		} elseif ( 'da_DK' === $locale ) {
1259			$chars[ chr(195).chr(134) ] = 'Ae';
1260 			$chars[ chr(195).chr(166) ] = 'ae';
1261			$chars[ chr(195).chr(152) ] = 'Oe';
1262			$chars[ chr(195).chr(184) ] = 'oe';
1263			$chars[ chr(195).chr(133) ] = 'Aa';
1264			$chars[ chr(195).chr(165) ] = 'aa';
1265		}
1266
1267		$string = strtr($string, $chars);
1268	} else {
1269		$chars = array();
1270		// Assume ISO-8859-1 if not UTF-8
1271		$chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
1272			.chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
1273			.chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
1274			.chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
1275			.chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
1276			.chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
1277			.chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
1278			.chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
1279			.chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
1280			.chr(252).chr(253).chr(255);
1281
1282		$chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
1283
1284		$string = strtr($string, $chars['in'], $chars['out']);
1285		$double_chars = array();
1286		$double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
1287		$double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
1288		$string = str_replace($double_chars['in'], $double_chars['out'], $string);
1289	}
1290
1291	return $string;
1292}
1293
1294/**
1295 * Sanitizes a filename, replacing whitespace with dashes.
1296 *
1297 * Remo…

Large files files are truncated, but you can click here to view the full file