351438ebe1
* Currently handles link, quote, image, gallery, audio, and video formats. * `add_theme_support()` for a given post format is now an indicator that the theme handles format-specific metadata. * If no support for a given format is defined, fallback output will be generated and hooked onto the_content if a post has metadata for that format. * Fallbacks attempt to be smart about not duplicating data already appearing in the post content itself. Gallery is particularly liberal, looking for any instance of the gallery shortcode in the content, not just an exact match to the gallery shortcode defined in the format-specific meta. * Compat output defaults to being wrapped in a `div` with a class of `post-format-content`. Theme authors: please test and evaluate, keeping in mind that the goal is to support user expectations of not losing format-specific data they've entered in the admin when viewing the front-end of their site. props wonderboymusic, beaulebens, helen. see #23347. git-svn-id: https://develop.svn.wordpress.org/trunk@23450 602fd350-edb4-49c9-b593-d223f7449a82
3567 lines
114 KiB
PHP
3567 lines
114 KiB
PHP
<?php
|
|
/**
|
|
* Main WordPress Formatting API.
|
|
*
|
|
* Handles many functions for formatting output.
|
|
*
|
|
* @package WordPress
|
|
*/
|
|
|
|
/**
|
|
* Replaces common plain text characters into formatted entities
|
|
*
|
|
* As an example,
|
|
* <code>
|
|
* 'cause today's effort makes it worth tomorrow's "holiday"...
|
|
* </code>
|
|
* Becomes:
|
|
* <code>
|
|
* ’cause today’s effort makes it worth tomorrow’s “holiday”…
|
|
* </code>
|
|
* 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();
|
|
|
|
$textarr = preg_split('/(<.*>|\[.*\])/Us', $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 ) {
|
|
_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. <pre>) even if there was invalid nesting before that
|
|
*
|
|
* Example: in the case <pre>sadsadasd</code>"baba"</pre>
|
|
* "baba" won't be texturize
|
|
*/
|
|
|
|
array_push($stack, $matches[1]);
|
|
}
|
|
} 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 <<br />> 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, '<pre') !== false ) {
|
|
$pee_parts = explode( '</pre>', $pee );
|
|
$last_pee = array_pop($pee_parts);
|
|
$pee = '';
|
|
$i = 0;
|
|
|
|
foreach ( $pee_parts as $pee_part ) {
|
|
$start = strpos($pee_part, '<pre');
|
|
|
|
// Malformed html?
|
|
if ( $start === false ) {
|
|
$pee .= $pee_part;
|
|
continue;
|
|
}
|
|
|
|
$name = "<pre wp-pre-tag-$i></pre>";
|
|
$pre_tags[$name] = substr( $pee_part, $start ) . '</pre>';
|
|
|
|
$pee .= substr( $pee_part, 0, $start ) . $name;
|
|
$i++;
|
|
}
|
|
|
|
$pee .= $last_pee;
|
|
}
|
|
|
|
$pee = preg_replace('|<br />\s*<br />|', "\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
|
|
if ( strpos($pee, '<object') !== false ) {
|
|
$pee = preg_replace('|\s*<param([^>]*)>\s*|', "<param$1>", $pee); // no pee inside object/embed
|
|
$pee = preg_replace('|\s*</embed>\s*|', '</embed>', $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 .= '<p>' . trim($tinkle, "\n") . "</p>\n";
|
|
$pee = preg_replace('|<p>\s*</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace
|
|
$pee = preg_replace('!<p>([^<]+)</(div|address|form)>!', "<p>$1</p></$2>", $pee);
|
|
$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag
|
|
$pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists
|
|
$pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
|
|
$pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
|
|
$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
|
|
$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
|
|
if ( $br ) {
|
|
$pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', '_autop_newline_preservation_helper', $pee);
|
|
$pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks
|
|
$pee = str_replace('<WPPreserveNewline />', "\n", $pee);
|
|
}
|
|
$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee);
|
|
$pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
|
|
$pee = preg_replace( "|\n</p>$|", '</p>', $pee );
|
|
|
|
if ( !empty($pre_tags) )
|
|
$pee = str_replace(array_keys($pre_tags), array_values($pre_tags), $pee);
|
|
|
|
return $pee;
|
|
}
|
|
|
|
/**
|
|
* 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", "<WPPreserveNewline />", $matches[0]);
|
|
}
|
|
|
|
/**
|
|
* Don't auto-p wrap shortcodes that stand alone
|
|
*
|
|
* Ensures that shortcodes are not wrapped in <<p>>...<</p>>.
|
|
*
|
|
* @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 =
|
|
'/'
|
|
. '<p>' // 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 ASC/DESC, and also accepts
|
|
* RAND().
|
|
*
|
|
* @since 2.5.1
|
|
*
|
|
* @param string $orderby Order by string to be checked.
|
|
* @return string|bool Returns the order by clause if it is a match, false otherwise.
|
|
*/
|
|
function sanitize_sql_orderby( $orderby ){
|
|
preg_match('/^\s*([a-z0-9_]+(\s+(ASC|DESC))?(\s*,\s*|\s*$))+|^\s*RAND\(\s*\)\s*$/i', $orderby, $obmatches);
|
|
if ( !$obmatches )
|
|
return false;
|
|
return $orderby;
|
|
}
|
|
|
|
/**
|
|
* 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 <<title>> and <<category>> are removed, <<br>> and <<hr>> 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>(.+?)<\/title>/','',$content);
|
|
$content = preg_replace('/<category>(.+?)<\/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('<br>', '<br />', $content);
|
|
$content = str_replace('<hr>', '<hr />', $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 <leonard@acm.org>
|
|
* @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("< !--","<!--",$newtext);
|
|
$newtext = str_replace("< !--","< !--",$newtext);
|
|
|
|
return $newtext;
|
|
}
|
|
|
|
/**
|
|
* Acts on text which is about to be edited.
|
|
*
|
|
* The $content is run through esc_textarea(), which uses htmlspecialchars()
|
|
* to convert special characters to HTML entities. If $richedit is set to true,
|
|
* it is simply a holder for the 'format_to_edit' filter.
|
|
*
|
|
* @since 0.71
|
|
*
|
|
* @param string $content The text about to be edited.
|
|
* @param bool $richedit Whether the $content should not pass through htmlspecialchars(). Default false (meaning it will be passed).
|
|
* @return string The text after the filter (and possibly htmlspecialchars()) has been run.
|
|
*/
|
|
function format_to_edit( $content, $richedit = false ) {
|
|
$content = apply_filters( 'format_to_edit', $content );
|
|
if ( ! $richedit )
|
|
$content = esc_textarea( $content );
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Holder for the 'format_to_post' filter.
|
|
*
|
|
* @since 0.71
|
|
*
|
|
* @param string $content The text to pass through the filter.
|
|
* @return string Text returned from the 'format_to_post' filter.
|
|
*/
|
|
function format_to_post($content) {
|
|
$content = apply_filters('format_to_post', $content);
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Add leading zeros when necessary.
|
|
*
|
|
* If you set the threshold to '4' and the number is '10', then you will get
|
|
* back '0010'. If you set the threshold to '4' and the number is '5000', then you
|
|
* will get back '5000'.
|
|
*
|
|
* Uses sprintf to append the amount of zeros based on the $threshold parameter
|
|
* and the size of the number. If the number is large enough, then no zeros will
|
|
* be appended.
|
|
*
|
|
* @since 0.71
|
|
*
|
|
* @param mixed $number Number to append zeros to if not greater than threshold.
|
|
* @param int $threshold Digit places number needs to be to not have zeros added.
|
|
* @return string Adds leading zeros to number if needed.
|
|
*/
|
|
function zeroise($number, $threshold) {
|
|
return sprintf('%0'.$threshold.'s', $number);
|
|
}
|
|
|
|
/**
|
|
* Adds backslashes before letters and before a number at the start of a string.
|
|
*
|
|
* @since 0.71
|
|
*
|
|
* @param string $string Value to which backslashes will be added.
|
|
* @return string String with backslashes inserted.
|
|
*/
|
|
function backslashit($string) {
|
|
$string = preg_replace('/^([0-9])/', '\\\\\\\\\1', $string);
|
|
$string = preg_replace('/([a-z])/i', '\\\\\1', $string);
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Appends a trailing slash.
|
|
*
|
|
* Will remove trailing slash if it exists already before adding a trailing
|
|
* slash. This prevents double slashing a string or path.
|
|
*
|
|
* The primary use of this is for paths and thus should be used for paths. It is
|
|
* not restricted to paths and offers no specific path support.
|
|
*
|
|
* @since 1.2.0
|
|
* @uses untrailingslashit() Unslashes string if it was slashed already.
|
|
*
|
|
* @param string $string What to add the trailing slash to.
|
|
* @return string String with trailing slash added.
|
|
*/
|
|
function trailingslashit($string) {
|
|
return untrailingslashit($string) . '/';
|
|
}
|
|
|
|
/**
|
|
* Removes trailing slash if it exists.
|
|
*
|
|
* The primary use of this is for paths and thus should be used for paths. It is
|
|
* not restricted to paths and offers no specific path support.
|
|
*
|
|
* @since 2.2.0
|
|
*
|
|
* @param string $string What to remove the trailing slash from.
|
|
* @return string String without the trailing slash.
|
|
*/
|
|
function untrailingslashit($string) {
|
|
return rtrim($string, '/');
|
|
}
|
|
|
|
/**
|
|
* Adds slashes to escape strings.
|
|
*
|
|
* Slashes will first be removed if magic_quotes_gpc is set, see {@link
|
|
* http://www.php.net/magic_quotes} for more details.
|
|
*
|
|
* @since 0.71
|
|
*
|
|
* @param string $gpc The string returned from HTTP request data.
|
|
* @return string Returns a string escaped with slashes.
|
|
*/
|
|
function addslashes_gpc($gpc) {
|
|
if ( get_magic_quotes_gpc() )
|
|
$gpc = stripslashes($gpc);
|
|
|
|
return esc_sql($gpc);
|
|
}
|
|
|
|
/**
|
|
* Navigates through an array and removes slashes from the values.
|
|
*
|
|
* If an array is passed, the array_map() function causes a callback to pass the
|
|
* value back to the function. The slashes from this value will removed.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param mixed $value The value to be stripped.
|
|
* @return mixed Stripped value.
|
|
*/
|
|
function stripslashes_deep($value) {
|
|
if ( is_array($value) ) {
|
|
$value = array_map('stripslashes_deep', $value);
|
|
} elseif ( is_object($value) ) {
|
|
$vars = get_object_vars( $value );
|
|
foreach ($vars as $key=>$data) {
|
|
$value->{$key} = stripslashes_deep( $data );
|
|
}
|
|
} elseif ( is_string( $value ) ) {
|
|
$value = stripslashes($value);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Navigates through an array and encodes the values to be used in a URL.
|
|
*
|
|
*
|
|
* @since 2.2.0
|
|
*
|
|
* @param array|string $value The array or string to be encoded.
|
|
* @return array|string $value The encoded array (or string from the callback).
|
|
*/
|
|
function urlencode_deep($value) {
|
|
$value = is_array($value) ? array_map('urlencode_deep', $value) : urlencode($value);
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Navigates through an array and raw encodes the values to be used in a URL.
|
|
*
|
|
* @since 3.4.0
|
|
*
|
|
* @param array|string $value The array or string to be encoded.
|
|
* @return array|string $value The encoded array (or string from the callback).
|
|
*/
|
|
function rawurlencode_deep( $value ) {
|
|
return is_array( $value ) ? array_map( 'rawurlencode_deep', $value ) : rawurlencode( $value );
|
|
}
|
|
|
|
/**
|
|
* Converts email addresses characters to HTML entities to block spam bots.
|
|
*
|
|
* @since 0.71
|
|
*
|
|
* @param string $emailaddy Email address.
|
|
* @param int $mailto Optional. Range from 0 to 1. Used for encoding.
|
|
* @return string Converted email address.
|
|
*/
|
|
function antispambot($emailaddy, $mailto=0) {
|
|
$emailNOSPAMaddy = '';
|
|
srand ((float) microtime() * 1000000);
|
|
for ($i = 0; $i < strlen($emailaddy); $i = $i + 1) {
|
|
$j = floor(rand(0, 1+$mailto));
|
|
if ($j==0) {
|
|
$emailNOSPAMaddy .= '&#'.ord(substr($emailaddy,$i,1)).';';
|
|
} elseif ($j==1) {
|
|
$emailNOSPAMaddy .= substr($emailaddy,$i,1);
|
|
} elseif ($j==2) {
|
|
$emailNOSPAMaddy .= '%'.zeroise(dechex(ord(substr($emailaddy, $i, 1))), 2);
|
|
}
|
|
}
|
|
$emailNOSPAMaddy = str_replace('@','@',$emailNOSPAMaddy);
|
|
return $emailNOSPAMaddy;
|
|
}
|
|
|
|
/**
|
|
* Callback to convert URI match to HTML A element.
|
|
*
|
|
* This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
|
|
* make_clickable()}.
|
|
*
|
|
* @since 2.3.2
|
|
* @access private
|
|
*
|
|
* @param array $matches Single Regex Match.
|
|
* @return string HTML A element with URI address.
|
|
*/
|
|
function _make_url_clickable_cb($matches) {
|
|
$url = $matches[2];
|
|
|
|
if ( ')' == $matches[3] && strpos( $url, '(' ) ) {
|
|
// If the trailing character is a closing parethesis, and the URL has an opening parenthesis in it, add the closing parenthesis to the URL.
|
|
// Then we can let the parenthesis balancer do its thing below.
|
|
$url .= $matches[3];
|
|
$suffix = '';
|
|
} else {
|
|
$suffix = $matches[3];
|
|
}
|
|
|
|
// Include parentheses in the URL only if paired
|
|
while ( substr_count( $url, '(' ) < substr_count( $url, ')' ) ) {
|
|
$suffix = strrchr( $url, ')' ) . $suffix;
|
|
$url = substr( $url, 0, strrpos( $url, ')' ) );
|
|
}
|
|
|
|
$url = esc_url($url);
|
|
if ( empty($url) )
|
|
return $matches[0];
|
|
|
|
return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>" . $suffix;
|
|
}
|
|
|
|
/**
|
|
* Callback to convert URL match to HTML A element.
|
|
*
|
|
* This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
|
|
* make_clickable()}.
|
|
*
|
|
* @since 2.3.2
|
|
* @access private
|
|
*
|
|
* @param array $matches Single Regex Match.
|
|
* @return string HTML A element with URL address.
|
|
*/
|
|
function _make_web_ftp_clickable_cb($matches) {
|
|
$ret = '';
|
|
$dest = $matches[2];
|
|
$dest = 'http://' . $dest;
|
|
$dest = esc_url($dest);
|
|
if ( empty($dest) )
|
|
return $matches[0];
|
|
|
|
// removed trailing [.,;:)] from URL
|
|
if ( in_array( substr($dest, -1), array('.', ',', ';', ':', ')') ) === true ) {
|
|
$ret = substr($dest, -1);
|
|
$dest = substr($dest, 0, strlen($dest)-1);
|
|
}
|
|
return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>$ret";
|
|
}
|
|
|
|
/**
|
|
* Callback to convert email address match to HTML A element.
|
|
*
|
|
* This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
|
|
* make_clickable()}.
|
|
*
|
|
* @since 2.3.2
|
|
* @access private
|
|
*
|
|
* @param array $matches Single Regex Match.
|
|
* @return string HTML A element with email address.
|
|
*/
|
|
function _make_email_clickable_cb($matches) {
|
|
$email = $matches[2] . '@' . $matches[3];
|
|
return $matches[1] . "<a href=\"mailto:$email\">$email</a>";
|
|
}
|
|
|
|
/**
|
|
* Convert plaintext URI to HTML links.
|
|
*
|
|
* Converts URI, www and ftp, and email addresses. Finishes by fixing links
|
|
* within links.
|
|
*
|
|
* @since 0.71
|
|
*
|
|
* @param string $text Content to convert URIs.
|
|
* @return string Content with converted URIs.
|
|
*/
|
|
function make_clickable( $text ) {
|
|
$r = '';
|
|
$textarr = preg_split( '/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // split out HTML tags
|
|
foreach ( $textarr as $piece ) {
|
|
if ( empty( $piece ) || ( $piece[0] == '<' && ! preg_match('|^<\s*[\w]{1,20}+://|', $piece) ) ) {
|
|
$r .= $piece;
|
|
continue;
|
|
}
|
|
|
|
// Long strings might contain expensive edge cases ...
|
|
if ( 10000 < strlen( $piece ) ) {
|
|
// ... break it up
|
|
foreach ( _split_str_by_whitespace( $piece, 2100 ) as $chunk ) { // 2100: Extra room for scheme and leading and trailing paretheses
|
|
if ( 2101 < strlen( $chunk ) ) {
|
|
$r .= $chunk; // Too big, no whitespace: bail.
|
|
} else {
|
|
$r .= make_clickable( $chunk );
|
|
}
|
|
}
|
|
} else {
|
|
$ret = " $piece "; // Pad with whitespace to simplify the regexes
|
|
|
|
$url_clickable = '~
|
|
([\\s(<.,;:!?]) # 1: Leading whitespace, or punctuation
|
|
( # 2: URL
|
|
[\\w]{1,20}+:// # Scheme and hier-part prefix
|
|
(?=\S{1,2000}\s) # Limit to URLs less than about 2000 characters long
|
|
[\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]*+ # Non-punctuation URL character
|
|
(?: # Unroll the Loop: Only allow puctuation URL character if followed by a non-punctuation URL character
|
|
[\'.,;:!?)] # Punctuation URL character
|
|
[\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]++ # Non-punctuation URL character
|
|
)*
|
|
)
|
|
(\)?) # 3: Trailing closing parenthesis (for parethesis balancing post processing)
|
|
~xS'; // The regex is a non-anchored pattern and does not have a single fixed starting character.
|
|
// Tell PCRE to spend more time optimizing since, when used on a page load, it will probably be used several times.
|
|
|
|
$ret = preg_replace_callback( $url_clickable, '_make_url_clickable_cb', $ret );
|
|
|
|
$ret = preg_replace_callback( '#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret );
|
|
$ret = preg_replace_callback( '#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret );
|
|
|
|
$ret = substr( $ret, 1, -1 ); // Remove our whitespace padding.
|
|
$r .= $ret;
|
|
}
|
|
}
|
|
|
|
// Cleanup of accidental links within links
|
|
$r = preg_replace( '#(<a( [^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i', "$1$3</a>", $r );
|
|
return $r;
|
|
}
|
|
|
|
/**
|
|
* Breaks a string into chunks by splitting at whitespace characters.
|
|
* The length of each returned chunk is as close to the specified length goal as possible,
|
|
* with the caveat that each chunk includes its trailing delimiter.
|
|
* Chunks longer than the goal are guaranteed to not have any inner whitespace.
|
|
*
|
|
* Joining the returned chunks with empty delimiters reconstructs the input string losslessly.
|
|
*
|
|
* Input string must have no null characters (or eventual transformations on output chunks must not care about null characters)
|
|
*
|
|
* <code>
|
|
* _split_str_by_whitespace( "1234 67890 1234 67890a cd 1234 890 123456789 1234567890a 45678 1 3 5 7 90 ", 10 ) ==
|
|
* array (
|
|
* 0 => '1234 67890 ', // 11 characters: Perfect split
|
|
* 1 => '1234 ', // 5 characters: '1234 67890a' was too long
|
|
* 2 => '67890a cd ', // 10 characters: '67890a cd 1234' was too long
|
|
* 3 => '1234 890 ', // 11 characters: Perfect split
|
|
* 4 => '123456789 ', // 10 characters: '123456789 1234567890a' was too long
|
|
* 5 => '1234567890a ', // 12 characters: Too long, but no inner whitespace on which to split
|
|
* 6 => ' 45678 ', // 11 characters: Perfect split
|
|
* 7 => '1 3 5 7 9', // 9 characters: End of $string
|
|
* );
|
|
* </code>
|
|
*
|
|
* @since 3.4.0
|
|
* @access private
|
|
*
|
|
* @param string $string The string to split.
|
|
* @param int $goal The desired chunk length.
|
|
* @return array Numeric array of chunks.
|
|
*/
|
|
function _split_str_by_whitespace( $string, $goal ) {
|
|
$chunks = array();
|
|
|
|
$string_nullspace = strtr( $string, "\r\n\t\v\f ", "\000\000\000\000\000\000" );
|
|
|
|
while ( $goal < strlen( $string_nullspace ) ) {
|
|
$pos = strrpos( substr( $string_nullspace, 0, $goal + 1 ), "\000" );
|
|
|
|
if ( false === $pos ) {
|
|
$pos = strpos( $string_nullspace, "\000", $goal + 1 );
|
|
if ( false === $pos ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
$chunks[] = substr( $string, 0, $pos + 1 );
|
|
$string = substr( $string, $pos + 1 );
|
|
$string_nullspace = substr( $string_nullspace, $pos + 1 );
|
|
}
|
|
|
|
if ( $string ) {
|
|
$chunks[] = $string;
|
|
}
|
|
|
|
return $chunks;
|
|
}
|
|
|
|
/**
|
|
* Adds rel nofollow string to all HTML A elements in content.
|
|
*
|
|
* @since 1.5.0
|
|
*
|
|
* @param string $text Content that may contain HTML A elements.
|
|
* @return string Converted content.
|
|
*/
|
|
function wp_rel_nofollow( $text ) {
|
|
$text = preg_replace_callback('|<a (.+?)>|i', 'wp_rel_nofollow_callback', $text);
|
|
return $text;
|
|
}
|
|
|
|
/**
|
|
* Callback to add rel=nofollow string to HTML A element.
|
|
*
|
|
* Will remove already existing rel="nofollow" and rel='nofollow' from the
|
|
* string to prevent from invalidating (X)HTML.
|
|
*
|
|
* @since 2.3.0
|
|
*
|
|
* @param array $matches Single Match
|
|
* @return string HTML A Element with rel nofollow.
|
|
*/
|
|
function wp_rel_nofollow_callback( $matches ) {
|
|
$text = $matches[1];
|
|
$text = str_replace(array(' rel="nofollow"', " rel='nofollow'"), '', $text);
|
|
return "<a $text rel=\"nofollow\">";
|
|
}
|
|
|
|
/**
|
|
* Convert one smiley code to the icon graphic file equivalent.
|
|
*
|
|
* Looks up one smiley code in the $wpsmiliestrans global array and returns an
|
|
* <img> string for that smiley.
|
|
*
|
|
* @global array $wpsmiliestrans
|
|
* @since 2.8.0
|
|
*
|
|
* @param string $smiley Smiley code to convert to image.
|
|
* @return string Image string for smiley.
|
|
*/
|
|
function translate_smiley($smiley) {
|
|
global $wpsmiliestrans;
|
|
|
|
if (count($smiley) == 0) {
|
|
return '';
|
|
}
|
|
|
|
$smiley = trim(reset($smiley));
|
|
$img = $wpsmiliestrans[$smiley];
|
|
$smiley_masked = esc_attr($smiley);
|
|
|
|
$srcurl = apply_filters('smilies_src', includes_url("images/smilies/$img"), $img, site_url());
|
|
|
|
return " <img src='$srcurl' alt='$smiley_masked' class='wp-smiley' /> ";
|
|
}
|
|
|
|
/**
|
|
* Convert text equivalent of smilies to images.
|
|
*
|
|
* Will only convert smilies if the option 'use_smilies' is true and the global
|
|
* used in the function isn't empty.
|
|
*
|
|
* @since 0.71
|
|
* @uses $wp_smiliessearch
|
|
*
|
|
* @param string $text Content to convert smilies from text.
|
|
* @return string Converted content with text smilies replaced with images.
|
|
*/
|
|
function convert_smilies($text) {
|
|
global $wp_smiliessearch;
|
|
$output = '';
|
|
if ( get_option('use_smilies') && !empty($wp_smiliessearch) ) {
|
|
// HTML loop taken from texturize function, could possible be consolidated
|
|
$textarr = preg_split("/(<.*>)/U", $text, -1, PREG_SPLIT_DELIM_CAPTURE); // capture the tags as well as in between
|
|
$stop = count($textarr);// loop stuff
|
|
for ($i = 0; $i < $stop; $i++) {
|
|
$content = $textarr[$i];
|
|
if ((strlen($content) > 0) && ('<' != $content[0])) { // If it's not a tag
|
|
$content = preg_replace_callback($wp_smiliessearch, 'translate_smiley', $content);
|
|
}
|
|
$output .= $content;
|
|
}
|
|
} else {
|
|
// return default text.
|
|
$output = $text;
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Return the class for a post format content wrapper
|
|
*
|
|
* @since 3.6.0
|
|
*
|
|
* @param string $format
|
|
*/
|
|
function get_post_format_content_class( $format ) {
|
|
return apply_filters( 'post_format_content_class', 'post-format-content', $format );
|
|
}
|
|
|
|
/**
|
|
* Ouput the class for a post format content wrapper
|
|
*
|
|
* @since 3.6.0
|
|
*
|
|
* @param string $format
|
|
*/
|
|
function post_format_content_class( $format ) {
|
|
echo get_post_format_content_class( $format );
|
|
}
|
|
|
|
/**
|
|
* Provide fallback behavior for Posts that have associated post format
|
|
*
|
|
* @since 3.6.0
|
|
*
|
|
* @param string $content
|
|
*/
|
|
function post_formats_compat( $content, $id = 0 ) {
|
|
$post = empty( $id ) ? get_post() : get_post( $id );
|
|
if ( empty( $post ) )
|
|
return $content;
|
|
|
|
$format = get_post_format( $post );
|
|
if ( empty( $format ) || in_array( $format, array( 'status', 'aside', 'chat' ) ) )
|
|
return $content;
|
|
|
|
if ( current_theme_supports( 'post-formats', $format ) )
|
|
return $content;
|
|
|
|
$defaults = array(
|
|
'position' => 'after',
|
|
'tag' => 'div',
|
|
'class' => get_post_format_content_class( $format ),
|
|
'link_class' => '',
|
|
'image_class' => '',
|
|
'gallery' => '[gallery]',
|
|
'audio' => '',
|
|
'video' => ''
|
|
);
|
|
|
|
$args = apply_filters( 'post_format_compat', array() );
|
|
$compat = wp_parse_args( $args, $defaults );
|
|
|
|
$show_content = true;
|
|
$format_output = '';
|
|
$meta = get_post_format_meta( $post->ID );
|
|
|
|
switch ( $format ) {
|
|
case 'link':
|
|
$compat['tag'] = '';
|
|
|
|
if ( ! empty( $meta['url'] ) ) {
|
|
$esc_url = preg_quote( $meta['url'], '#' );
|
|
// Make sure the same URL isn't in the post (modified/extended versions allowed)
|
|
if ( ! preg_match( '#' . $esc_url . '[^/&\?]#', $content ) ) {
|
|
$format_output .= sprintf(
|
|
'<a %shref="%s">%s</a>',
|
|
empty( $compat['link_class'] ) ? '' : sprintf( 'class="%s" ', esc_attr( $compat['link_class'] ) ),
|
|
esc_url( $meta['url'] ),
|
|
empty( $post->post_title ) ? esc_url( $meta['url'] ) : apply_filters( 'the_title', $post->post_title )
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'quote':
|
|
if ( ! empty( $meta['quote'] ) && ! stristr( $content, $meta['quote'] ) ) {
|
|
$format_output .= sprintf( '<blockquote>%s</blockquote>', $meta['quote'] );
|
|
if ( ! empty( $meta['quote_source'] ) ) {
|
|
$format_output .= sprintf(
|
|
'<cite>%s</cite>',
|
|
! empty( $meta['url'] ) ?
|
|
sprintf( '<a href="%s">%s</a>', esc_url( $meta['url'] ), $meta['quote_source'] ) :
|
|
$meta['quote_source']
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'image':
|
|
if ( ! empty( $meta['image'] ) ) {
|
|
$image = is_numeric( $meta['image'] ) ? wp_get_attachment_url( $meta['image'] ) : $meta['image'];
|
|
|
|
if ( ! empty( $image ) && ! stristr( $content, $image ) ) {
|
|
$image_html = sprintf(
|
|
'<img %ssrc="%s" alt="" />',
|
|
empty( $compat['image_class'] ) ? '' : sprintf( 'class="%s" ', esc_attr( $compat['image_class'] ) ),
|
|
$image
|
|
);
|
|
if ( empty( $meta['url'] ) ) {
|
|
$format_output .= $image_html;
|
|
} else {
|
|
$format_output .= sprintf(
|
|
'<a href="%s">%s</a>',
|
|
esc_url( $meta['url'] ),
|
|
$image_html
|
|
);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'gallery':
|
|
preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches );
|
|
if ( ! empty( $matches ) && isset( $matches[2] ) ) {
|
|
foreach ( (array) $matches[2] as $match ) {
|
|
if ( 'gallery' === $match )
|
|
break 2; // foreach + case
|
|
}
|
|
}
|
|
|
|
if ( empty( $meta['gallery'] ) && ! empty( $compat['gallery'] ) ) {
|
|
$format_output .= $compat['gallery'];
|
|
} elseif ( ! empty( $meta['gallery'] ) ) {
|
|
$format_output .= $meta['gallery'];
|
|
}
|
|
break;
|
|
|
|
case 'video':
|
|
case 'audio':
|
|
$shortcode_regex = '/' . get_shortcode_regex() . '/s';
|
|
$matches = preg_match( $shortcode_regex, $content );
|
|
if ( ! $matches || $format !== $matches[2] ) {
|
|
if ( empty( $meta['media'] ) && ! empty( $compat[$format] ) ) {
|
|
$format_output .= $compat[$format];
|
|
} elseif ( ! empty( $meta['media'] ) ) {
|
|
// the metadata is a shortcode or an embed code
|
|
if ( preg_match( $shortcode_regex, $meta['media'] ) || preg_match( '#<[^>]+>#', $meta['media'] ) ) {
|
|
$format_output .= $meta['media'];
|
|
} elseif ( ! stristr( $content, $meta['media'] ) ) {
|
|
// attempt to embed the URL
|
|
$format_output .= sprintf( '[embed]%s[/embed]', $meta['media'] );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return $content;
|
|
break;
|
|
}
|
|
|
|
if ( empty( $format_output ) )
|
|
return $content;
|
|
|
|
$output = '';
|
|
|
|
if ( ! empty( $content ) && $show_content && 'before' !== $compat['position'] )
|
|
$output .= $content . PHP_EOL . PHP_EOL;
|
|
|
|
if ( ! empty( $compat['tag'] ) )
|
|
$output .= sprintf( '<%s class="%s">', tag_escape( $compat['tag'] ), esc_attr( $compat['class'] ) );
|
|
|
|
$output .= $format_output;
|
|
|
|
if ( ! empty( $compat['tag'] ) )
|
|
$output .= sprintf( '</%s>', tag_escape( $compat['tag'] ) );
|
|
|
|
if ( ! empty( $content ) && $show_content && 'before' === $compat['position'] )
|
|
$output .= PHP_EOL . PHP_EOL . $content;
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Verifies that an email is valid.
|
|
*
|
|
* Does not grok i18n domains. Not RFC compliant.
|
|
*
|
|
* @since 0.71
|
|
*
|
|
* @param string $email Email address to verify.
|
|
* @param boolean $deprecated Deprecated.
|
|
* @return string|bool Either false or the valid email address.
|
|
*/
|
|
function is_email( $email, $deprecated = false ) {
|
|
if ( ! empty( $deprecated ) )
|
|
_deprecated_argument( __FUNCTION__, '3.0' );
|
|
|
|
// Test for the minimum length the email can be
|
|
if ( strlen( $email ) < 3 ) {
|
|
return apply_filters( 'is_email', false, $email, 'email_too_short' );
|
|
}
|
|
|
|
// Test for an @ character after the first position
|
|
if ( strpos( $email, '@', 1 ) === false ) {
|
|
return apply_filters( 'is_email', false, $email, 'email_no_at' );
|
|
}
|
|
|
|
// Split out the local and domain parts
|
|
list( $local, $domain ) = explode( '@', $email, 2 );
|
|
|
|
// LOCAL PART
|
|
// Test for invalid characters
|
|
if ( !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local ) ) {
|
|
return apply_filters( 'is_email', false, $email, 'local_invalid_chars' );
|
|
}
|
|
|
|
// DOMAIN PART
|
|
// Test for sequences of periods
|
|
if ( preg_match( '/\.{2,}/', $domain ) ) {
|
|
return apply_filters( 'is_email', false, $email, 'domain_period_sequence' );
|
|
}
|
|
|
|
// Test for leading and trailing periods and whitespace
|
|
if ( trim( $domain, " \t\n\r\0\x0B." ) !== $domain ) {
|
|
return apply_filters( 'is_email', false, $email, 'domain_period_limits' );
|
|
}
|
|
|
|
// Split the domain into subs
|
|
$subs = explode( '.', $domain );
|
|
|
|
// Assume the domain will have at least two subs
|
|
if ( 2 > count( $subs ) ) {
|
|
return apply_filters( 'is_email', false, $email, 'domain_no_periods' );
|
|
}
|
|
|
|
// Loop through each sub
|
|
foreach ( $subs as $sub ) {
|
|
// Test for leading and trailing hyphens and whitespace
|
|
if ( trim( $sub, " \t\n\r\0\x0B-" ) !== $sub ) {
|
|
return apply_filters( 'is_email', false, $email, 'sub_hyphen_limits' );
|
|
}
|
|
|
|
// Test for invalid characters
|
|
if ( !preg_match('/^[a-z0-9-]+$/i', $sub ) ) {
|
|
return apply_filters( 'is_email', false, $email, 'sub_invalid_chars' );
|
|
}
|
|
}
|
|
|
|
// Congratulations your email made it!
|
|
return apply_filters( 'is_email', $email, $email, null );
|
|
}
|
|
|
|
/**
|
|
* Convert to ASCII from email subjects.
|
|
*
|
|
* @since 1.2.0
|
|
*
|
|
* @param string $string Subject line
|
|
* @return string Converted string to ASCII
|
|
*/
|
|
function wp_iso_descrambler($string) {
|
|
/* this may only work with iso-8859-1, I'm afraid */
|
|
if (!preg_match('#\=\?(.+)\?Q\?(.+)\?\=#i', $string, $matches)) {
|
|
return $string;
|
|
} else {
|
|
$subject = str_replace('_', ' ', $matches[2]);
|
|
$subject = preg_replace_callback('#\=([0-9a-f]{2})#i', '_wp_iso_convert', $subject);
|
|
return $subject;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper function to convert hex encoded chars to ASCII
|
|
*
|
|
* @since 3.1.0
|
|
* @access private
|
|
*
|
|
* @param array $match The preg_replace_callback matches array
|
|
* @return array Converted chars
|
|
*/
|
|
function _wp_iso_convert( $match ) {
|
|
return chr( hexdec( strtolower( $match[1] ) ) );
|
|
}
|
|
|
|
/**
|
|
* Returns a date in the GMT equivalent.
|
|
*
|
|
* Requires and returns a date in the Y-m-d H:i:s format. Simply subtracts the
|
|
* value of the 'gmt_offset' option. Return format can be overridden using the
|
|
* $format parameter. The DateTime and DateTimeZone classes are used to respect
|
|
* time zone differences in DST.
|
|
*
|
|
* @since 1.2.0
|
|
*
|
|
* @uses get_option() to retrieve the the value of 'gmt_offset'.
|
|
* @param string $string The date to be converted.
|
|
* @param string $format The format string for the returned date (default is Y-m-d H:i:s)
|
|
* @return string GMT version of the date provided.
|
|
*/
|
|
function get_gmt_from_date($string, $format = 'Y-m-d H:i:s') {
|
|
preg_match('#([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})#', $string, $matches);
|
|
if ( ! $matches )
|
|
return date( $format, 0 );
|
|
|
|
$tz = get_option('timezone_string');
|
|
if ( $tz ) {
|
|
date_default_timezone_set( $tz );
|
|
$datetime = date_create( $string );
|
|
if ( ! $datetime )
|
|
return date( $format, 0 );
|
|
|
|
$datetime->setTimezone( new DateTimeZone('UTC') );
|
|
$offset = $datetime->getOffset();
|
|
$datetime->modify( '+' . $offset / HOUR_IN_SECONDS . ' hours');
|
|
$string_gmt = gmdate($format, $datetime->format('U'));
|
|
|
|
date_default_timezone_set('UTC');
|
|
} else {
|
|
$string_time = gmmktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
|
|
$string_gmt = gmdate($format, $string_time - get_option('gmt_offset') * HOUR_IN_SECONDS);
|
|
}
|
|
return $string_gmt;
|
|
}
|
|
|
|
/**
|
|
* Converts a GMT date into the correct format for the blog.
|
|
*
|
|
* Requires and returns in the Y-m-d H:i:s format. Simply adds the value of
|
|
* gmt_offset.Return format can be overridden using the $format parameter
|
|
*
|
|
* @since 1.2.0
|
|
*
|
|
* @param string $string The date to be converted.
|
|
* @param string $format The format string for the returned date (default is Y-m-d H:i:s)
|
|
* @return string Formatted date relative to the GMT offset.
|
|
*/
|
|
function get_date_from_gmt($string, $format = 'Y-m-d H:i:s') {
|
|
preg_match('#([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})#', $string, $matches);
|
|
$string_time = gmmktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
|
|
$string_localtime = gmdate($format, $string_time + get_option('gmt_offset') * HOUR_IN_SECONDS);
|
|
return $string_localtime;
|
|
}
|
|
|
|
/**
|
|
* Computes an offset in seconds from an iso8601 timezone.
|
|
*
|
|
* @since 1.5.0
|
|
*
|
|
* @param string $timezone Either 'Z' for 0 offset or '±hhmm'.
|
|
* @return int|float The offset in seconds.
|
|
*/
|
|
function iso8601_timezone_to_offset($timezone) {
|
|
// $timezone is either 'Z' or '[+|-]hhmm'
|
|
if ($timezone == 'Z') {
|
|
$offset = 0;
|
|
} else {
|
|
$sign = (substr($timezone, 0, 1) == '+') ? 1 : -1;
|
|
$hours = intval(substr($timezone, 1, 2));
|
|
$minutes = intval(substr($timezone, 3, 4)) / 60;
|
|
$offset = $sign * HOUR_IN_SECONDS * ($hours + $minutes);
|
|
}
|
|
return $offset;
|
|
}
|
|
|
|
/**
|
|
* Converts an iso8601 date to MySQL DateTime format used by post_date[_gmt].
|
|
*
|
|
* @since 1.5.0
|
|
*
|
|
* @param string $date_string Date and time in ISO 8601 format {@link http://en.wikipedia.org/wiki/ISO_8601}.
|
|
* @param string $timezone Optional. If set to GMT returns the time minus gmt_offset. Default is 'user'.
|
|
* @return string The date and time in MySQL DateTime format - Y-m-d H:i:s.
|
|
*/
|
|
function iso8601_to_datetime($date_string, $timezone = 'user') {
|
|
$timezone = strtolower($timezone);
|
|
|
|
if ($timezone == 'gmt') {
|
|
|
|
preg_match('#([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(Z|[\+|\-][0-9]{2,4}){0,1}#', $date_string, $date_bits);
|
|
|
|
if (!empty($date_bits[7])) { // we have a timezone, so let's compute an offset
|
|
$offset = iso8601_timezone_to_offset($date_bits[7]);
|
|
} else { // we don't have a timezone, so we assume user local timezone (not server's!)
|
|
$offset = HOUR_IN_SECONDS * get_option('gmt_offset');
|
|
}
|
|
|
|
$timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]);
|
|
$timestamp -= $offset;
|
|
|
|
return gmdate('Y-m-d H:i:s', $timestamp);
|
|
|
|
} else if ($timezone == 'user') {
|
|
return preg_replace('#([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(Z|[\+|\-][0-9]{2,4}){0,1}#', '$1-$2-$3 $4:$5:$6', $date_string);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a element attributes to open links in new windows.
|
|
*
|
|
* Comment text in popup windows should be filtered through this. Right now it's
|
|
* a moderately dumb function, ideally it would detect whether a target or rel
|
|
* attribute was already there and adjust its actions accordingly.
|
|
*
|
|
* @since 0.71
|
|
*
|
|
* @param string $text Content to replace links to open in a new window.
|
|
* @return string Content that has filtered links.
|
|
*/
|
|
function popuplinks($text) {
|
|
$text = preg_replace('/<a (.+?)>/i', "<a $1 target='_blank' rel='external'>", $text);
|
|
return $text;
|
|
}
|
|
|
|
/**
|
|
* Strips out all characters that are not allowable in an email.
|
|
*
|
|
* @since 1.5.0
|
|
*
|
|
* @param string $email Email address to filter.
|
|
* @return string Filtered email address.
|
|
*/
|
|
function sanitize_email( $email ) {
|
|
// Test for the minimum length the email can be
|
|
if ( strlen( $email ) < 3 ) {
|
|
return apply_filters( 'sanitize_email', '', $email, 'email_too_short' );
|
|
}
|
|
|
|
// Test for an @ character after the first position
|
|
if ( strpos( $email, '@', 1 ) === false ) {
|
|
return apply_filters( 'sanitize_email', '', $email, 'email_no_at' );
|
|
}
|
|
|
|
// Split out the local and domain parts
|
|
list( $local, $domain ) = explode( '@', $email, 2 );
|
|
|
|
// LOCAL PART
|
|
// Test for invalid characters
|
|
$local = preg_replace( '/[^a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]/', '', $local );
|
|
if ( '' === $local ) {
|
|
return apply_filters( 'sanitize_email', '', $email, 'local_invalid_chars' );
|
|
}
|
|
|
|
// DOMAIN PART
|
|
// Test for sequences of periods
|
|
$domain = preg_replace( '/\.{2,}/', '', $domain );
|
|
if ( '' === $domain ) {
|
|
return apply_filters( 'sanitize_email', '', $email, 'domain_period_sequence' );
|
|
}
|
|
|
|
// Test for leading and trailing periods and whitespace
|
|
$domain = trim( $domain, " \t\n\r\0\x0B." );
|
|
if ( '' === $domain ) {
|
|
return apply_filters( 'sanitize_email', '', $email, 'domain_period_limits' );
|
|
}
|
|
|
|
// Split the domain into subs
|
|
$subs = explode( '.', $domain );
|
|
|
|
// Assume the domain will have at least two subs
|
|
if ( 2 > count( $subs ) ) {
|
|
return apply_filters( 'sanitize_email', '', $email, 'domain_no_periods' );
|
|
}
|
|
|
|
// Create an array that will contain valid subs
|
|
$new_subs = array();
|
|
|
|
// Loop through each sub
|
|
foreach ( $subs as $sub ) {
|
|
// Test for leading and trailing hyphens
|
|
$sub = trim( $sub, " \t\n\r\0\x0B-" );
|
|
|
|
// Test for invalid characters
|
|
$sub = preg_replace( '/[^a-z0-9-]+/i', '', $sub );
|
|
|
|
// If there's anything left, add it to the valid subs
|
|
if ( '' !== $sub ) {
|
|
$new_subs[] = $sub;
|
|
}
|
|
}
|
|
|
|
// If there aren't 2 or more valid subs
|
|
if ( 2 > count( $new_subs ) ) {
|
|
return apply_filters( 'sanitize_email', '', $email, 'domain_no_valid_subs' );
|
|
}
|
|
|
|
// Join valid subs into the new domain
|
|
$domain = join( '.', $new_subs );
|
|
|
|
// Put the email back together
|
|
$email = $local . '@' . $domain;
|
|
|
|
// Congratulations your email made it!
|
|
return apply_filters( 'sanitize_email', $email, $email, null );
|
|
}
|
|
|
|
/**
|
|
* Determines the difference between two timestamps.
|
|
*
|
|
* The difference is returned in a human readable format such as "1 hour",
|
|
* "5 mins", "2 days".
|
|
*
|
|
* @since 1.5.0
|
|
*
|
|
* @param int $from Unix timestamp from which the difference begins.
|
|
* @param int $to Optional. Unix timestamp to end the time difference. Default becomes time() if not set.
|
|
* @return string Human readable time difference.
|
|
*/
|
|
function human_time_diff( $from, $to = '' ) {
|
|
if ( empty( $to ) )
|
|
$to = time();
|
|
$diff = (int) abs( $to - $from );
|
|
if ( $diff <= HOUR_IN_SECONDS ) {
|
|
$mins = round( $diff / MINUTE_IN_SECONDS );
|
|
if ( $mins <= 1 ) {
|
|
$mins = 1;
|
|
}
|
|
/* translators: min=minute */
|
|
$since = sprintf( _n( '%s min', '%s mins', $mins ), $mins );
|
|
} elseif ( ( $diff <= DAY_IN_SECONDS ) && ( $diff > HOUR_IN_SECONDS ) ) {
|
|
$hours = round( $diff / HOUR_IN_SECONDS );
|
|
if ( $hours <= 1 ) {
|
|
$hours = 1;
|
|
}
|
|
$since = sprintf( _n( '%s hour', '%s hours', $hours ), $hours );
|
|
} elseif ( $diff >= DAY_IN_SECONDS ) {
|
|
$days = round( $diff / DAY_IN_SECONDS );
|
|
if ( $days <= 1 ) {
|
|
$days = 1;
|
|
}
|
|
$since = sprintf( _n( '%s day', '%s days', $days ), $days );
|
|
}
|
|
return $since;
|
|
}
|
|
|
|
/**
|
|
* Generates an excerpt from the content, if needed.
|
|
*
|
|
* The excerpt word amount will be 55 words and if the amount is greater than
|
|
* that, then the string ' [...]' will be appended to the excerpt. If the string
|
|
* is less than 55 words, then the content will be returned as is.
|
|
*
|
|
* The 55 word limit can be modified by plugins/themes using the excerpt_length filter
|
|
* The ' [...]' string can be modified by plugins/themes using the excerpt_more filter
|
|
*
|
|
* @since 1.5.0
|
|
*
|
|
* @param string $text Optional. The excerpt. If set to empty, an excerpt is generated.
|
|
* @return string The excerpt.
|
|
*/
|
|
function wp_trim_excerpt($text = '') {
|
|
$raw_excerpt = $text;
|
|
if ( '' == $text ) {
|
|
$text = get_the_content('');
|
|
|
|
$text = strip_shortcodes( $text );
|
|
|
|
$text = apply_filters('the_content', $text);
|
|
$text = str_replace(']]>', ']]>', $text);
|
|
$excerpt_length = apply_filters('excerpt_length', 55);
|
|
$excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
|
|
$text = wp_trim_words( $text, $excerpt_length, $excerpt_more );
|
|
}
|
|
return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
|
|
}
|
|
|
|
/**
|
|
* Trims text to a certain number of words.
|
|
*
|
|
* This function is localized. For languages that count 'words' by the individual
|
|
* character (such as East Asian languages), the $num_words argument will apply
|
|
* to the number of individual characters.
|
|
*
|
|
* @since 3.3.0
|
|
*
|
|
* @param string $text Text to trim.
|
|
* @param int $num_words Number of words. Default 55.
|
|
* @param string $more What to append if $text needs to be trimmed. Default '…'.
|
|
* @return string Trimmed text.
|
|
*/
|
|
function wp_trim_words( $text, $num_words = 55, $more = null ) {
|
|
if ( null === $more )
|
|
$more = __( '…' );
|
|
$original_text = $text;
|
|
$text = wp_strip_all_tags( $text );
|
|
/* translators: If your word count is based on single characters (East Asian characters),
|
|
enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
|
|
if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
|
|
$text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
|
|
preg_match_all( '/./u', $text, $words_array );
|
|
$words_array = array_slice( $words_array[0], 0, $num_words + 1 );
|
|
$sep = '';
|
|
} else {
|
|
$words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
|
|
$sep = ' ';
|
|
}
|
|
if ( count( $words_array ) > $num_words ) {
|
|
array_pop( $words_array );
|
|
$text = implode( $sep, $words_array );
|
|
$text = $text . $more;
|
|
} else {
|
|
$text = implode( $sep, $words_array );
|
|
}
|
|
return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text );
|
|
}
|
|
|
|
/**
|
|
* Converts named entities into numbered entities.
|
|
*
|
|
* @since 1.5.1
|
|
*
|
|
* @param string $text The text within which entities will be converted.
|
|
* @return string Text with converted entities.
|
|
*/
|
|
function ent2ncr($text) {
|
|
|
|
// Allow a plugin to short-circuit and override the mappings.
|
|
$filtered = apply_filters( 'pre_ent2ncr', null, $text );
|
|
if( null !== $filtered )
|
|
return $filtered;
|
|
|
|
$to_ncr = array(
|
|
'"' => '"',
|
|
'&' => '&',
|
|
'⁄' => '/',
|
|
'<' => '<',
|
|
'>' => '>',
|
|
'|' => '|',
|
|
' ' => ' ',
|
|
'¡' => '¡',
|
|
'¢' => '¢',
|
|
'£' => '£',
|
|
'¤' => '¤',
|
|
'¥' => '¥',
|
|
'¦' => '¦',
|
|
'&brkbar;' => '¦',
|
|
'§' => '§',
|
|
'¨' => '¨',
|
|
'¨' => '¨',
|
|
'©' => '©',
|
|
'ª' => 'ª',
|
|
'«' => '«',
|
|
'¬' => '¬',
|
|
'­' => '­',
|
|
'®' => '®',
|
|
'¯' => '¯',
|
|
'&hibar;' => '¯',
|
|
'°' => '°',
|
|
'±' => '±',
|
|
'²' => '²',
|
|
'³' => '³',
|
|
'´' => '´',
|
|
'µ' => 'µ',
|
|
'¶' => '¶',
|
|
'·' => '·',
|
|
'¸' => '¸',
|
|
'¹' => '¹',
|
|
'º' => 'º',
|
|
'»' => '»',
|
|
'¼' => '¼',
|
|
'½' => '½',
|
|
'¾' => '¾',
|
|
'¿' => '¿',
|
|
'À' => 'À',
|
|
'Á' => 'Á',
|
|
'Â' => 'Â',
|
|
'Ã' => 'Ã',
|
|
'Ä' => 'Ä',
|
|
'Å' => 'Å',
|
|
'Æ' => 'Æ',
|
|
'Ç' => 'Ç',
|
|
'È' => 'È',
|
|
'É' => 'É',
|
|
'Ê' => 'Ê',
|
|
'Ë' => 'Ë',
|
|
'Ì' => 'Ì',
|
|
'Í' => 'Í',
|
|
'Î' => 'Î',
|
|
'Ï' => 'Ï',
|
|
'Ð' => 'Ð',
|
|
'Ñ' => 'Ñ',
|
|
'Ò' => 'Ò',
|
|
'Ó' => 'Ó',
|
|
'Ô' => 'Ô',
|
|
'Õ' => 'Õ',
|
|
'Ö' => 'Ö',
|
|
'×' => '×',
|
|
'Ø' => 'Ø',
|
|
'Ù' => 'Ù',
|
|
'Ú' => 'Ú',
|
|
'Û' => 'Û',
|
|
'Ü' => 'Ü',
|
|
'Ý' => 'Ý',
|
|
'Þ' => 'Þ',
|
|
'ß' => 'ß',
|
|
'à' => 'à',
|
|
'á' => 'á',
|
|
'â' => 'â',
|
|
'ã' => 'ã',
|
|
'ä' => 'ä',
|
|
'å' => 'å',
|
|
'æ' => 'æ',
|
|
'ç' => 'ç',
|
|
'è' => 'è',
|
|
'é' => 'é',
|
|
'ê' => 'ê',
|
|
'ë' => 'ë',
|
|
'ì' => 'ì',
|
|
'í' => 'í',
|
|
'î' => 'î',
|
|
'ï' => 'ï',
|
|
'ð' => 'ð',
|
|
'ñ' => 'ñ',
|
|
'ò' => 'ò',
|
|
'ó' => 'ó',
|
|
'ô' => 'ô',
|
|
'õ' => 'õ',
|
|
'ö' => 'ö',
|
|
'÷' => '÷',
|
|
'ø' => 'ø',
|
|
'ù' => 'ù',
|
|
'ú' => 'ú',
|
|
'û' => 'û',
|
|
'ü' => 'ü',
|
|
'ý' => 'ý',
|
|
'þ' => 'þ',
|
|
'ÿ' => 'ÿ',
|
|
'Œ' => 'Œ',
|
|
'œ' => 'œ',
|
|
'Š' => 'Š',
|
|
'š' => 'š',
|
|
'Ÿ' => 'Ÿ',
|
|
'ƒ' => 'ƒ',
|
|
'ˆ' => 'ˆ',
|
|
'˜' => '˜',
|
|
'Α' => 'Α',
|
|
'Β' => 'Β',
|
|
'Γ' => 'Γ',
|
|
'Δ' => 'Δ',
|
|
'Ε' => 'Ε',
|
|
'Ζ' => 'Ζ',
|
|
'Η' => 'Η',
|
|
'Θ' => 'Θ',
|
|
'Ι' => 'Ι',
|
|
'Κ' => 'Κ',
|
|
'Λ' => 'Λ',
|
|
'Μ' => 'Μ',
|
|
'Ν' => 'Ν',
|
|
'Ξ' => 'Ξ',
|
|
'Ο' => 'Ο',
|
|
'Π' => 'Π',
|
|
'Ρ' => 'Ρ',
|
|
'Σ' => 'Σ',
|
|
'Τ' => 'Τ',
|
|
'Υ' => 'Υ',
|
|
'Φ' => 'Φ',
|
|
'Χ' => 'Χ',
|
|
'Ψ' => 'Ψ',
|
|
'Ω' => 'Ω',
|
|
'α' => 'α',
|
|
'β' => 'β',
|
|
'γ' => 'γ',
|
|
'δ' => 'δ',
|
|
'ε' => 'ε',
|
|
'ζ' => 'ζ',
|
|
'η' => 'η',
|
|
'θ' => 'θ',
|
|
'ι' => 'ι',
|
|
'κ' => 'κ',
|
|
'λ' => 'λ',
|
|
'μ' => 'μ',
|
|
'ν' => 'ν',
|
|
'ξ' => 'ξ',
|
|
'ο' => 'ο',
|
|
'π' => 'π',
|
|
'ρ' => 'ρ',
|
|
'ς' => 'ς',
|
|
'σ' => 'σ',
|
|
'τ' => 'τ',
|
|
'υ' => 'υ',
|
|
'φ' => 'φ',
|
|
'χ' => 'χ',
|
|
'ψ' => 'ψ',
|
|
'ω' => 'ω',
|
|
'ϑ' => 'ϑ',
|
|
'ϒ' => 'ϒ',
|
|
'ϖ' => 'ϖ',
|
|
' ' => ' ',
|
|
' ' => ' ',
|
|
' ' => ' ',
|
|
'‌' => '‌',
|
|
'‍' => '‍',
|
|
'‎' => '‎',
|
|
'‏' => '‏',
|
|
'–' => '–',
|
|
'—' => '—',
|
|
'‘' => '‘',
|
|
'’' => '’',
|
|
'‚' => '‚',
|
|
'“' => '“',
|
|
'”' => '”',
|
|
'„' => '„',
|
|
'†' => '†',
|
|
'‡' => '‡',
|
|
'•' => '•',
|
|
'…' => '…',
|
|
'‰' => '‰',
|
|
'′' => '′',
|
|
'″' => '″',
|
|
'‹' => '‹',
|
|
'›' => '›',
|
|
'‾' => '‾',
|
|
'⁄' => '⁄',
|
|
'€' => '€',
|
|
'ℑ' => 'ℑ',
|
|
'℘' => '℘',
|
|
'ℜ' => 'ℜ',
|
|
'™' => '™',
|
|
'ℵ' => 'ℵ',
|
|
'↵' => '↵',
|
|
'⇐' => '⇐',
|
|
'⇑' => '⇑',
|
|
'⇒' => '⇒',
|
|
'⇓' => '⇓',
|
|
'⇔' => '⇔',
|
|
'∀' => '∀',
|
|
'∂' => '∂',
|
|
'∃' => '∃',
|
|
'∅' => '∅',
|
|
'∇' => '∇',
|
|
'∈' => '∈',
|
|
'∉' => '∉',
|
|
'∋' => '∋',
|
|
'∏' => '∏',
|
|
'∑' => '∑',
|
|
'−' => '−',
|
|
'∗' => '∗',
|
|
'√' => '√',
|
|
'∝' => '∝',
|
|
'∞' => '∞',
|
|
'∠' => '∠',
|
|
'∧' => '∧',
|
|
'∨' => '∨',
|
|
'∩' => '∩',
|
|
'∪' => '∪',
|
|
'∫' => '∫',
|
|
'∴' => '∴',
|
|
'∼' => '∼',
|
|
'≅' => '≅',
|
|
'≈' => '≈',
|
|
'≠' => '≠',
|
|
'≡' => '≡',
|
|
'≤' => '≤',
|
|
'≥' => '≥',
|
|
'⊂' => '⊂',
|
|
'⊃' => '⊃',
|
|
'⊄' => '⊄',
|
|
'⊆' => '⊆',
|
|
'⊇' => '⊇',
|
|
'⊕' => '⊕',
|
|
'⊗' => '⊗',
|
|
'⊥' => '⊥',
|
|
'⋅' => '⋅',
|
|
'⌈' => '⌈',
|
|
'⌉' => '⌉',
|
|
'⌊' => '⌊',
|
|
'⌋' => '⌋',
|
|
'⟨' => '〈',
|
|
'⟩' => '〉',
|
|
'←' => '←',
|
|
'↑' => '↑',
|
|
'→' => '→',
|
|
'↓' => '↓',
|
|
'↔' => '↔',
|
|
'◊' => '◊',
|
|
'♠' => '♠',
|
|
'♣' => '♣',
|
|
'♥' => '♥',
|
|
'♦' => '♦'
|
|
);
|
|
|
|
return str_replace( array_keys($to_ncr), array_values($to_ncr), $text );
|
|
}
|
|
|
|
/**
|
|
* Formats text for the rich text editor.
|
|
*
|
|
* The filter 'richedit_pre' is applied here. If $text is empty the filter will
|
|
* be applied to an empty string.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $text The text to be formatted.
|
|
* @return string The formatted text after filter is applied.
|
|
*/
|
|
function wp_richedit_pre($text) {
|
|
// Filtering a blank results in an annoying <br />\n
|
|
if ( empty($text) ) return apply_filters('richedit_pre', '');
|
|
|
|
$output = convert_chars($text);
|
|
$output = wpautop($output);
|
|
$output = htmlspecialchars($output, ENT_NOQUOTES);
|
|
|
|
return apply_filters('richedit_pre', $output);
|
|
}
|
|
|
|
/**
|
|
* Formats text for the HTML editor.
|
|
*
|
|
* Unless $output is empty it will pass through htmlspecialchars before the
|
|
* 'htmledit_pre' filter is applied.
|
|
*
|
|
* @since 2.5.0
|
|
*
|
|
* @param string $output The text to be formatted.
|
|
* @return string Formatted text after filter applied.
|
|
*/
|
|
function wp_htmledit_pre($output) {
|
|
if ( !empty($output) )
|
|
$output = htmlspecialchars($output, ENT_NOQUOTES); // convert only < > &
|
|
|
|
return apply_filters('htmledit_pre', $output);
|
|
}
|
|
|
|
/**
|
|
* Perform a deep string replace operation to ensure the values in $search are no longer present
|
|
*
|
|
* Repeats the replacement operation until it no longer replaces anything so as to remove "nested" values
|
|
* e.g. $subject = '%0%0%0DDD', $search ='%0D', $result ='' rather than the '%0%0DD' that
|
|
* str_replace would return
|
|
*
|
|
* @since 2.8.1
|
|
* @access private
|
|
*
|
|
* @param string|array $search
|
|
* @param string $subject
|
|
* @return string The processed string
|
|
*/
|
|
function _deep_replace( $search, $subject ) {
|
|
$found = true;
|
|
$subject = (string) $subject;
|
|
while ( $found ) {
|
|
$found = false;
|
|
foreach ( (array) $search as $val ) {
|
|
while ( strpos( $subject, $val ) !== false ) {
|
|
$found = true;
|
|
$subject = str_replace( $val, '', $subject );
|
|
}
|
|
}
|
|
}
|
|
|
|
return $subject;
|
|
}
|
|
|
|
/**
|
|
* Escapes data for use in a MySQL query
|
|
*
|
|
* This is just a handy shortcut for $wpdb->escape(), for completeness' sake
|
|
*
|
|
* @since 2.8.0
|
|
* @param string $sql Unescaped SQL data
|
|
* @return string The cleaned $sql
|
|
*/
|
|
function esc_sql( $sql ) {
|
|
global $wpdb;
|
|
return $wpdb->escape( $sql );
|
|
}
|
|
|
|
/**
|
|
* Checks and cleans a URL.
|
|
*
|
|
* A number of characters are removed from the URL. If the URL is for displaying
|
|
* (the default behaviour) ampersands are also replaced. The 'clean_url' filter
|
|
* is applied to the returned cleaned URL.
|
|
*
|
|
* @since 2.8.0
|
|
* @uses wp_kses_bad_protocol() To only permit protocols in the URL set
|
|
* via $protocols or the common ones set in the function.
|
|
*
|
|
* @param string $url The URL to be cleaned.
|
|
* @param array $protocols Optional. An array of acceptable protocols.
|
|
* Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn' if not set.
|
|
* @param string $_context Private. Use esc_url_raw() for database usage.
|
|
* @return string The cleaned $url after the 'clean_url' filter is applied.
|
|
*/
|
|
function esc_url( $url, $protocols = null, $_context = 'display' ) {
|
|
$original_url = $url;
|
|
|
|
if ( '' == $url )
|
|
return $url;
|
|
$url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url);
|
|
$strip = array('%0d', '%0a', '%0D', '%0A');
|
|
$url = _deep_replace($strip, $url);
|
|
$url = str_replace(';//', '://', $url);
|
|
/* If the URL doesn't appear to contain a scheme, we
|
|
* presume it needs http:// appended (unless a relative
|
|
* link starting with /, # or ? or a php file).
|
|
*/
|
|
if ( strpos($url, ':') === false && ! in_array( $url[0], array( '/', '#', '?' ) ) &&
|
|
! preg_match('/^[a-z0-9-]+?\.php/i', $url) )
|
|
$url = 'http://' . $url;
|
|
|
|
// Replace ampersands and single quotes only when displaying.
|
|
if ( 'display' == $_context ) {
|
|
$url = wp_kses_normalize_entities( $url );
|
|
$url = str_replace( '&', '&', $url );
|
|
$url = str_replace( "'", ''', $url );
|
|
}
|
|
|
|
if ( ! is_array( $protocols ) )
|
|
$protocols = wp_allowed_protocols();
|
|
$good_protocol_url = wp_kses_bad_protocol( $url, $protocols );
|
|
if ( strtolower( $good_protocol_url ) != strtolower( $url ) )
|
|
return '';
|
|
|
|
return apply_filters('clean_url', $good_protocol_url, $original_url, $_context);
|
|
}
|
|
|
|
/**
|
|
* Performs esc_url() for database usage.
|
|
*
|
|
* @since 2.8.0
|
|
* @uses esc_url()
|
|
*
|
|
* @param string $url The URL to be cleaned.
|
|
* @param array $protocols An array of acceptable protocols.
|
|
* @return string The cleaned URL.
|
|
*/
|
|
function esc_url_raw( $url, $protocols = null ) {
|
|
return esc_url( $url, $protocols, 'db' );
|
|
}
|
|
|
|
/**
|
|
* Convert entities, while preserving already-encoded entities.
|
|
*
|
|
* @link http://www.php.net/htmlentities Borrowed from the PHP Manual user notes.
|
|
*
|
|
* @since 1.2.2
|
|
*
|
|
* @param string $myHTML The text to be converted.
|
|
* @return string Converted text.
|
|
*/
|
|
function htmlentities2($myHTML) {
|
|
$translation_table = get_html_translation_table( HTML_ENTITIES, ENT_QUOTES );
|
|
$translation_table[chr(38)] = '&';
|
|
return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/", "&", strtr($myHTML, $translation_table) );
|
|
}
|
|
|
|
/**
|
|
* Escape single quotes, htmlspecialchar " < > &, and fix line endings.
|
|
*
|
|
* Escapes text strings for echoing in JS. It is intended to be used for inline JS
|
|
* (in a tag attribute, for example onclick="..."). Note that the strings have to
|
|
* be in single quotes. The filter 'js_escape' is also applied here.
|
|
*
|
|
* @since 2.8.0
|
|
*
|
|
* @param string $text The text to be escaped.
|
|
* @return string Escaped text.
|
|
*/
|
|
function esc_js( $text ) {
|
|
$safe_text = wp_check_invalid_utf8( $text );
|
|
$safe_text = _wp_specialchars( $safe_text, ENT_COMPAT );
|
|
$safe_text = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", stripslashes( $safe_text ) );
|
|
$safe_text = str_replace( "\r", '', $safe_text );
|
|
$safe_text = str_replace( "\n", '\\n', addslashes( $safe_text ) );
|
|
return apply_filters( 'js_escape', $safe_text, $text );
|
|
}
|
|
|
|
/**
|
|
* Escaping for HTML blocks.
|
|
*
|
|
* @since 2.8.0
|
|
*
|
|
* @param string $text
|
|
* @return string
|
|
*/
|
|
function esc_html( $text ) {
|
|
$safe_text = wp_check_invalid_utf8( $text );
|
|
$safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
|
|
return apply_filters( 'esc_html', $safe_text, $text );
|
|
}
|
|
|
|
/**
|
|
* Escaping for HTML attributes.
|
|
*
|
|
* @since 2.8.0
|
|
*
|
|
* @param string $text
|
|
* @return string
|
|
*/
|
|
function esc_attr( $text ) {
|
|
$safe_text = wp_check_invalid_utf8( $text );
|
|
$safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
|
|
return apply_filters( 'attribute_escape', $safe_text, $text );
|
|
}
|
|
|
|
/**
|
|
* Escaping for textarea values.
|
|
*
|
|
* @since 3.1.0
|
|
*
|
|
* @param string $text
|
|
* @return string
|
|
*/
|
|
function esc_textarea( $text ) {
|
|
$safe_text = htmlspecialchars( $text, ENT_QUOTES );
|
|
return apply_filters( 'esc_textarea', $safe_text, $text );
|
|
}
|
|
|
|
/**
|
|
* Escape an HTML tag name.
|
|
*
|
|
* @since 2.5.0
|
|
*
|
|
* @param string $tag_name
|
|
* @return string
|
|
*/
|
|
function tag_escape($tag_name) {
|
|
$safe_tag = strtolower( preg_replace('/[^a-zA-Z0-9_:]/', '', $tag_name) );
|
|
return apply_filters('tag_escape', $safe_tag, $tag_name);
|
|
}
|
|
|
|
/**
|
|
* Escapes text for SQL LIKE special characters % and _.
|
|
*
|
|
* @since 2.5.0
|
|
*
|
|
* @param string $text The text to be escaped.
|
|
* @return string text, safe for inclusion in LIKE query.
|
|
*/
|
|
function like_escape($text) {
|
|
return str_replace(array("%", "_"), array("\\%", "\\_"), $text);
|
|
}
|
|
|
|
/**
|
|
* Convert full URL paths to absolute paths.
|
|
*
|
|
* Removes the http or https protocols and the domain. Keeps the path '/' at the
|
|
* beginning, so it isn't a true relative link, but from the web root base.
|
|
*
|
|
* @since 2.1.0
|
|
*
|
|
* @param string $link Full URL path.
|
|
* @return string Absolute path.
|
|
*/
|
|
function wp_make_link_relative( $link ) {
|
|
return preg_replace( '|https?://[^/]+(/.*)|i', '$1', $link );
|
|
}
|
|
|
|
/**
|
|
* Sanitises various option values based on the nature of the option.
|
|
*
|
|
* This is basically a switch statement which will pass $value through a number
|
|
* of functions depending on the $option.
|
|
*
|
|
* @since 2.0.5
|
|
*
|
|
* @param string $option The name of the option.
|
|
* @param string $value The unsanitised value.
|
|
* @return string Sanitized value.
|
|
*/
|
|
function sanitize_option($option, $value) {
|
|
|
|
switch ( $option ) {
|
|
case 'admin_email' :
|
|
case 'new_admin_email' :
|
|
$value = sanitize_email( $value );
|
|
if ( ! is_email( $value ) ) {
|
|
$value = get_option( $option ); // Resets option to stored value in the case of failed sanitization
|
|
if ( function_exists( 'add_settings_error' ) )
|
|
add_settings_error( $option, 'invalid_admin_email', __( 'The email address entered did not appear to be a valid email address. Please enter a valid email address.' ) );
|
|
}
|
|
break;
|
|
|
|
case 'thumbnail_size_w':
|
|
case 'thumbnail_size_h':
|
|
case 'medium_size_w':
|
|
case 'medium_size_h':
|
|
case 'large_size_w':
|
|
case 'large_size_h':
|
|
case 'mailserver_port':
|
|
case 'comment_max_links':
|
|
case 'page_on_front':
|
|
case 'page_for_posts':
|
|
case 'rss_excerpt_length':
|
|
case 'default_category':
|
|
case 'default_email_category':
|
|
case 'default_link_category':
|
|
case 'close_comments_days_old':
|
|
case 'comments_per_page':
|
|
case 'thread_comments_depth':
|
|
case 'users_can_register':
|
|
case 'start_of_week':
|
|
$value = absint( $value );
|
|
break;
|
|
|
|
case 'posts_per_page':
|
|
case 'posts_per_rss':
|
|
$value = (int) $value;
|
|
if ( empty($value) )
|
|
$value = 1;
|
|
if ( $value < -1 )
|
|
$value = abs($value);
|
|
break;
|
|
|
|
case 'default_ping_status':
|
|
case 'default_comment_status':
|
|
// Options that if not there have 0 value but need to be something like "closed"
|
|
if ( $value == '0' || $value == '')
|
|
$value = 'closed';
|
|
break;
|
|
|
|
case 'blogdescription':
|
|
case 'blogname':
|
|
$value = wp_kses_post( $value );
|
|
$value = esc_html( $value );
|
|
break;
|
|
|
|
case 'blog_charset':
|
|
$value = preg_replace('/[^a-zA-Z0-9_-]/', '', $value); // strips slashes
|
|
break;
|
|
|
|
case 'blog_public':
|
|
// This is the value if the settings checkbox is not checked on POST. Don't rely on this.
|
|
if ( null === $value )
|
|
$value = 1;
|
|
else
|
|
$value = intval( $value );
|
|
break;
|
|
|
|
case 'date_format':
|
|
case 'time_format':
|
|
case 'mailserver_url':
|
|
case 'mailserver_login':
|
|
case 'mailserver_pass':
|
|
case 'upload_path':
|
|
$value = strip_tags( $value );
|
|
$value = wp_kses_data( $value );
|
|
break;
|
|
|
|
case 'ping_sites':
|
|
$value = explode( "\n", $value );
|
|
$value = array_filter( array_map( 'trim', $value ) );
|
|
$value = array_filter( array_map( 'esc_url_raw', $value ) );
|
|
$value = implode( "\n", $value );
|
|
break;
|
|
|
|
case 'gmt_offset':
|
|
$value = preg_replace('/[^0-9:.-]/', '', $value); // strips slashes
|
|
break;
|
|
|
|
case 'siteurl':
|
|
if ( (bool)preg_match( '#http(s?)://(.+)#i', $value) ) {
|
|
$value = esc_url_raw($value);
|
|
} else {
|
|
$value = get_option( $option ); // Resets option to stored value in the case of failed sanitization
|
|
if ( function_exists('add_settings_error') )
|
|
add_settings_error('siteurl', 'invalid_siteurl', __('The WordPress address you entered did not appear to be a valid URL. Please enter a valid URL.'));
|
|
}
|
|
break;
|
|
|
|
case 'home':
|
|
if ( (bool)preg_match( '#http(s?)://(.+)#i', $value) ) {
|
|
$value = esc_url_raw($value);
|
|
} else {
|
|
$value = get_option( $option ); // Resets option to stored value in the case of failed sanitization
|
|
if ( function_exists('add_settings_error') )
|
|
add_settings_error('home', 'invalid_home', __('The Site address you entered did not appear to be a valid URL. Please enter a valid URL.'));
|
|
}
|
|
break;
|
|
|
|
case 'WPLANG':
|
|
$allowed = get_available_languages();
|
|
if ( ! in_array( $value, $allowed ) && ! empty( $value ) )
|
|
$value = get_option( $option );
|
|
break;
|
|
|
|
case 'illegal_names':
|
|
if ( ! is_array( $value ) )
|
|
$value = explode( "\n", $value );
|
|
|
|
$value = array_values( array_filter( array_map( 'trim', $value ) ) );
|
|
|
|
if ( ! $value )
|
|
$value = '';
|
|
break;
|
|
|
|
case 'limited_email_domains':
|
|
case 'banned_email_domains':
|
|
if ( ! is_array( $value ) )
|
|
$value = explode( "\n", $value );
|
|
|
|
$domains = array_values( array_filter( array_map( 'trim', $value ) ) );
|
|
$value = array();
|
|
|
|
foreach ( $domains as $domain ) {
|
|
if ( ! preg_match( '/(--|\.\.)/', $domain ) && preg_match( '|^([a-zA-Z0-9-\.])+$|', $domain ) )
|
|
$value[] = $domain;
|
|
}
|
|
if ( ! $value )
|
|
$value = '';
|
|
break;
|
|
|
|
case 'timezone_string':
|
|
$allowed_zones = timezone_identifiers_list();
|
|
if ( ! in_array( $value, $allowed_zones ) && ! empty( $value ) ) {
|
|
$value = get_option( $option ); // Resets option to stored value in the case of failed sanitization
|
|
if ( function_exists('add_settings_error') )
|
|
add_settings_error('timezone_string', 'invalid_timezone_string', __('The timezone you have entered is not valid. Please select a valid timezone.') );
|
|
}
|
|
break;
|
|
|
|
case 'permalink_structure':
|
|
case 'category_base':
|
|
case 'tag_base':
|
|
$value = esc_url_raw( $value );
|
|
$value = str_replace( 'http://', '', $value );
|
|
break;
|
|
}
|
|
|
|
$value = apply_filters("sanitize_option_{$option}", $value, $option);
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Parses a string into variables to be stored in an array.
|
|
*
|
|
* Uses {@link http://www.php.net/parse_str parse_str()} and stripslashes if
|
|
* {@link http://www.php.net/magic_quotes magic_quotes_gpc} is on.
|
|
*
|
|
* @since 2.2.1
|
|
* @uses apply_filters() for the 'wp_parse_str' filter.
|
|
*
|
|
* @param string $string The string to be parsed.
|
|
* @param array $array Variables will be stored in this array.
|
|
*/
|
|
function wp_parse_str( $string, &$array ) {
|
|
parse_str( $string, $array );
|
|
if ( get_magic_quotes_gpc() )
|
|
$array = stripslashes_deep( $array );
|
|
$array = apply_filters( 'wp_parse_str', $array );
|
|
}
|
|
|
|
/**
|
|
* Convert lone less than signs.
|
|
*
|
|
* KSES already converts lone greater than signs.
|
|
*
|
|
* @uses wp_pre_kses_less_than_callback in the callback function.
|
|
* @since 2.3.0
|
|
*
|
|
* @param string $text Text to be converted.
|
|
* @return string Converted text.
|
|
*/
|
|
function wp_pre_kses_less_than( $text ) {
|
|
return preg_replace_callback('%<[^>]*?((?=<)|>|$)%', 'wp_pre_kses_less_than_callback', $text);
|
|
}
|
|
|
|
/**
|
|
* Callback function used by preg_replace.
|
|
*
|
|
* @uses esc_html to format the $matches text.
|
|
* @since 2.3.0
|
|
*
|
|
* @param array $matches Populated by matches to preg_replace.
|
|
* @return string The text returned after esc_html if needed.
|
|
*/
|
|
function wp_pre_kses_less_than_callback( $matches ) {
|
|
if ( false === strpos($matches[0], '>') )
|
|
return esc_html($matches[0]);
|
|
return $matches[0];
|
|
}
|
|
|
|
/**
|
|
* WordPress implementation of PHP sprintf() with filters.
|
|
*
|
|
* @since 2.5.0
|
|
* @link http://www.php.net/sprintf
|
|
*
|
|
* @param string $pattern The string which formatted args are inserted.
|
|
* @param mixed $args,... Arguments to be formatted into the $pattern string.
|
|
* @return string The formatted string.
|
|
*/
|
|
function wp_sprintf( $pattern ) {
|
|
$args = func_get_args();
|
|
$len = strlen($pattern);
|
|
$start = 0;
|
|
$result = '';
|
|
$arg_index = 0;
|
|
while ( $len > $start ) {
|
|
// Last character: append and break
|
|
if ( strlen($pattern) - 1 == $start ) {
|
|
$result .= substr($pattern, -1);
|
|
break;
|
|
}
|
|
|
|
// Literal %: append and continue
|
|
if ( substr($pattern, $start, 2) == '%%' ) {
|
|
$start += 2;
|
|
$result .= '%';
|
|
continue;
|
|
}
|
|
|
|
// Get fragment before next %
|
|
$end = strpos($pattern, '%', $start + 1);
|
|
if ( false === $end )
|
|
$end = $len;
|
|
$fragment = substr($pattern, $start, $end - $start);
|
|
|
|
// Fragment has a specifier
|
|
if ( $pattern[$start] == '%' ) {
|
|
// Find numbered arguments or take the next one in order
|
|
if ( preg_match('/^%(\d+)\$/', $fragment, $matches) ) {
|
|
$arg = isset($args[$matches[1]]) ? $args[$matches[1]] : '';
|
|
$fragment = str_replace("%{$matches[1]}$", '%', $fragment);
|
|
} else {
|
|
++$arg_index;
|
|
$arg = isset($args[$arg_index]) ? $args[$arg_index] : '';
|
|
}
|
|
|
|
// Apply filters OR sprintf
|
|
$_fragment = apply_filters( 'wp_sprintf', $fragment, $arg );
|
|
if ( $_fragment != $fragment )
|
|
$fragment = $_fragment;
|
|
else
|
|
$fragment = sprintf($fragment, strval($arg) );
|
|
}
|
|
|
|
// Append to result and move to next fragment
|
|
$result .= $fragment;
|
|
$start = $end;
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Localize list items before the rest of the content.
|
|
*
|
|
* The '%l' must be at the first characters can then contain the rest of the
|
|
* content. The list items will have ', ', ', and', and ' and ' added depending
|
|
* on the amount of list items in the $args parameter.
|
|
*
|
|
* @since 2.5.0
|
|
*
|
|
* @param string $pattern Content containing '%l' at the beginning.
|
|
* @param array $args List items to prepend to the content and replace '%l'.
|
|
* @return string Localized list items and rest of the content.
|
|
*/
|
|
function wp_sprintf_l($pattern, $args) {
|
|
// Not a match
|
|
if ( substr($pattern, 0, 2) != '%l' )
|
|
return $pattern;
|
|
|
|
// Nothing to work with
|
|
if ( empty($args) )
|
|
return '';
|
|
|
|
// Translate and filter the delimiter set (avoid ampersands and entities here)
|
|
$l = apply_filters('wp_sprintf_l', array(
|
|
/* translators: used between list items, there is a space after the comma */
|
|
'between' => __(', '),
|
|
/* translators: used between list items, there is a space after the and */
|
|
'between_last_two' => __(', and '),
|
|
/* translators: used between only two list items, there is a space after the and */
|
|
'between_only_two' => __(' and '),
|
|
));
|
|
|
|
$args = (array) $args;
|
|
$result = array_shift($args);
|
|
if ( count($args) == 1 )
|
|
$result .= $l['between_only_two'] . array_shift($args);
|
|
// Loop when more than two args
|
|
$i = count($args);
|
|
while ( $i ) {
|
|
$arg = array_shift($args);
|
|
$i--;
|
|
if ( 0 == $i )
|
|
$result .= $l['between_last_two'] . $arg;
|
|
else
|
|
$result .= $l['between'] . $arg;
|
|
}
|
|
return $result . substr($pattern, 2);
|
|
}
|
|
|
|
/**
|
|
* Safely extracts not more than the first $count characters from html string.
|
|
*
|
|
* UTF-8, tags and entities safe prefix extraction. Entities inside will *NOT*
|
|
* be counted as one character. For example & will be counted as 4, < as
|
|
* 3, etc.
|
|
*
|
|
* @since 2.5.0
|
|
*
|
|
* @param integer $str String to get the excerpt from.
|
|
* @param integer $count Maximum number of characters to take.
|
|
* @return string The excerpt.
|
|
*/
|
|
function wp_html_excerpt( $str, $count ) {
|
|
$str = wp_strip_all_tags( $str, true );
|
|
$str = mb_substr( $str, 0, $count );
|
|
// remove part of an entity at the end
|
|
$str = preg_replace( '/&[^;\s]{0,6}$/', '', $str );
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* Add a Base url to relative links in passed content.
|
|
*
|
|
* By default it supports the 'src' and 'href' attributes. However this can be
|
|
* changed via the 3rd param.
|
|
*
|
|
* @since 2.7.0
|
|
*
|
|
* @param string $content String to search for links in.
|
|
* @param string $base The base URL to prefix to links.
|
|
* @param array $attrs The attributes which should be processed.
|
|
* @return string The processed content.
|
|
*/
|
|
function links_add_base_url( $content, $base, $attrs = array('src', 'href') ) {
|
|
global $_links_add_base;
|
|
$_links_add_base = $base;
|
|
$attrs = implode('|', (array)$attrs);
|
|
return preg_replace_callback( "!($attrs)=(['\"])(.+?)\\2!i", '_links_add_base', $content );
|
|
}
|
|
|
|
/**
|
|
* Callback to add a base url to relative links in passed content.
|
|
*
|
|
* @since 2.7.0
|
|
* @access private
|
|
*
|
|
* @param string $m The matched link.
|
|
* @return string The processed link.
|
|
*/
|
|
function _links_add_base($m) {
|
|
global $_links_add_base;
|
|
//1 = attribute name 2 = quotation mark 3 = URL
|
|
return $m[1] . '=' . $m[2] .
|
|
( preg_match( '#^(\w{1,20}):#', $m[3], $protocol ) && in_array( $protocol[1], wp_allowed_protocols() ) ?
|
|
$m[3] :
|
|
path_join( $_links_add_base, $m[3] ) )
|
|
. $m[2];
|
|
}
|
|
|
|
/**
|
|
* Adds a Target attribute to all links in passed content.
|
|
*
|
|
* This function by default only applies to <a> tags, however this can be
|
|
* modified by the 3rd param.
|
|
*
|
|
* <b>NOTE:</b> Any current target attributed will be stripped and replaced.
|
|
*
|
|
* @since 2.7.0
|
|
*
|
|
* @param string $content String to search for links in.
|
|
* @param string $target The Target to add to the links.
|
|
* @param array $tags An array of tags to apply to.
|
|
* @return string The processed content.
|
|
*/
|
|
function links_add_target( $content, $target = '_blank', $tags = array('a') ) {
|
|
global $_links_add_target;
|
|
$_links_add_target = $target;
|
|
$tags = implode('|', (array)$tags);
|
|
return preg_replace_callback( "!<($tags)(.+?)>!i", '_links_add_target', $content );
|
|
}
|
|
|
|
/**
|
|
* Callback to add a target attribute to all links in passed content.
|
|
*
|
|
* @since 2.7.0
|
|
* @access private
|
|
*
|
|
* @param string $m The matched link.
|
|
* @return string The processed link.
|
|
*/
|
|
function _links_add_target( $m ) {
|
|
global $_links_add_target;
|
|
$tag = $m[1];
|
|
$link = preg_replace('|(target=[\'"](.*?)[\'"])|i', '', $m[2]);
|
|
return '<' . $tag . $link . ' target="' . esc_attr( $_links_add_target ) . '">';
|
|
}
|
|
|
|
/**
|
|
* Normalize EOL characters and strip duplicate whitespace.
|
|
*
|
|
* @since 2.7.0
|
|
*
|
|
* @param string $str The string to normalize.
|
|
* @return string The normalized string.
|
|
*/
|
|
function normalize_whitespace( $str ) {
|
|
$str = trim( $str );
|
|
$str = str_replace( "\r", "\n", $str );
|
|
$str = preg_replace( array( '/\n+/', '/[ \t]+/' ), array( "\n", ' ' ), $str );
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* Properly strip all HTML tags including script and style
|
|
*
|
|
* @since 2.9.0
|
|
*
|
|
* @param string $string String containing HTML tags
|
|
* @param bool $remove_breaks optional Whether to remove left over line breaks and white space chars
|
|
* @return string The processed string.
|
|
*/
|
|
function wp_strip_all_tags($string, $remove_breaks = false) {
|
|
$string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string );
|
|
$string = strip_tags($string);
|
|
|
|
if ( $remove_breaks )
|
|
$string = preg_replace('/[\r\n\t ]+/', ' ', $string);
|
|
|
|
return trim( $string );
|
|
}
|
|
|
|
/**
|
|
* Sanitize a string from user input or from the db
|
|
*
|
|
* check for invalid UTF-8,
|
|
* Convert single < characters to entity,
|
|
* strip all tags,
|
|
* remove line breaks, tabs and extra white space,
|
|
* strip octets.
|
|
*
|
|
* @since 2.9.0
|
|
*
|
|
* @param string $str
|
|
* @return string
|
|
*/
|
|
function sanitize_text_field($str) {
|
|
$filtered = wp_check_invalid_utf8( $str );
|
|
|
|
if ( strpos($filtered, '<') !== false ) {
|
|
$filtered = wp_pre_kses_less_than( $filtered );
|
|
// This will strip extra whitespace for us.
|
|
$filtered = wp_strip_all_tags( $filtered, true );
|
|
} else {
|
|
$filtered = trim( preg_replace('/[\r\n\t ]+/', ' ', $filtered) );
|
|
}
|
|
|
|
$match = array();
|
|
$found = false;
|
|
while ( preg_match('/%[a-f0-9]{2}/i', $filtered, $match) ) {
|
|
$filtered = str_replace($match[0], '', $filtered);
|
|
$found = true;
|
|
}
|
|
|
|
if ( $found ) {
|
|
// Strip out the whitespace that may now exist after removing the octets.
|
|
$filtered = trim( preg_replace('/ +/', ' ', $filtered) );
|
|
}
|
|
|
|
return apply_filters('sanitize_text_field', $filtered, $str);
|
|
}
|
|
|
|
/**
|
|
* i18n friendly version of basename()
|
|
*
|
|
* @since 3.1.0
|
|
*
|
|
* @param string $path A path.
|
|
* @param string $suffix If the filename ends in suffix this will also be cut off.
|
|
* @return string
|
|
*/
|
|
function wp_basename( $path, $suffix = '' ) {
|
|
return urldecode( basename( str_replace( array( '%2F', '%5C' ), '/', urlencode( $path ) ), $suffix ) );
|
|
}
|
|
|
|
/**
|
|
* Forever eliminate "Wordpress" from the planet (or at least the little bit we can influence).
|
|
*
|
|
* Violating our coding standards for a good function name.
|
|
*
|
|
* @since 3.0.0
|
|
*/
|
|
function capital_P_dangit( $text ) {
|
|
// Simple replacement for titles
|
|
if ( 'the_title' === current_filter() )
|
|
return str_replace( 'Wordpress', 'WordPress', $text );
|
|
// Still here? Use the more judicious replacement
|
|
static $dblq = false;
|
|
if ( false === $dblq )
|
|
$dblq = _x( '“', 'opening curly double quote' );
|
|
return str_replace(
|
|
array( ' Wordpress', '‘Wordpress', $dblq . 'Wordpress', '>Wordpress', '(Wordpress' ),
|
|
array( ' WordPress', '‘WordPress', $dblq . 'WordPress', '>WordPress', '(WordPress' ),
|
|
$text );
|
|
|
|
}
|
|
|
|
/**
|
|
* Sanitize a mime type
|
|
*
|
|
* @since 3.1.3
|
|
*
|
|
* @param string $mime_type Mime type
|
|
* @return string Sanitized mime type
|
|
*/
|
|
function sanitize_mime_type( $mime_type ) {
|
|
$sani_mime_type = preg_replace( '/[^-+*.a-zA-Z0-9\/]/', '', $mime_type );
|
|
return apply_filters( 'sanitize_mime_type', $sani_mime_type, $mime_type );
|
|
}
|
|
|
|
/**
|
|
* Sanitize space or carriage return separated URLs that are used to send trackbacks.
|
|
*
|
|
* @since 3.4.0
|
|
*
|
|
* @param string $to_ping Space or carriage return separated URLs
|
|
* @return string URLs starting with the http or https protocol, separated by a carriage return.
|
|
*/
|
|
function sanitize_trackback_urls( $to_ping ) {
|
|
$urls_to_ping = preg_split( '/[\r\n\t ]/', trim( $to_ping ), -1, PREG_SPLIT_NO_EMPTY );
|
|
foreach ( $urls_to_ping as $k => $url ) {
|
|
if ( !preg_match( '#^https?://.#i', $url ) )
|
|
unset( $urls_to_ping[$k] );
|
|
}
|
|
$urls_to_ping = array_map( 'esc_url_raw', $urls_to_ping );
|
|
$urls_to_ping = implode( "\n", $urls_to_ping );
|
|
return apply_filters( 'sanitize_trackback_urls', $urls_to_ping, $to_ping );
|
|
}
|
|
|
|
/**
|
|
* Conditionally add slashes to a string or array of strings. When GPCS
|
|
* slashing is turned on, slashes are added. When GPCS slashing is turned off,
|
|
* slashes are not added.
|
|
*
|
|
* This should be used when preparing data for core API that deal directly with GPCS data.
|
|
* Outside of unit tests, this should be rare. At a future date GPCS will no longer
|
|
* be slashed and this function will noop. Do not use it in situations where adding slashes
|
|
* is always required regardless of whether GPCS is slashed.
|
|
*
|
|
* @since 3.6.0
|
|
*
|
|
* @param string|array $value String or array of strings to slash.
|
|
* @return string|array Slashed $value
|
|
*/
|
|
function wp_slash( $value ) {
|
|
if ( is_array( $value ) ) {
|
|
foreach ( $value as $k => $v ) {
|
|
if ( is_array( $v ) ) {
|
|
$value[$k] = wp_slash( $v );
|
|
} else {
|
|
$value[$k] = addslashes( $v );
|
|
}
|
|
}
|
|
} else {
|
|
$value = addslashes( $value );
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Conditionally removes slashes from a string or array of strings. When GPCS
|
|
* slashing is turned on, slashes are stripped. When GPCS slashing is turned off,
|
|
* slashes are not stripped.
|
|
*
|
|
* This should be used for GPCS data before passing it along to core API. At a future
|
|
* date GPCS will no longer be slashed and this function will noop. Do not use it
|
|
* in situations where slash stripping is always required regardless of whether GPCS
|
|
* is slashed.
|
|
*
|
|
* @since 3.6.0
|
|
*
|
|
* @param string|array $value String or array of strings to unslash.
|
|
* @return string|array Unslashed $value
|
|
*/
|
|
function wp_unslash( $value ) {
|
|
return stripslashes_deep( $value );
|
|
}
|