* 'cause today's effort makes it worth tomorrow's "holiday"...
*
* Becomes:
*
* ’cause today’s effort makes it worth tomorrow’s “holiday”…
*
* Code within certain html blocks are skipped.
*
* @since 0.71
* @uses $wp_cockneyreplace Array of formatted entities for certain common phrases
*
* @param string $text The text to be formatted
* @return string The string replaced with html entities
*/
function wptexturize($text) {
global $wp_cockneyreplace;
static $static_characters, $static_replacements, $dynamic_characters, $dynamic_replacements,
$default_no_texturize_tags, $default_no_texturize_shortcodes;
// No need to set up these static variables more than once
if ( ! isset( $static_characters ) ) {
/* translators: opening curly double quote */
$opening_quote = _x( '“', 'opening curly double quote' );
/* translators: closing curly double quote */
$closing_quote = _x( '”', 'closing curly double quote' );
/* translators: apostrophe, for example in 'cause or can't */
$apos = _x( '’', 'apostrophe' );
/* translators: prime, for example in 9' (nine feet) */
$prime = _x( '′', 'prime' );
/* translators: double prime, for example in 9" (nine inches) */
$double_prime = _x( '″', 'double prime' );
/* translators: opening curly single quote */
$opening_single_quote = _x( '‘', 'opening curly single quote' );
/* translators: closing curly single quote */
$closing_single_quote = _x( '’', 'closing curly single quote' );
/* translators: en dash */
$en_dash = _x( '–', 'en dash' );
/* translators: em dash */
$em_dash = _x( '—', 'em dash' );
$default_no_texturize_tags = array('pre', 'code', 'kbd', 'style', 'script', 'tt');
$default_no_texturize_shortcodes = array('code');
// if a plugin has provided an autocorrect array, use it
if ( isset($wp_cockneyreplace) ) {
$cockney = array_keys($wp_cockneyreplace);
$cockneyreplace = array_values($wp_cockneyreplace);
} elseif ( "'" != $apos ) { // Only bother if we're doing a replacement.
$cockney = array( "'tain't", "'twere", "'twas", "'tis", "'twill", "'til", "'bout", "'nuff", "'round", "'cause" );
$cockneyreplace = array( $apos . "tain" . $apos . "t", $apos . "twere", $apos . "twas", $apos . "tis", $apos . "twill", $apos . "til", $apos . "bout", $apos . "nuff", $apos . "round", $apos . "cause" );
} else {
$cockney = $cockneyreplace = array();
}
$static_characters = array_merge( array( '---', ' -- ', '--', ' - ', 'xn–', '...', '``', '\'\'', ' (tm)' ), $cockney );
$static_replacements = array_merge( array( $em_dash, ' ' . $em_dash . ' ', $en_dash, ' ' . $en_dash . ' ', 'xn--', '…', $opening_quote, $closing_quote, ' ™' ), $cockneyreplace );
$dynamic = array();
if ( "'" != $apos ) {
$dynamic[ '/\'(\d\d(?:’|\')?s)/' ] = $apos . '$1'; // '99's
$dynamic[ '/\'(\d)/' ] = $apos . '$1'; // '99
}
if ( "'" != $opening_single_quote )
$dynamic[ '/(\s|\A|[([{<]|")\'/' ] = '$1' . $opening_single_quote; // opening single quote, even after (, {, <, [
if ( '"' != $double_prime )
$dynamic[ '/(\d)"/' ] = '$1' . $double_prime; // 9" (double prime)
if ( "'" != $prime )
$dynamic[ '/(\d)\'/' ] = '$1' . $prime; // 9' (prime)
if ( "'" != $apos )
$dynamic[ '/(\S)\'([^\'\s])/' ] = '$1' . $apos . '$2'; // apostrophe in a word
if ( '"' != $opening_quote )
$dynamic[ '/(\s|\A|[([{<])"(?!\s)/' ] = '$1' . $opening_quote . '$2'; // opening double quote, even after (, {, <, [
if ( '"' != $closing_quote )
$dynamic[ '/"(\s|\S|\Z)/' ] = $closing_quote . '$1'; // closing double quote
if ( "'" != $closing_single_quote )
$dynamic[ '/\'([\s.]|\Z)/' ] = $closing_single_quote . '$1'; // closing single quote
$dynamic[ '/\b(\d+)x(\d+)\b/' ] = '$1×$2'; // 9x9 (times)
$dynamic_characters = array_keys( $dynamic );
$dynamic_replacements = array_values( $dynamic );
}
// Transform into regexp sub-expression used in _wptexturize_pushpop_element
// Must do this every time in case plugins use these filters in a context sensitive manner
$no_texturize_tags = '(' . implode('|', apply_filters('no_texturize_tags', $default_no_texturize_tags) ) . ')';
$no_texturize_shortcodes = '(' . implode('|', apply_filters('no_texturize_shortcodes', $default_no_texturize_shortcodes) ) . ')';
$no_texturize_tags_stack = array();
$no_texturize_shortcodes_stack = array();
// Look for shortcodes and HTML elements.
$shortcode_regex =
'\[' // Find start of shortcode.
. '[^\[\]<>]++' // Shortcodes do not contain other shortcodes. Possessive critical.
. '\]'; // Find end of shortcode.
$textarr = preg_split("/(<[^>]*>|$shortcode_regex)/s", $text, -1, PREG_SPLIT_DELIM_CAPTURE);
foreach ( $textarr as &$curl ) {
if ( empty( $curl ) )
continue;
// Only call _wptexturize_pushpop_element if first char is correct tag opening
$first = $curl[0];
if ( '<' === $first ) {
_wptexturize_pushpop_element($curl, $no_texturize_tags_stack, $no_texturize_tags, '<', '>');
} elseif ( '[' === $first && 1 === preg_match( '/^' . $shortcode_regex . '$/', $curl ) ) {
_wptexturize_pushpop_element($curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes, '[', ']');
} elseif ( empty($no_texturize_shortcodes_stack) && empty($no_texturize_tags_stack) ) {
// This is not a tag, nor is the texturization disabled static strings
$curl = str_replace($static_characters, $static_replacements, $curl);
// regular expressions
$curl = preg_replace($dynamic_characters, $dynamic_replacements, $curl);
}
$curl = preg_replace('/&([^#])(?![a-zA-Z1-4]{1,8};)/', '&$1', $curl);
}
return implode( '', $textarr );
}
/**
* Search for disabled element tags. Push element to stack on tag open and pop
* on tag close. Assumes first character of $text is tag opening.
*
* @since 2.9.0
* @access private
*
* @param string $text Text to check. First character is assumed to be $opening
* @param array $stack Array used as stack of opened tag elements
* @param string $disabled_elements Tags to match against formatted as regexp sub-expression
* @param string $opening Tag opening character, assumed to be 1 character long
* @param string $closing Tag closing character
*/
function _wptexturize_pushpop_element($text, &$stack, $disabled_elements, $opening = '<', $closing = '>') {
// Check if it is a closing tag -- otherwise assume opening tag
if (strncmp($opening . '/', $text, 2)) {
// Opening? Check $text+1 against disabled elements
if (preg_match('/^' . $disabled_elements . '\b/', substr($text, 1), $matches)) {
/*
* This disables texturize until we find a closing tag of our type
* (e.g.
) even if there was invalid nesting before that
*
* Example: in the case sadsadasd"baba"
* "baba" won't be texturize
*/
array_push($stack, $matches[1]);
}
} elseif ( 0 == count( $stack ) ) {
// Stack is empty. Just stop.
} else {
// Closing? Check $text+2 against disabled elements
$c = preg_quote($closing, '/');
if (preg_match('/^' . $disabled_elements . $c . '/', substr($text, 2), $matches)) {
$last = array_pop($stack);
// Make sure it matches the opening tag
if ($last != $matches[1])
array_push($stack, $last);
}
}
}
/**
* Replaces double line-breaks with paragraph elements.
*
* A group of regex replaces used to identify text formatted with newlines and
* replace double line-breaks with HTML paragraph tags. The remaining
* line-breaks after conversion become < > tags, unless $br is set to '0'
* or 'false'.
*
* @since 0.71
*
* @param string $pee The text which has to be formatted.
* @param bool $br Optional. If set, this will convert all remaining line-breaks after paragraphing. Default true.
* @return string Text which has been converted into correct paragraph tags.
*/
function wpautop($pee, $br = true) {
$pre_tags = array();
if ( trim($pee) === '' )
return '';
$pee = $pee . "\n"; // just to make things a little easier, pad the end
if ( strpos($pee, '', $pee );
$last_pee = array_pop($pee_parts);
$pee = '';
$i = 0;
foreach ( $pee_parts as $pee_part ) {
$start = strpos($pee_part, ' ";
$pre_tags[$name] = substr( $pee_part, $start ) . ' ';
$pee .= substr( $pee_part, 0, $start ) . $name;
$i++;
}
$pee .= $last_pee;
}
$pee = preg_replace('| \s* |', "\n\n", $pee);
// Space things out a little
$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|p|h[1-6]|hr|fieldset|noscript|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)';
$pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
$pee = preg_replace('!(' . $allblocks . '>)!', "$1\n\n", $pee);
$pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
// Strip newlines from all elements.
$pee = wp_replace_in_html_tags( $pee, array( "\n" => " " ) );
if ( strpos($pee, ']*)>\s*|', " ", $pee); // no pee inside object/embed
$pee = preg_replace('|\s*\s*|', '', $pee);
}
$pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates
// make paragraphs, including one at the end
$pees = preg_split('/\n\s*\n/', $pee, -1, PREG_SPLIT_NO_EMPTY);
$pee = '';
foreach ( $pees as $tinkle )
$pee .= '' . trim($tinkle, "\n") . "
\n";
$pee = preg_replace('|\s*
|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace
$pee = preg_replace('!([^<]+)(div|address|form)>!', "
$1
$2>", $pee);
$pee = preg_replace('!\s*(?' . $allblocks . '[^>]*>)\s*
!', "$1", $pee); // don't pee all over a tag
$pee = preg_replace("|(
|", "$1", $pee); // problem with nested lists
$pee = preg_replace('|
]*)>|i', "", $pee);
$pee = str_replace('
', ' ', $pee);
$pee = preg_replace('!\s*(?' . $allblocks . '[^>]*>)!', "$1", $pee);
$pee = preg_replace('!(?' . $allblocks . '[^>]*>)\s*
!', "$1", $pee);
if ( $br ) {
$pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', '_autop_newline_preservation_helper', $pee);
$pee = preg_replace('|(?)\s*\n|', " \n", $pee); // optionally make line breaks
$pee = str_replace(' ', "\n", $pee);
}
$pee = preg_replace('!(?' . $allblocks . '[^>]*>)\s* !', "$1", $pee);
$pee = preg_replace('! (\s*?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
$pee = preg_replace( "|\n$|", '', $pee );
if ( !empty($pre_tags) )
$pee = str_replace(array_keys($pre_tags), array_values($pre_tags), $pee);
return $pee;
}
/**
* Replace characters or phrases within HTML elements only.
*
* @since 4.2.3
*
* @param string $haystack The text which has to be formatted.
* @param array $replace_pairs In the form array('from' => 'to', ...).
* @return string The formatted text.
*/
function wp_replace_in_html_tags( $haystack, $replace_pairs ) {
// Find all elements.
$comments =
'!' // Start of comment, after the <.
. '(?:' // Unroll the loop: Consume everything until --> is found.
. '-(?!->)' // Dash not followed by end of comment.
. '[^\-]*+' // Consume non-dashes.
. ')*+' // Loop possessively.
. '(?:-->)?'; // End of comment. If not found, match all input.
$regex =
'/(' // Capture the entire match.
. '<' // Find start of element.
. '(?(?=!--)' // Is this a comment?
. $comments // Find end of comment.
. '|'
. '[^>]*>?' // Find end of element. If not found, match all input.
. ')'
. ')/s';
$textarr = preg_split( $regex, $haystack, -1, PREG_SPLIT_DELIM_CAPTURE );
$changed = false;
// Optimize when searching for one item.
if ( 1 === count( $replace_pairs ) ) {
// Extract $needle and $replace.
foreach ( $replace_pairs as $needle => $replace );
// Loop through delimeters (elements) only.
for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
if ( false !== strpos( $textarr[$i], $needle ) ) {
$textarr[$i] = str_replace( $needle, $replace, $textarr[$i] );
$changed = true;
}
}
} else {
// Extract all $needles.
$needles = array_keys( $replace_pairs );
// Loop through delimeters (elements) only.
for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
foreach ( $needles as $needle ) {
if ( false !== strpos( $textarr[$i], $needle ) ) {
$textarr[$i] = strtr( $textarr[$i], $replace_pairs );
$changed = true;
// After one strtr() break out of the foreach loop and look at next element.
break;
}
}
}
}
if ( $changed ) {
$haystack = implode( $textarr );
}
return $haystack;
}
/**
* Newline preservation help function for wpautop
*
* @since 3.1.0
* @access private
*
* @param array $matches preg_replace_callback matches array
* @return string
*/
function _autop_newline_preservation_helper( $matches ) {
return str_replace("\n", " ", $matches[0]);
}
/**
* Don't auto-p wrap shortcodes that stand alone
*
* Ensures that shortcodes are not wrapped in <>...<
>.
*
* @since 2.9.0
*
* @param string $pee The content.
* @return string The filtered content.
*/
function shortcode_unautop( $pee ) {
global $shortcode_tags;
if ( empty( $shortcode_tags ) || !is_array( $shortcode_tags ) ) {
return $pee;
}
$tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) );
$pattern =
'/'
. '' // Opening paragraph
. '\\s*+' // Optional leading whitespace
. '(' // 1: The shortcode
. '\\[' // Opening bracket
. "($tagregexp)" // 2: Shortcode name
. '(?![\\w-])' // Not followed by word character or hyphen
// Unroll the loop: Inside the opening shortcode tag
. '[^\\]\\/]*' // Not a closing bracket or forward slash
. '(?:'
. '\\/(?!\\])' // A forward slash not followed by a closing bracket
. '[^\\]\\/]*' // Not a closing bracket or forward slash
. ')*?'
. '(?:'
. '\\/\\]' // Self closing tag and closing bracket
. '|'
. '\\]' // Closing bracket
. '(?:' // Unroll the loop: Optionally, anything between the opening and closing shortcode tags
. '[^\\[]*+' // Not an opening bracket
. '(?:'
. '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag
. '[^\\[]*+' // Not an opening bracket
. ')*+'
. '\\[\\/\\2\\]' // Closing shortcode tag
. ')?'
. ')'
. ')'
. '\\s*+' // optional trailing whitespace
. '<\\/p>' // closing paragraph
. '/s';
return preg_replace( $pattern, '$1', $pee );
}
/**
* Checks to see if a string is utf8 encoded.
*
* NOTE: This function checks for 5-Byte sequences, UTF8
* has Bytes Sequences with a maximum length of 4.
*
* @author bmorel at ssi dot fr (modified)
* @since 1.2.1
*
* @param string $str The string to be checked
* @return bool True if $str fits a UTF-8 model, false otherwise.
*/
function seems_utf8($str) {
$length = strlen($str);
for ($i=0; $i < $length; $i++) {
$c = ord($str[$i]);
if ($c < 0x80) $n = 0; # 0bbbbbbb
elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
else return false; # Does not match any model
for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
return false;
}
}
return true;
}
/**
* Converts a number of special characters into their HTML entities.
*
* Specifically deals with: &, <, >, ", and '.
*
* $quote_style can be set to ENT_COMPAT to encode " to
* ", or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded.
*
* @since 1.2.2
* @access private
*
* @param string $string The text which is to be encoded.
* @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES.
* @param string $charset Optional. The character encoding of the string. Default is false.
* @param boolean $double_encode Optional. Whether to encode existing html entities. Default is false.
* @return string The encoded text with HTML entities.
*/
function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) {
$string = (string) $string;
if ( 0 === strlen( $string ) )
return '';
// Don't bother if there are no specialchars - saves some processing
if ( ! preg_match( '/[&<>"\']/', $string ) )
return $string;
// Account for the previous behaviour of the function when the $quote_style is not an accepted value
if ( empty( $quote_style ) )
$quote_style = ENT_NOQUOTES;
elseif ( ! in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) )
$quote_style = ENT_QUOTES;
// Store the site charset as a static to avoid multiple calls to wp_load_alloptions()
if ( ! $charset ) {
static $_charset;
if ( ! isset( $_charset ) ) {
$alloptions = wp_load_alloptions();
$_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : '';
}
$charset = $_charset;
}
if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) )
$charset = 'UTF-8';
$_quote_style = $quote_style;
if ( $quote_style === 'double' ) {
$quote_style = ENT_COMPAT;
$_quote_style = ENT_COMPAT;
} elseif ( $quote_style === 'single' ) {
$quote_style = ENT_NOQUOTES;
}
// Handle double encoding ourselves
if ( $double_encode ) {
$string = @htmlspecialchars( $string, $quote_style, $charset );
} else {
// Decode & into &
$string = wp_specialchars_decode( $string, $_quote_style );
// Guarantee every &entity; is valid or re-encode the &
$string = wp_kses_normalize_entities( $string );
// Now re-encode everything except &entity;
$string = preg_split( '/(?x?[0-9a-z]+;)/i', $string, -1, PREG_SPLIT_DELIM_CAPTURE );
for ( $i = 0; $i < count( $string ); $i += 2 )
$string[$i] = @htmlspecialchars( $string[$i], $quote_style, $charset );
$string = implode( '', $string );
}
// Backwards compatibility
if ( 'single' === $_quote_style )
$string = str_replace( "'", ''', $string );
return $string;
}
/**
* Converts a number of HTML entities into their special characters.
*
* Specifically deals with: &, <, >, ", and '.
*
* $quote_style can be set to ENT_COMPAT to decode " entities,
* or ENT_QUOTES to do both " and '. Default is ENT_NOQUOTES where no quotes are decoded.
*
* @since 2.8.0
*
* @param string $string The text which is to be decoded.
* @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old _wp_specialchars() values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES.
* @return string The decoded text without HTML entities.
*/
function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) {
$string = (string) $string;
if ( 0 === strlen( $string ) ) {
return '';
}
// Don't bother if there are no entities - saves a lot of processing
if ( strpos( $string, '&' ) === false ) {
return $string;
}
// Match the previous behaviour of _wp_specialchars() when the $quote_style is not an accepted value
if ( empty( $quote_style ) ) {
$quote_style = ENT_NOQUOTES;
} elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
$quote_style = ENT_QUOTES;
}
// More complete than get_html_translation_table( HTML_SPECIALCHARS )
$single = array( ''' => '\'', ''' => '\'' );
$single_preg = array( '/*39;/' => ''', '/*27;/i' => ''' );
$double = array( '"' => '"', '"' => '"', '"' => '"' );
$double_preg = array( '/*34;/' => '"', '/*22;/i' => '"' );
$others = array( '<' => '<', '<' => '<', '>' => '>', '>' => '>', '&' => '&', '&' => '&', '&' => '&' );
$others_preg = array( '/*60;/' => '<', '/*62;/' => '>', '/*38;/' => '&', '/*26;/i' => '&' );
if ( $quote_style === ENT_QUOTES ) {
$translation = array_merge( $single, $double, $others );
$translation_preg = array_merge( $single_preg, $double_preg, $others_preg );
} elseif ( $quote_style === ENT_COMPAT || $quote_style === 'double' ) {
$translation = array_merge( $double, $others );
$translation_preg = array_merge( $double_preg, $others_preg );
} elseif ( $quote_style === 'single' ) {
$translation = array_merge( $single, $others );
$translation_preg = array_merge( $single_preg, $others_preg );
} elseif ( $quote_style === ENT_NOQUOTES ) {
$translation = $others;
$translation_preg = $others_preg;
}
// Remove zero padding on numeric entities
$string = preg_replace( array_keys( $translation_preg ), array_values( $translation_preg ), $string );
// Replace characters according to translation table
return strtr( $string, $translation );
}
/**
* Checks for invalid UTF8 in a string.
*
* @since 2.8.0
*
* @param string $string The text which is to be checked.
* @param boolean $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false.
* @return string The checked text.
*/
function wp_check_invalid_utf8( $string, $strip = false ) {
$string = (string) $string;
if ( 0 === strlen( $string ) ) {
return '';
}
// Store the site charset as a static to avoid multiple calls to get_option()
static $is_utf8;
if ( !isset( $is_utf8 ) ) {
$is_utf8 = in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) );
}
if ( !$is_utf8 ) {
return $string;
}
// Check for support for utf8 in the installed PCRE library once and store the result in a static
static $utf8_pcre;
if ( !isset( $utf8_pcre ) ) {
$utf8_pcre = @preg_match( '/^./u', 'a' );
}
// We can't demand utf8 in the PCRE installation, so just return the string in those cases
if ( !$utf8_pcre ) {
return $string;
}
// preg_match fails when it encounters invalid UTF8 in $string
if ( 1 === @preg_match( '/^./us', $string ) ) {
return $string;
}
// Attempt to strip the bad chars if requested (not recommended)
if ( $strip && function_exists( 'iconv' ) ) {
return iconv( 'utf-8', 'utf-8', $string );
}
return '';
}
/**
* Encode the Unicode values to be used in the URI.
*
* @since 1.5.0
*
* @param string $utf8_string
* @param int $length Max length of the string
* @return string String with Unicode encoded for URI.
*/
function utf8_uri_encode( $utf8_string, $length = 0 ) {
$unicode = '';
$values = array();
$num_octets = 1;
$unicode_length = 0;
$string_length = strlen( $utf8_string );
for ($i = 0; $i < $string_length; $i++ ) {
$value = ord( $utf8_string[ $i ] );
if ( $value < 128 ) {
if ( $length && ( $unicode_length >= $length ) )
break;
$unicode .= chr($value);
$unicode_length++;
} else {
if ( count( $values ) == 0 ) $num_octets = ( $value < 224 ) ? 2 : 3;
$values[] = $value;
if ( $length && ( $unicode_length + ($num_octets * 3) ) > $length )
break;
if ( count( $values ) == $num_octets ) {
if ($num_octets == 3) {
$unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]) . '%' . dechex($values[2]);
$unicode_length += 9;
} else {
$unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]);
$unicode_length += 6;
}
$values = array();
$num_octets = 1;
}
}
}
return $unicode;
}
/**
* Converts all accent characters to ASCII characters.
*
* If there are no accent characters, then the string given is just returned.
*
* @since 1.2.1
*
* @param string $string Text that might have accent characters
* @return string Filtered string with replaced "nice" characters.
*/
function remove_accents($string) {
if ( !preg_match('/[\x80-\xff]/', $string) )
return $string;
if (seems_utf8($string)) {
$chars = array(
// Decompositions for Latin-1 Supplement
chr(194).chr(170) => 'a', chr(194).chr(186) => 'o',
chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
chr(195).chr(134) => 'AE',chr(195).chr(135) => 'C',
chr(195).chr(136) => 'E', chr(195).chr(137) => 'E',
chr(195).chr(138) => 'E', chr(195).chr(139) => 'E',
chr(195).chr(140) => 'I', chr(195).chr(141) => 'I',
chr(195).chr(142) => 'I', chr(195).chr(143) => 'I',
chr(195).chr(144) => 'D', chr(195).chr(145) => 'N',
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
chr(195).chr(158) => 'TH',chr(195).chr(159) => 's',
chr(195).chr(160) => 'a', chr(195).chr(161) => 'a',
chr(195).chr(162) => 'a', chr(195).chr(163) => 'a',
chr(195).chr(164) => 'a', chr(195).chr(165) => 'a',
chr(195).chr(166) => 'ae',chr(195).chr(167) => 'c',
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
chr(195).chr(176) => 'd', chr(195).chr(177) => 'n',
chr(195).chr(178) => 'o', chr(195).chr(179) => 'o',
chr(195).chr(180) => 'o', chr(195).chr(181) => 'o',
chr(195).chr(182) => 'o', chr(195).chr(184) => 'o',
chr(195).chr(185) => 'u', chr(195).chr(186) => 'u',
chr(195).chr(187) => 'u', chr(195).chr(188) => 'u',
chr(195).chr(189) => 'y', chr(195).chr(190) => 'th',
chr(195).chr(191) => 'y', chr(195).chr(152) => 'O',
// Decompositions for Latin Extended-A
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
// Decompositions for Latin Extended-B
chr(200).chr(152) => 'S', chr(200).chr(153) => 's',
chr(200).chr(154) => 'T', chr(200).chr(155) => 't',
// Euro Sign
chr(226).chr(130).chr(172) => 'E',
// GBP (Pound) Sign
chr(194).chr(163) => '',
// Vowels with diacritic (Vietnamese)
// unmarked
chr(198).chr(160) => 'O', chr(198).chr(161) => 'o',
chr(198).chr(175) => 'U', chr(198).chr(176) => 'u',
// grave accent
chr(225).chr(186).chr(166) => 'A', chr(225).chr(186).chr(167) => 'a',
chr(225).chr(186).chr(176) => 'A', chr(225).chr(186).chr(177) => 'a',
chr(225).chr(187).chr(128) => 'E', chr(225).chr(187).chr(129) => 'e',
chr(225).chr(187).chr(146) => 'O', chr(225).chr(187).chr(147) => 'o',
chr(225).chr(187).chr(156) => 'O', chr(225).chr(187).chr(157) => 'o',
chr(225).chr(187).chr(170) => 'U', chr(225).chr(187).chr(171) => 'u',
chr(225).chr(187).chr(178) => 'Y', chr(225).chr(187).chr(179) => 'y',
// hook
chr(225).chr(186).chr(162) => 'A', chr(225).chr(186).chr(163) => 'a',
chr(225).chr(186).chr(168) => 'A', chr(225).chr(186).chr(169) => 'a',
chr(225).chr(186).chr(178) => 'A', chr(225).chr(186).chr(179) => 'a',
chr(225).chr(186).chr(186) => 'E', chr(225).chr(186).chr(187) => 'e',
chr(225).chr(187).chr(130) => 'E', chr(225).chr(187).chr(131) => 'e',
chr(225).chr(187).chr(136) => 'I', chr(225).chr(187).chr(137) => 'i',
chr(225).chr(187).chr(142) => 'O', chr(225).chr(187).chr(143) => 'o',
chr(225).chr(187).chr(148) => 'O', chr(225).chr(187).chr(149) => 'o',
chr(225).chr(187).chr(158) => 'O', chr(225).chr(187).chr(159) => 'o',
chr(225).chr(187).chr(166) => 'U', chr(225).chr(187).chr(167) => 'u',
chr(225).chr(187).chr(172) => 'U', chr(225).chr(187).chr(173) => 'u',
chr(225).chr(187).chr(182) => 'Y', chr(225).chr(187).chr(183) => 'y',
// tilde
chr(225).chr(186).chr(170) => 'A', chr(225).chr(186).chr(171) => 'a',
chr(225).chr(186).chr(180) => 'A', chr(225).chr(186).chr(181) => 'a',
chr(225).chr(186).chr(188) => 'E', chr(225).chr(186).chr(189) => 'e',
chr(225).chr(187).chr(132) => 'E', chr(225).chr(187).chr(133) => 'e',
chr(225).chr(187).chr(150) => 'O', chr(225).chr(187).chr(151) => 'o',
chr(225).chr(187).chr(160) => 'O', chr(225).chr(187).chr(161) => 'o',
chr(225).chr(187).chr(174) => 'U', chr(225).chr(187).chr(175) => 'u',
chr(225).chr(187).chr(184) => 'Y', chr(225).chr(187).chr(185) => 'y',
// acute accent
chr(225).chr(186).chr(164) => 'A', chr(225).chr(186).chr(165) => 'a',
chr(225).chr(186).chr(174) => 'A', chr(225).chr(186).chr(175) => 'a',
chr(225).chr(186).chr(190) => 'E', chr(225).chr(186).chr(191) => 'e',
chr(225).chr(187).chr(144) => 'O', chr(225).chr(187).chr(145) => 'o',
chr(225).chr(187).chr(154) => 'O', chr(225).chr(187).chr(155) => 'o',
chr(225).chr(187).chr(168) => 'U', chr(225).chr(187).chr(169) => 'u',
// dot below
chr(225).chr(186).chr(160) => 'A', chr(225).chr(186).chr(161) => 'a',
chr(225).chr(186).chr(172) => 'A', chr(225).chr(186).chr(173) => 'a',
chr(225).chr(186).chr(182) => 'A', chr(225).chr(186).chr(183) => 'a',
chr(225).chr(186).chr(184) => 'E', chr(225).chr(186).chr(185) => 'e',
chr(225).chr(187).chr(134) => 'E', chr(225).chr(187).chr(135) => 'e',
chr(225).chr(187).chr(138) => 'I', chr(225).chr(187).chr(139) => 'i',
chr(225).chr(187).chr(140) => 'O', chr(225).chr(187).chr(141) => 'o',
chr(225).chr(187).chr(152) => 'O', chr(225).chr(187).chr(153) => 'o',
chr(225).chr(187).chr(162) => 'O', chr(225).chr(187).chr(163) => 'o',
chr(225).chr(187).chr(164) => 'U', chr(225).chr(187).chr(165) => 'u',
chr(225).chr(187).chr(176) => 'U', chr(225).chr(187).chr(177) => 'u',
chr(225).chr(187).chr(180) => 'Y', chr(225).chr(187).chr(181) => 'y',
// Vowels with diacritic (Chinese, Hanyu Pinyin)
chr(201).chr(145) => 'a',
// macron
chr(199).chr(149) => 'U', chr(199).chr(150) => 'u',
// acute accent
chr(199).chr(151) => 'U', chr(199).chr(152) => 'u',
// caron
chr(199).chr(141) => 'A', chr(199).chr(142) => 'a',
chr(199).chr(143) => 'I', chr(199).chr(144) => 'i',
chr(199).chr(145) => 'O', chr(199).chr(146) => 'o',
chr(199).chr(147) => 'U', chr(199).chr(148) => 'u',
chr(199).chr(153) => 'U', chr(199).chr(154) => 'u',
// grave accent
chr(199).chr(155) => 'U', chr(199).chr(156) => 'u',
);
// Used for locale-specific rules
$locale = get_locale();
if ( 'de_DE' == $locale ) {
$chars[ chr(195).chr(132) ] = 'Ae';
$chars[ chr(195).chr(164) ] = 'ae';
$chars[ chr(195).chr(150) ] = 'Oe';
$chars[ chr(195).chr(182) ] = 'oe';
$chars[ chr(195).chr(156) ] = 'Ue';
$chars[ chr(195).chr(188) ] = 'ue';
$chars[ chr(195).chr(159) ] = 'ss';
}
$string = strtr($string, $chars);
} else {
// Assume ISO-8859-1 if not UTF-8
$chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
.chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
.chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
.chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
.chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
.chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
.chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
.chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
.chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
.chr(252).chr(253).chr(255);
$chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
$string = strtr($string, $chars['in'], $chars['out']);
$double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
$double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
$string = str_replace($double_chars['in'], $double_chars['out'], $string);
}
return $string;
}
/**
* Sanitizes a filename, replacing whitespace with dashes.
*
* Removes special characters that are illegal in filenames on certain
* operating systems and special characters requiring special escaping
* to manipulate at the command line. Replaces spaces and consecutive
* dashes with a single dash. Trims period, dash and underscore from beginning
* and end of filename.
*
* @since 2.1.0
*
* @param string $filename The filename to be sanitized
* @return string The sanitized filename
*/
function sanitize_file_name( $filename ) {
$filename_raw = $filename;
$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", chr(0));
$special_chars = apply_filters('sanitize_file_name_chars', $special_chars, $filename_raw);
$filename = str_replace($special_chars, '', $filename);
$filename = preg_replace('/[\s-]+/', '-', $filename);
$filename = trim($filename, '.-_');
// Split the filename into a base and extension[s]
$parts = explode('.', $filename);
// Return if only one extension
if ( count($parts) <= 2 )
return apply_filters('sanitize_file_name', $filename, $filename_raw);
// Process multiple extensions
$filename = array_shift($parts);
$extension = array_pop($parts);
$mimes = get_allowed_mime_types();
// Loop over any intermediate extensions. Munge them with a trailing underscore if they are a 2 - 5 character
// long alpha string not in the extension whitelist.
foreach ( (array) $parts as $part) {
$filename .= '.' . $part;
if ( preg_match("/^[a-zA-Z]{2,5}\d?$/", $part) ) {
$allowed = false;
foreach ( $mimes as $ext_preg => $mime_match ) {
$ext_preg = '!^(' . $ext_preg . ')$!i';
if ( preg_match( $ext_preg, $part ) ) {
$allowed = true;
break;
}
}
if ( !$allowed )
$filename .= '_';
}
}
$filename .= '.' . $extension;
return apply_filters('sanitize_file_name', $filename, $filename_raw);
}
/**
* Sanitizes a username, stripping out unsafe characters.
*
* Removes tags, octets, entities, and if strict is enabled, will only keep
* alphanumeric, _, space, ., -, @. After sanitizing, it passes the username,
* raw username (the username in the parameter), and the value of $strict as
* parameters for the 'sanitize_user' filter.
*
* @since 2.0.0
* @uses apply_filters() Calls 'sanitize_user' hook on username, raw username,
* and $strict parameter.
*
* @param string $username The username to be sanitized.
* @param bool $strict If set limits $username to specific characters. Default false.
* @return string The sanitized username, after passing through filters.
*/
function sanitize_user( $username, $strict = false ) {
$raw_username = $username;
$username = wp_strip_all_tags( $username );
$username = remove_accents( $username );
// Kill octets
$username = preg_replace( '|%([a-fA-F0-9][a-fA-F0-9])|', '', $username );
$username = preg_replace( '/&.+?;/', '', $username ); // Kill entities
// If strict, reduce to ASCII for max portability.
if ( $strict )
$username = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $username );
$username = trim( $username );
// Consolidate contiguous whitespace
$username = preg_replace( '|\s+|', ' ', $username );
return apply_filters( 'sanitize_user', $username, $raw_username, $strict );
}
/**
* Sanitizes a string key.
*
* Keys are used as internal identifiers. Lowercase alphanumeric characters, dashes and underscores are allowed.
*
* @since 3.0.0
*
* @param string $key String key
* @return string Sanitized key
*/
function sanitize_key( $key ) {
$raw_key = $key;
$key = strtolower( $key );
$key = preg_replace( '/[^a-z0-9_\-]/', '', $key );
return apply_filters( 'sanitize_key', $key, $raw_key );
}
/**
* Sanitizes a title, or returns a fallback title.
*
* Specifically, HTML and PHP tags are stripped. Further actions can be added
* via the plugin API. If $title is empty and $fallback_title is set, the latter
* will be used.
*
* @since 1.0.0
*
* @param string $title The string to be sanitized.
* @param string $fallback_title Optional. A title to use if $title is empty.
* @param string $context Optional. The operation for which the string is sanitized
* @return string The sanitized string.
*/
function sanitize_title( $title, $fallback_title = '', $context = 'save' ) {
$raw_title = $title;
if ( 'save' == $context )
$title = remove_accents($title);
$title = apply_filters('sanitize_title', $title, $raw_title, $context);
if ( '' === $title || false === $title )
$title = $fallback_title;
return $title;
}
/**
* Sanitizes a title with the 'query' context.
*
* Used for querying the database for a value from URL.
*
* @since 3.1.0
* @uses sanitize_title()
*
* @param string $title The string to be sanitized.
* @return string The sanitized string.
*/
function sanitize_title_for_query( $title ) {
return sanitize_title( $title, '', 'query' );
}
/**
* Sanitizes a title, replacing whitespace and a few other characters with dashes.
*
* Limits the output to alphanumeric characters, underscore (_) and dash (-).
* Whitespace becomes a dash.
*
* @since 1.2.0
*
* @param string $title The title to be sanitized.
* @param string $raw_title Optional. Not used.
* @param string $context Optional. The operation for which the string is sanitized.
* @return string The sanitized title.
*/
function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'display' ) {
$title = strip_tags($title);
// Preserve escaped octets.
$title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title);
// Remove percent signs that are not part of an octet.
$title = str_replace('%', '', $title);
// Restore octets.
$title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title);
if (seems_utf8($title)) {
if (function_exists('mb_strtolower')) {
$title = mb_strtolower($title, 'UTF-8');
}
$title = utf8_uri_encode($title, 200);
}
$title = strtolower($title);
$title = preg_replace('/&.+?;/', '', $title); // kill entities
$title = str_replace('.', '-', $title);
if ( 'save' == $context ) {
// Convert nbsp, ndash and mdash to hyphens
$title = str_replace( array( '%c2%a0', '%e2%80%93', '%e2%80%94' ), '-', $title );
// Strip these characters entirely
$title = str_replace( array(
// iexcl and iquest
'%c2%a1', '%c2%bf',
// angle quotes
'%c2%ab', '%c2%bb', '%e2%80%b9', '%e2%80%ba',
// curly quotes
'%e2%80%98', '%e2%80%99', '%e2%80%9c', '%e2%80%9d',
'%e2%80%9a', '%e2%80%9b', '%e2%80%9e', '%e2%80%9f',
// copy, reg, deg, hellip and trade
'%c2%a9', '%c2%ae', '%c2%b0', '%e2%80%a6', '%e2%84%a2',
// acute accents
'%c2%b4', '%cb%8a', '%cc%81', '%cd%81',
// grave accent, macron, caron
'%cc%80', '%cc%84', '%cc%8c',
), '', $title );
// Convert times to x
$title = str_replace( '%c3%97', 'x', $title );
}
$title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
$title = preg_replace('/\s+/', '-', $title);
$title = preg_replace('|-+|', '-', $title);
$title = trim($title, '-');
return $title;
}
/**
* Ensures a string is a valid SQL 'order by' clause.
*
* Accepts one or more columns, with or without a sort order (ASC / DESC).
* e.g. 'column_1', 'column_1, column_2', 'column_1 ASC, column_2 DESC' etc.
*
* Also accepts 'RAND()'.
*
* @since 2.5.1
*
* @param string $orderby Order by clause to be validated.
* @return string|bool Returns $orderby if valid, false otherwise.
*/
function sanitize_sql_orderby( $orderby ) {
if ( preg_match( '/^\s*(([a-z0-9_]+|`[a-z0-9_]+`)(\s+(ASC|DESC))?\s*(,\s*(?=[a-z0-9_`])|$))+$/i', $orderby ) || preg_match( '/^\s*RAND\(\s*\)\s*$/i', $orderby ) ) {
return $orderby;
}
return false;
}
/**
* Sanitizes an HTML classname to ensure it only contains valid characters.
*
* Strips the string down to A-Z,a-z,0-9,_,-. If this results in an empty
* string then it will return the alternative value supplied.
*
* @todo Expand to support the full range of CDATA that a class attribute can contain.
*
* @since 2.8.0
*
* @param string $class The classname to be sanitized
* @param string $fallback Optional. The value to return if the sanitization end's up as an empty string.
* Defaults to an empty string.
* @return string The sanitized value
*/
function sanitize_html_class( $class, $fallback = '' ) {
//Strip out any % encoded octets
$sanitized = preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $class );
//Limit to A-Z,a-z,0-9,_,-
$sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $sanitized );
if ( '' == $sanitized )
$sanitized = $fallback;
return apply_filters( 'sanitize_html_class', $sanitized, $class, $fallback );
}
/**
* Converts a number of characters from a string.
*
* Metadata tags <
> and <> are removed, < > and < > are
* converted into correct XHTML and Unicode characters are converted to the
* valid range.
*
* @since 0.71
*
* @param string $content String of characters to be converted.
* @param string $deprecated Not used.
* @return string Converted string.
*/
function convert_chars($content, $deprecated = '') {
if ( !empty( $deprecated ) )
_deprecated_argument( __FUNCTION__, '0.71' );
// Translation of invalid Unicode references range to valid range
$wp_htmltranswinuni = array(
'' => '€', // the Euro sign
'' => '',
'' => '‚', // these are Windows CP1252 specific characters
'' => 'ƒ', // they would look weird on non-Windows browsers
'' => '„',
'
' => '…',
'' => '†',
'' => '‡',
'' => 'ˆ',
'' => '‰',
'' => 'Š',
'' => '‹',
'' => 'Œ',
'' => '',
'' => 'Ž',
'' => '',
'' => '',
'' => '‘',
'' => '’',
'' => '“',
'' => '”',
'' => '•',
'' => '–',
'' => '—',
'' => '˜',
'' => '™',
'' => 'š',
'' => '›',
'' => 'œ',
'' => '',
'' => 'ž',
'' => 'Ÿ'
);
// Remove metadata tags
$content = preg_replace('/(.+?)<\/title>/','',$content);
$content = preg_replace('/(.+?)<\/category>/','',$content);
// Converts lone & characters into & (a.k.a. &)
$content = preg_replace('/&([^#])(?![a-z1-4]{1,8};)/i', '&$1', $content);
// Fix Word pasting
$content = strtr($content, $wp_htmltranswinuni);
// Just a little XHTML help
$content = str_replace(' ', ' ', $content);
$content = str_replace(' ', ' ', $content);
return $content;
}
/**
* Balances tags if forced to, or if the 'use_balanceTags' option is set to true.
*
* @since 0.71
*
* @param string $text Text to be balanced
* @param bool $force If true, forces balancing, ignoring the value of the option. Default false.
* @return string Balanced text
*/
function balanceTags( $text, $force = false ) {
if ( $force || get_option('use_balanceTags') == 1 )
return force_balance_tags( $text );
else
return $text;
}
/**
* Balances tags of string using a modified stack.
*
* @since 2.0.4
*
* @author Leonard Lin
* @license GPL
* @copyright November 4, 2001
* @version 1.1
* @todo Make better - change loop condition to $text in 1.2
* @internal Modified by Scott Reilly (coffee2code) 02 Aug 2004
* 1.1 Fixed handling of append/stack pop order of end text
* Added Cleaning Hooks
* 1.0 First Version
*
* @param string $text Text to be balanced.
* @return string Balanced text.
*/
function force_balance_tags( $text ) {
$tagstack = array();
$stacksize = 0;
$tagqueue = '';
$newtext = '';
// Known single-entity/self-closing tags
$single_tags = array( 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param', 'source' );
// Tags that can be immediately nested within themselves
$nestable_tags = array( 'blockquote', 'div', 'object', 'q', 'span' );
// WP bug fix for comments - in case you REALLY meant to type '< !--'
$text = str_replace('< !--', '< !--', $text);
// WP bug fix for LOVE <3 (and other situations with '<' before a number)
$text = preg_replace('#<([0-9]{1})#', '<$1', $text);
while ( preg_match("/<(\/?[\w:]*)\s*([^>]*)>/", $text, $regex) ) {
$newtext .= $tagqueue;
$i = strpos($text, $regex[0]);
$l = strlen($regex[0]);
// clear the shifter
$tagqueue = '';
// Pop or Push
if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag
$tag = strtolower(substr($regex[1],1));
// if too many closing tags
if( $stacksize <= 0 ) {
$tag = '';
// or close to be safe $tag = '/' . $tag;
}
// if stacktop value = tag close value then pop
else if ( $tagstack[$stacksize - 1] == $tag ) { // found closing tag
$tag = '' . $tag . '>'; // Close Tag
// Pop
array_pop( $tagstack );
$stacksize--;
} else { // closing tag not at top, search for it
for ( $j = $stacksize-1; $j >= 0; $j-- ) {
if ( $tagstack[$j] == $tag ) {
// add tag to tagqueue
for ( $k = $stacksize-1; $k >= $j; $k--) {
$tagqueue .= '' . array_pop( $tagstack ) . '>';
$stacksize--;
}
break;
}
}
$tag = '';
}
} else { // Begin Tag
$tag = strtolower($regex[1]);
// Tag Cleaning
// If it's an empty tag "< >", do nothing
if ( '' == $tag ) {
// do nothing
}
// ElseIf it presents itself as a self-closing tag...
elseif ( substr( $regex[2], -1 ) == '/' ) {
// ...but it isn't a known single-entity self-closing tag, then don't let it be treated as such and
// immediately close it with a closing tag (the tag will encapsulate no text as a result)
if ( ! in_array( $tag, $single_tags ) )
$regex[2] = trim( substr( $regex[2], 0, -1 ) ) . ">$tag";
}
// ElseIf it's a known single-entity tag but it doesn't close itself, do so
elseif ( in_array($tag, $single_tags) ) {
$regex[2] .= '/';
}
// Else it's not a single-entity tag
else {
// If the top of the stack is the same as the tag we want to push, close previous tag
if ( $stacksize > 0 && !in_array($tag, $nestable_tags) && $tagstack[$stacksize - 1] == $tag ) {
$tagqueue = '' . array_pop( $tagstack ) . '>';
$stacksize--;
}
$stacksize = array_push( $tagstack, $tag );
}
// Attributes
$attributes = $regex[2];
if( ! empty( $attributes ) && $attributes[0] != '>' )
$attributes = ' ' . $attributes;
$tag = '<' . $tag . $attributes . '>';
//If already queuing a close tag, then put this tag on, too
if ( !empty($tagqueue) ) {
$tagqueue .= $tag;
$tag = '';
}
}
$newtext .= substr($text, 0, $i) . $tag;
$text = substr($text, $i + $l);
}
// Clear Tag Queue
$newtext .= $tagqueue;
// Add Remaining text
$newtext .= $text;
// Empty Stack
while( $x = array_pop($tagstack) )
$newtext .= '' . $x . '>'; // Add remaining tags to close
// WP fix for the bug with HTML comments
$newtext = str_replace("< !--","