X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/5aa86a9053fb0fa15846bb60aac2fb8fdfff524a..256a3b381f63716209b3527d0a14442ae570c283:/wp-includes/formatting.php diff --git a/wp-includes/formatting.php b/wp-includes/formatting.php index 3452ed2a..c56d8201 100644 --- a/wp-includes/formatting.php +++ b/wp-includes/formatting.php @@ -5,7 +5,7 @@ * Handles many functions for formatting output. * * @package WordPress - **/ + */ /** * Replaces common plain text characters into formatted entities @@ -28,18 +28,33 @@ */ function wptexturize($text) { global $wp_cockneyreplace; - static $opening_quote, $closing_quote, $en_dash, $em_dash, $default_no_texturize_tags, $default_no_texturize_shortcodes, $static_characters, $static_replacements, $dynamic_characters, $dynamic_replacements; + 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 ( empty( $opening_quote ) ) { - /* translators: opening curly quote */ - $opening_quote = _x('“', 'opening curly quote'); - /* translators: closing curly quote */ - $closing_quote = _x('”', 'closing curly quote'); + 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'); + $en_dash = _x( '–', 'en dash' ); /* translators: em dash */ - $em_dash = _x('—', 'em dash'); + $em_dash = _x( '—', 'em dash' ); $default_no_texturize_tags = array('pre', 'code', 'kbd', 'style', 'script', 'tt'); $default_no_texturize_shortcodes = array('code'); @@ -48,22 +63,60 @@ function wptexturize($text) { 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 = array("'tain't","'twere","'twas","'tis","'twill","'til","'bout","'nuff","'round","'cause"); - $cockneyreplace = array("’tain’t","’twere","’twas","’tis","’twill","’til","’bout","’nuff","’round","’cause"); + $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 ); + $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_characters = array('/\'(\d\d(?:’|\')?s)/', '/\'(\d)/', '/(\s|\A|[([{<]|")\'/', '/(\d)"/', '/(\d)\'/', '/(\S)\'([^\'\s])/', '/(\s|\A|[([{<])"(?!\s)/', '/"(\s|\S|\Z)/', '/\'([\s.]|\Z)/', '/\b(\d+)x(\d+)\b/'); - $dynamic_replacements = array('’$1','’$1', '$1‘', '$1″', '$1′', '$1’$2', '$1' . $opening_quote . '$2', $closing_quote . '$1', '’$1', '$1×$2'); + $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 everytime 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) ) . ')'; + // Must do this every time in case plugins use these filters in a context sensitive manner + /** + * Filter the list of HTML elements not to texturize. + * + * @since 2.8.0 + * + * @param array $default_no_texturize_tags An array of HTML element names. + */ + $no_texturize_tags = '(' . implode( '|', apply_filters( 'no_texturize_tags', $default_no_texturize_tags ) ) . ')'; + /** + * Filter the list of shortcodes not to texturize. + * + * @since 2.8.0 + * + * @param array $default_no_texturize_shortcodes An array of shortcode names. + */ + $no_texturize_shortcodes = '(' . implode( '|', apply_filters( 'no_texturize_shortcodes', $default_no_texturize_shortcodes ) ) . ')'; $no_texturize_tags_stack = array(); $no_texturize_shortcodes_stack = array(); @@ -95,15 +148,14 @@ function wptexturize($text) { * 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. * - * @access private * @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 $opening Tag closing character - * @return object + * @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 @@ -133,30 +185,6 @@ function _wptexturize_pushpop_element($text, &$stack, $disabled_elements, $openi } } -/** - * Accepts matches array from preg_replace_callback in wpautop() or a string. - * - * Ensures that the contents of a <
>...<
> HTML block are not - * converted into paragraphs or line-breaks. - * - * @since 1.2.0 - * - * @param array|string $matches The array or string - * @return string The pre block without paragraph/line-break conversion. - */ -function clean_pre($matches) { - if ( is_array($matches) ) - $text = $matches[1] . $matches[2] . ""; - else - $text = $matches; - - $text = str_replace('
', '', $text); - $text = str_replace('

', "\n", $text); - $text = str_replace('

', '', $text); - - return $text; -} - /** * Replaces double line-breaks with paragraph elements. * @@ -168,17 +196,45 @@ function clean_pre($matches) { * @since 0.71 * * @param string $pee The text which has to be formatted. - * @param int|bool $br Optional. If set, this will convert all remaining line-breaks after paragraphing. Default true. + * @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 = 1) { +function wpautop($pee, $br = true) { + $pre_tags = array(); if ( trim($pee) === '' ) return ''; + $pee = $pee . "\n"; // just to make things a little easier, pad the end + + if ( strpos($pee, '', $pee ); + $last_pee = array_pop($pee_parts); + $pee = ''; + $i = 0; + + foreach ( $pee_parts as $pee_part ) { + $start = strpos($pee_part, ''; + + $pee .= substr( $pee_part, 0, $start ) . $name; + $i++; + } + + $pee .= $last_pee; + } + $pee = preg_replace('|
\s*
|', "\n\n", $pee); // Space things out a little - $allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|option|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)'; + $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('!()!', "$1\n\n", $pee); $pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines @@ -200,17 +256,18 @@ function wpautop($pee, $br = 1) { $pee = str_replace('

', '

', $pee); $pee = preg_replace('!

\s*(]*>)!', "$1", $pee); $pee = preg_replace('!(]*>)\s*

!', "$1", $pee); - if ($br) { + if ( $br ) { $pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', '_autop_newline_preservation_helper', $pee); $pee = preg_replace('|(?)\s*\n|', "
\n", $pee); // optionally make line breaks $pee = str_replace('', "\n", $pee); } $pee = preg_replace('!(]*>)\s*
!', "$1", $pee); $pee = preg_replace('!
(\s*]*>)!', '$1', $pee); - if (strpos($pee, ']*>)(.*?)!is', 'clean_pre', $pee ); $pee = preg_replace( "|\n

$|", '

', $pee ); + if ( !empty($pre_tags) ) + $pee = str_replace(array_keys($pre_tags), array_values($pre_tags), $pee); + return $pee; } @@ -219,8 +276,9 @@ function wpautop($pee, $br = 1) { * * @since 3.1.0 * @access private + * * @param array $matches preg_replace_callback matches array - * @returns string + * @return string */ function _autop_newline_preservation_helper( $matches ) { return str_replace("\n", "", $matches[0]); @@ -252,7 +310,7 @@ function shortcode_unautop( $pee ) { . '(' // 1: The shortcode . '\\[' // Opening bracket . "($tagregexp)" // 2: Shortcode name - . '\\b' // Word boundary + . '(?![\\w-])' // Not followed by word character or hyphen // Unroll the loop: Inside the opening shortcode tag . '[^\\]\\/]*' // Not a closing bracket or forward slash . '(?:' @@ -320,6 +378,7 @@ function seems_utf8($str) { * ", 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. @@ -399,7 +458,7 @@ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = fals * $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 + * @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. @@ -456,7 +515,7 @@ function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) { /** * Checks for invalid UTF8 in a string. * - * @since 2.8 + * @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. @@ -671,7 +730,96 @@ function remove_accents($string) { // Euro Sign chr(226).chr(130).chr(172) => 'E', // GBP (Pound) Sign - chr(194).chr(163) => ''); + 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'; + } elseif ( 'da_DK' === $locale ) { + $chars[ chr(195).chr(134) ] = 'Ae'; + $chars[ chr(195).chr(166) ] = 'ae'; + $chars[ chr(195).chr(152) ] = 'Oe'; + $chars[ chr(195).chr(184) ] = 'oe'; + $chars[ chr(195).chr(133) ] = 'Aa'; + $chars[ chr(195).chr(165) ] = 'aa'; + } $string = strtr($string, $chars); } else { @@ -699,12 +847,12 @@ function remove_accents($string) { } /** - * Sanitizes a filename replacing whitespace with dashes + * 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. Trim period, dash and underscore from beginning + * dashes with a single dash. Trims period, dash and underscore from beginning * and end of filename. * * @since 2.1.0 @@ -715,7 +863,15 @@ function remove_accents($string) { 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); + /** + * Filter the list of characters to remove from a filename. + * + * @since 2.8.0 + * + * @param array $special_chars Characters to remove. + * @param string $filename_raw Filename as it was passed into sanitize_file_name(). + */ + $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, '.-_'); @@ -724,16 +880,27 @@ function sanitize_file_name( $filename ) { $parts = explode('.', $filename); // Return if only one extension - if ( count($parts) <= 2 ) - return apply_filters('sanitize_file_name', $filename, $filename_raw); + if ( count( $parts ) <= 2 ) { + /** + * Filter a sanitized filename string. + * + * @since 2.8.0 + * + * @param string $filename Sanitized filename. + * @param string $filename_raw The filename prior to sanitization. + */ + 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. + /* + * Loop over any intermediate extensions. Postfix 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; @@ -751,12 +918,12 @@ function sanitize_file_name( $filename ) { } } $filename .= '.' . $extension; - + /** This filter is documented in wp-includes/formatting.php */ return apply_filters('sanitize_file_name', $filename, $filename_raw); } /** - * Sanitize username stripping out unsafe characters. + * 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, @@ -764,8 +931,6 @@ function sanitize_file_name( $filename ) { * 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. @@ -787,11 +952,20 @@ function sanitize_user( $username, $strict = false ) { // Consolidate contiguous whitespace $username = preg_replace( '|\s+|', ' ', $username ); + /** + * Filter a sanitized username string. + * + * @since 2.0.1 + * + * @param string $username Sanitized username. + * @param string $raw_username The username prior to sanitization. + * @param bool $strict Whether to limit the sanitization to specific characters. Default false. + */ return apply_filters( 'sanitize_user', $username, $raw_username, $strict ); } /** - * Sanitize a string key. + * Sanitizes a string key. * * Keys are used as internal identifiers. Lowercase alphanumeric characters, dashes and underscores are allowed. * @@ -804,11 +978,20 @@ function sanitize_key( $key ) { $raw_key = $key; $key = strtolower( $key ); $key = preg_replace( '/[^a-z0-9_\-]/', '', $key ); + + /** + * Filter a sanitized key string. + * + * @since 3.0.0 + * + * @param string $key Sanitized key. + * @param string $raw_key The key prior to sanitization. + */ return apply_filters( 'sanitize_key', $key, $raw_key ); } /** - * Sanitizes title or use fallback title. + * 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 @@ -821,13 +1004,22 @@ function sanitize_key( $key ) { * @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') { +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); + /** + * Filter a sanitized title string. + * + * @since 1.2.0 + * + * @param string $title Sanitized title. + * @param string $raw_title The title prior to sanitization. + * @param string $context The context for which the title is being sanitized. + */ + $title = apply_filters( 'sanitize_title', $title, $raw_title, $context ); if ( '' === $title || false === $title ) $title = $fallback_title; @@ -835,12 +1027,23 @@ function sanitize_title($title, $fallback_title = '', $context = 'save') { return $title; } -function sanitize_title_for_query($title) { - return sanitize_title($title, '', 'query'); +/** + * 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 title, replacing whitespace and a few other characters with dashes. + * 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. @@ -852,7 +1055,7 @@ function sanitize_title_for_query($title) { * @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') { +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); @@ -873,16 +1076,28 @@ function sanitize_title_with_dashes($title, $raw_title = '', $context = 'display $title = str_replace('.', '-', $title); if ( 'save' == $context ) { - // nbsp, ndash and mdash + // Convert nbsp, ndash and mdash to hyphens $title = str_replace( array( '%c2%a0', '%e2%80%93', '%e2%80%94' ), '-', $title ); - // iexcl and iquest - $title = str_replace( array( '%c2%a1', '%c2%bf' ), '', $title ); - // angle quotes - $title = str_replace( array( '%c2%ab', '%c2%bb', '%e2%80%b9', '%e2%80%ba' ), '', $title ); - // curly quotes - $title = str_replace( array( '%e2%80%98', '%e2%80%99', '%e2%80%9c', '%e2%80%9d' ), '', $title ); - // copy, reg, deg, hellip and trade - $title = str_replace( array( '%c2%a9', '%c2%ae', '%c2%b0', '%e2%80%a6', '%e2%84%a2' ), '', $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); @@ -902,7 +1117,7 @@ function sanitize_title_with_dashes($title, $raw_title = '', $context = 'display * @since 2.5.1 * * @param string $orderby Order by string to be checked. - * @return string|false Returns the order by clause if it is a match, false otherwise. + * @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); @@ -912,7 +1127,7 @@ function sanitize_sql_orderby( $orderby ){ } /** - * Santizes a html classname to ensure it only contains valid characters + * 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. @@ -936,6 +1151,15 @@ function sanitize_html_class( $class, $fallback = '' ) { if ( '' == $sanitized ) $sanitized = $fallback; + /** + * Filter a sanitized HTML class string. + * + * @since 2.8.0 + * + * @param string $sanitized The sanitized HTML class. + * @param string $class HTML class before sanitization. + * @param string $fallback The fallback string. + */ return apply_filters( 'sanitize_html_class', $sanitized, $class, $fallback ); } @@ -972,7 +1196,7 @@ function convert_chars($content, $deprecated = '') { '‹' => '‹', 'Œ' => 'Œ', '' => '', - 'Ž' => 'ž', + 'Ž' => 'Ž', '' => '', '' => '', '‘' => '‘', @@ -988,7 +1212,7 @@ function convert_chars($content, $deprecated = '') { '›' => '›', 'œ' => 'œ', '' => '', - 'ž' => '', + 'ž' => 'ž', 'Ÿ' => 'Ÿ' ); @@ -1010,22 +1234,20 @@ function convert_chars($content, $deprecated = '') { } /** - * Will only balance the tags if forced to and the option is set to balance tags. - * - * The option 'use_balanceTags' is used for whether the tags will be balanced. - * Both the $force parameter and 'use_balanceTags' option will have to be true - * before the tags will be balanced. + * 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 Forces balancing, ignoring the value of the option. Default false. + * @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') == 0 ) + if ( $force || get_option('use_balanceTags') == 1 ) { + return force_balance_tags( $text ); + } else { return $text; - return force_balance_tags( $text ); + } } /** @@ -1051,8 +1273,10 @@ function force_balance_tags( $text ) { $stacksize = 0; $tagqueue = ''; $newtext = ''; - $single_tags = array( 'br', 'hr', 'img', 'input' ); // Known single-entity/self-closing tags - $nestable_tags = array( 'blockquote', 'div', 'span', 'q' ); // Tags that can be immediately nested within themselves + // 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); @@ -1099,26 +1323,35 @@ function force_balance_tags( $text ) { // Tag Cleaning - // If self-closing or '', don't do anything. - if ( substr($regex[2],-1) == '/' || $tag == '' ) { + // 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 ) ) . "> 0 && !in_array($tag, $nestable_tags) && $tagstack[$stacksize - 1] == $tag ) { - $tagqueue = ''; + $tagqueue = ''; $stacksize--; } - $stacksize = array_push ($tagstack, $tag); + $stacksize = array_push( $tagstack, $tag ); } // Attributes $attributes = $regex[2]; - if( !empty($attributes) ) - $attributes = ' '.$attributes; + if( ! empty( $attributes ) && $attributes[0] != '>' ) + $attributes = ' ' . $attributes; $tag = '<' . $tag . $attributes . '>'; //If already queuing a close tag, then put this tag on, too @@ -1151,17 +1384,24 @@ function force_balance_tags( $text ) { /** * Acts on text which is about to be edited. * - * Unless $richedit is set, it is simply a holder for the 'format_to_edit' - * filter. If $richedit is set true htmlspecialchars(), through esc_textarea(), - * will be run on the content, converting special characters to HTML entities. + * 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 pass through htmlspecialchars(). Default false. + * @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 ) { + /** + * Filter the text to be formatted for editing. + * + * @since 1.2.0 + * + * @param string $content The text, prior to formatting for editing. + */ $content = apply_filters( 'format_to_edit', $content ); if ( ! $richedit ) $content = esc_textarea( $content ); @@ -1177,7 +1417,14 @@ function format_to_edit( $content, $richedit = false ) { * @return string Text returned from the 'format_to_post' filter. */ function format_to_post($content) { - $content = apply_filters('format_to_post', $content); + /** + * Filter the string returned by format_to_post(). + * + * @since 1.2.0 + * + * @param string $content The string to format. + */ + $content = apply_filters( 'format_to_post', $content ); return $content; } @@ -1211,9 +1458,9 @@ function zeroise($number, $threshold) { * @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; + if ( isset( $string[0] ) && $string[0] >= '0' && $string[0] <= '9' ) + $string = '\\\\' . $string; + return addcslashes( $string, 'A..Za..z' ); } /** @@ -1265,7 +1512,7 @@ function addslashes_gpc($gpc) { if ( get_magic_quotes_gpc() ) $gpc = stripslashes($gpc); - return esc_sql($gpc); + return wp_slash($gpc); } /** @@ -1276,8 +1523,8 @@ function addslashes_gpc($gpc) { * * @since 2.0.0 * - * @param array|string $value The array or string to be stripped. - * @return array|string Stripped array (or string in the callback). + * @param mixed $value The value to be stripped. + * @return mixed Stripped value. */ function stripslashes_deep($value) { if ( is_array($value) ) { @@ -1287,7 +1534,7 @@ function stripslashes_deep($value) { foreach ($vars as $key=>$data) { $value->{$key} = stripslashes_deep( $data ); } - } else { + } elseif ( is_string( $value ) ) { $value = stripslashes($value); } @@ -1297,8 +1544,6 @@ function stripslashes_deep($value) { /** * Navigates through an array and encodes the values to be used in a URL. * - * Uses a callback to pass the value of the array back to the function as a - * string. * * @since 2.2.0 * @@ -1310,30 +1555,43 @@ function urlencode_deep($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. + * @param string $email_address Email address. + * @param int $hex_encoding Optional. Set to 1 to enable hex 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); +function antispambot( $email_address, $hex_encoding = 0 ) { + $email_no_spam_address = ''; + for ( $i = 0; $i < strlen( $email_address ); $i++ ) { + $j = rand( 0, 1 + $hex_encoding ); + if ( $j == 0 ) { + $email_no_spam_address .= '&#' . ord( $email_address[$i] ) . ';'; + } elseif ( $j == 1 ) { + $email_no_spam_address .= $email_address[$i]; + } elseif ( $j == 2 ) { + $email_no_spam_address .= '%' . zeroise( dechex( ord( $email_address[$i] ) ), 2 ); } } - $emailNOSPAMaddy = str_replace('@','@',$emailNOSPAMaddy); - return $emailNOSPAMaddy; + + $email_no_spam_address = str_replace( '@', '@', $email_no_spam_address ); + + return $email_no_spam_address; } /** @@ -1350,9 +1608,17 @@ function antispambot($emailaddy, $mailto=0) { */ function _make_url_clickable_cb($matches) { $url = $matches[2]; - $suffix = ''; - /** Include parentheses in the URL only if paired **/ + 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, ')' ) ); @@ -1418,23 +1684,124 @@ function _make_email_clickable_cb($matches) { * * @since 0.71 * - * @param string $ret Content to convert URIs. + * @param string $text Content to convert URIs. * @return string Content with converted URIs. */ -function make_clickable($ret) { - $ret = ' ' . $ret; - // in testing, using arrays here was found to be faster - $save = @ini_set('pcre.recursion_limit', 10000); - $retval = preg_replace_callback('#(?])(\()?([\w]+?://(?:[\w\\x80-\\xff\#%~/?@\[\]-]{1,2000}|[\'*(+.,;:!=&$](?![\b\)]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:]|$)|\)))+)#is', '_make_url_clickable_cb', $ret); - if (null !== $retval ) - $ret = $retval; - @ini_set('pcre.recursion_limit', $save); - $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); - // this one is not in an array because we need it to run last, for cleanup of accidental links within links - $ret = preg_replace("#(]+?>|>))]+?>([^>]+?)#i", "$1$3", $ret); - $ret = trim($ret); - return $ret; +function make_clickable( $text ) { + $r = ''; + $textarr = preg_split( '/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // split out HTML tags + $nested_code_pre = 0; // Keep track of how many levels link is nested inside
 or 
+	foreach ( $textarr as $piece ) {
+
+		if ( preg_match( '|^]|i', $piece ) || preg_match( '|^]|i', $piece ) )
+			$nested_code_pre++;
+		elseif ( ( '' === strtolower( $piece ) || '
' === strtolower( $piece ) ) && $nested_code_pre ) + $nested_code_pre--; + + if ( $nested_code_pre || 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( '#(]+?>|>))]+?>([^>]+?)#i', "$1$3", $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) + * + * + * _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 + * ); + * + * + * @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; } /** @@ -1449,12 +1816,12 @@ function wp_rel_nofollow( $text ) { // This is a pre save filter, so text is already escaped. $text = stripslashes($text); $text = preg_replace_callback('||i', 'wp_rel_nofollow_callback', $text); - $text = esc_sql($text); + $text = wp_slash($text); return $text; } /** - * Callback to used to add rel=nofollow string to HTML A element. + * 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. @@ -1473,29 +1840,37 @@ function wp_rel_nofollow_callback( $matches ) { /** * Convert one smiley code to the icon graphic file equivalent. * + * Callback handler for {@link convert_smilies()}. * Looks up one smiley code in the $wpsmiliestrans global array and returns an * string for that smiley. * * @global array $wpsmiliestrans * @since 2.8.0 * - * @param string $smiley Smiley code to convert to image. + * @param array $matches Single match. Smiley code to convert to image. * @return string Image string for smiley. */ -function translate_smiley($smiley) { +function translate_smiley( $matches ) { global $wpsmiliestrans; - if (count($smiley) == 0) { + if ( count( $matches ) == 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()); + $smiley = trim( reset( $matches ) ); + $img = $wpsmiliestrans[ $smiley ]; + + /** + * Filter the Smiley image URL before it's used in the image element. + * + * @since 2.9.0 + * + * @param string $smiley_url URL for the smiley image. + * @param string $img Filename for the smiley image. + * @param string $site_url Site URL, as returned by site_url(). + */ + $src_url = apply_filters( 'smilies_src', includes_url( "images/smilies/$img" ), $img, site_url() ); - return " $smiley_masked "; + return sprintf( ' %s ', esc_url( $src_url ), esc_attr( $smiley ) ); } /** @@ -1510,18 +1885,36 @@ function translate_smiley($smiley) { * @param string $text Content to convert smilies from text. * @return string Converted content with text smilies replaced with images. */ -function convert_smilies($text) { +function convert_smilies( $text ) { global $wp_smiliessearch; $output = ''; - if ( get_option('use_smilies') && !empty($wp_smiliessearch) ) { + 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++) { + $textarr = preg_split( '/(<.*>)/U', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // capture the tags as well as in between + $stop = count( $textarr );// loop stuff + + // Ignore proessing of specific tags + $tags_to_ignore = 'code|pre|style|script|textarea'; + $ignore_block_element = ''; + + 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); + + // If we're in an ignore block, wait until we find its closing tag + if ( '' == $ignore_block_element && preg_match( '/^<(' . $tags_to_ignore . ')>/', $content, $matches ) ) { + $ignore_block_element = $matches[1]; + } + + // If it's not a tag and not in ignore block + if ( '' == $ignore_block_element && strlen( $content ) > 0 && '<' != $content[0] ) { + $content = preg_replace_callback( $wp_smiliessearch, 'translate_smiley', $content ); + } + + // did we exit ignore block + if ( '' != $ignore_block_element && '' == $content ) { + $ignore_block_element = ''; } + $output .= $content; } } else { @@ -1548,11 +1941,26 @@ function is_email( $email, $deprecated = false ) { // Test for the minimum length the email can be if ( strlen( $email ) < 3 ) { + /** + * Filter whether an email address is valid. + * + * This filter is evaluated under several different contexts, such as 'email_too_short', + * 'email_no_at', 'local_invalid_chars', 'domain_period_sequence', 'domain_period_limits', + * 'domain_no_periods', 'sub_hyphen_limits', 'sub_invalid_chars', or no specific context. + * + * @since 2.8.0 + * + * @param bool $is_email Whether the email address has passed the is_email() checks. Default false. + * @param string $email The email address being checked. + * @param string $message An explanatory message to the user. + * @param string $context Context under which the email was tested. + */ return apply_filters( 'is_email', false, $email, 'email_too_short' ); } // Test for an @ character after the first position if ( strpos( $email, '@', 1 ) === false ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'is_email', false, $email, 'email_no_at' ); } @@ -1562,17 +1970,20 @@ function is_email( $email, $deprecated = false ) { // LOCAL PART // Test for invalid characters if ( !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local ) ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'is_email', false, $email, 'local_invalid_chars' ); } // DOMAIN PART // Test for sequences of periods if ( preg_match( '/\.{2,}/', $domain ) ) { + /** This filter is documented in wp-includes/formatting.php */ 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 ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'is_email', false, $email, 'domain_period_limits' ); } @@ -1581,6 +1992,7 @@ function is_email( $email, $deprecated = false ) { // Assume the domain will have at least two subs if ( 2 > count( $subs ) ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'is_email', false, $email, 'domain_no_periods' ); } @@ -1588,16 +2000,19 @@ function is_email( $email, $deprecated = false ) { foreach ( $subs as $sub ) { // Test for leading and trailing hyphens and whitespace if ( trim( $sub, " \t\n\r\0\x0B-" ) !== $sub ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'is_email', false, $email, 'sub_hyphen_limits' ); } // Test for invalid characters if ( !preg_match('/^[a-z0-9-]+$/i', $sub ) ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'is_email', false, $email, 'sub_invalid_chars' ); } } // Congratulations your email made it! + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'is_email', $email, $email, null ); } @@ -1605,7 +2020,6 @@ function is_email( $email, $deprecated = false ) { * Convert to ASCII from email subjects. * * @since 1.2.0 - * @usedby wp_mail() handles charsets in email subjects * * @param string $string Subject line * @return string Converted string to ASCII @@ -1622,11 +2036,13 @@ function wp_iso_descrambler($string) { } /** - * Helper function to convert hex encoded chars to ascii + * Helper function to convert hex encoded chars to ASCII * * @since 3.1.0 * @access private - * @param array $match the preg_replace_callback matches array + * + * @param array $match The preg_replace_callback matches array + * @return array Converted chars */ function _wp_iso_convert( $match ) { return chr( hexdec( strtolower( $match[1] ) ) ); @@ -1635,33 +2051,31 @@ function _wp_iso_convert( $match ) { /** * 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. + * Requires and returns a date in the Y-m-d H:i:s format. If there is a + * timezone_string available, the date is assumed to be in that timezone, + * otherwise it simply subtracts the value of the 'gmt_offset' option. Return + * format can be overridden using the $format parameter. * * @since 1.2.0 * - * @uses get_option() to retrieve the the value of 'gmt_offset'. + * @uses get_option() to retrieve 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); - $tz = get_option('timezone_string'); +function get_gmt_from_date( $string, $format = 'Y-m-d H:i:s' ) { + $tz = get_option( 'timezone_string' ); if ( $tz ) { - date_default_timezone_set( $tz ); - $datetime = new DateTime( $string ); - $datetime->setTimezone( new DateTimeZone('UTC') ); - $offset = $datetime->getOffset(); - $datetime->modify( '+' . $offset / 3600 . ' hours'); - $string_gmt = gmdate($format, $datetime->format('U')); - - date_default_timezone_set('UTC'); + $datetime = date_create( $string, new DateTimeZone( $tz ) ); + if ( ! $datetime ) + return gmdate( $format, 0 ); + $datetime->setTimezone( new DateTimeZone( 'UTC' ) ); + $string_gmt = $datetime->format( $format ); } 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') * 3600); + if ( ! 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 ) ) + return gmdate( $format, 0 ); + $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; } @@ -1669,19 +2083,31 @@ function get_gmt_from_date($string, $format = 'Y-m-d H:i:s') { /** * 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 + * Requires and returns a date in the Y-m-d H:i:s format. If there is a + * timezone_string available, the returned date is in that timezone, otherwise + * it 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. + * @return string Formatted date relative to the timezone / 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')*3600); +function get_date_from_gmt( $string, $format = 'Y-m-d H:i:s' ) { + $tz = get_option( 'timezone_string' ); + if ( $tz ) { + $datetime = date_create( $string, new DateTimeZone( 'UTC' ) ); + if ( ! $datetime ) + return date( $format, 0 ); + $datetime->setTimezone( new DateTimeZone( $tz ) ); + $string_localtime = $datetime->format( $format ); + } else { + if ( ! 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) ) + return date( $format, 0 ); + $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; } @@ -1701,7 +2127,7 @@ function iso8601_timezone_to_offset($timezone) { $sign = (substr($timezone, 0, 1) == '+') ? 1 : -1; $hours = intval(substr($timezone, 1, 2)); $minutes = intval(substr($timezone, 3, 4)) / 60; - $offset = $sign * 3600 * ($hours + $minutes); + $offset = $sign * HOUR_IN_SECONDS * ($hours + $minutes); } return $offset; } @@ -1725,7 +2151,7 @@ function iso8601_to_datetime($date_string, $timezone = 'user') { 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 = 3600 * get_option('gmt_offset'); + $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]); @@ -1766,11 +2192,25 @@ function popuplinks($text) { function sanitize_email( $email ) { // Test for the minimum length the email can be if ( strlen( $email ) < 3 ) { + /** + * Filter a sanitized email address. + * + * This filter is evaluated under several contexts, including 'email_too_short', + * 'email_no_at', 'local_invalid_chars', 'domain_period_sequence', 'domain_period_limits', + * 'domain_no_periods', 'domain_no_valid_subs', or no context. + * + * @since 2.8.0 + * + * @param string $email The sanitized email address. + * @param string $email The email address, as provided to sanitize_email(). + * @param string $message A message to pass to the user. + */ return apply_filters( 'sanitize_email', '', $email, 'email_too_short' ); } // Test for an @ character after the first position if ( strpos( $email, '@', 1 ) === false ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'sanitize_email', '', $email, 'email_no_at' ); } @@ -1781,6 +2221,7 @@ function sanitize_email( $email ) { // Test for invalid characters $local = preg_replace( '/[^a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]/', '', $local ); if ( '' === $local ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'sanitize_email', '', $email, 'local_invalid_chars' ); } @@ -1788,12 +2229,14 @@ function sanitize_email( $email ) { // Test for sequences of periods $domain = preg_replace( '/\.{2,}/', '', $domain ); if ( '' === $domain ) { + /** This filter is documented in wp-includes/formatting.php */ 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 ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'sanitize_email', '', $email, 'domain_period_limits' ); } @@ -1802,6 +2245,7 @@ function sanitize_email( $email ) { // Assume the domain will have at least two subs if ( 2 > count( $subs ) ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'sanitize_email', '', $email, 'domain_no_periods' ); } @@ -1824,6 +2268,7 @@ function sanitize_email( $email ) { // If there aren't 2 or more valid subs if ( 2 > count( $new_subs ) ) { + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'sanitize_email', '', $email, 'domain_no_valid_subs' ); } @@ -1834,6 +2279,7 @@ function sanitize_email( $email ) { $email = $local . '@' . $domain; // Congratulations your email made it! + /** This filter is documented in wp-includes/formatting.php */ return apply_filters( 'sanitize_email', $email, $email, null ); } @@ -1850,29 +2296,44 @@ function sanitize_email( $email ) { * @return string Human readable time difference. */ function human_time_diff( $from, $to = '' ) { - if ( empty($to) ) + if ( empty( $to ) ) $to = time(); - $diff = (int) abs($to - $from); - if ($diff <= 3600) { - $mins = round($diff / 60); - if ($mins <= 1) { + + $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); - } else if (($diff <= 86400) && ($diff > 3600)) { - $hours = round($diff / 3600); - if ($hours <= 1) { + $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 >= 86400) { - $days = round($diff / 86400); - if ($days <= 1) { + $since = sprintf( _n( '%s hour', '%s hours', $hours ), $hours ); + } elseif ( $diff < WEEK_IN_SECONDS && $diff >= DAY_IN_SECONDS ) { + $days = round( $diff / DAY_IN_SECONDS ); + if ( $days <= 1 ) $days = 1; - } - $since = sprintf(_n('%s day', '%s days', $days), $days); + $since = sprintf( _n( '%s day', '%s days', $days ), $days ); + } elseif ( $diff < 30 * DAY_IN_SECONDS && $diff >= WEEK_IN_SECONDS ) { + $weeks = round( $diff / WEEK_IN_SECONDS ); + if ( $weeks <= 1 ) + $weeks = 1; + $since = sprintf( _n( '%s week', '%s weeks', $weeks ), $weeks ); + } elseif ( $diff < YEAR_IN_SECONDS && $diff >= 30 * DAY_IN_SECONDS ) { + $months = round( $diff / ( 30 * DAY_IN_SECONDS ) ); + if ( $months <= 1 ) + $months = 1; + $since = sprintf( _n( '%s month', '%s months', $months ), $months ); + } elseif ( $diff >= YEAR_IN_SECONDS ) { + $years = round( $diff / YEAR_IN_SECONDS ); + if ( $years <= 1 ) + $years = 1; + $since = sprintf( _n( '%s year', '%s years', $years ), $years ); } + return $since; } @@ -1880,11 +2341,11 @@ function human_time_diff( $from, $to = '' ) { * 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 + * 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 + * The ' […]' string can be modified by plugins/themes using the excerpt_more filter * * @since 1.5.0 * @@ -1898,23 +2359,51 @@ function wp_trim_excerpt($text = '') { $text = strip_shortcodes( $text ); - $text = apply_filters('the_content', $text); + /** This filter is documented in wp-includes/post-template.php */ + $text = apply_filters( 'the_content', $text ); $text = str_replace(']]>', ']]>', $text); - $excerpt_length = apply_filters('excerpt_length', 55); - $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]'); + + /** + * Filter the number of words in an excerpt. + * + * @since 2.7.0 + * + * @param int $number The number of words. Default 55. + */ + $excerpt_length = apply_filters( 'excerpt_length', 55 ); + /** + * Filter the string in the "more" link displayed after a trimmed excerpt. + * + * @since 2.9.0 + * + * @param string $more_string The string shown within the more link. + */ + $excerpt_more = apply_filters( 'excerpt_more', ' ' . '[…]' ); $text = wp_trim_words( $text, $excerpt_length, $excerpt_more ); } - return apply_filters('wp_trim_excerpt', $text, $raw_excerpt); + /** + * Filter the trimmed excerpt string. + * + * @since 2.8.0 + * + * @param string $text The trimmed text. + * @param string $raw_excerpt The text prior to trimming. + */ + 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 '…'. + * @param string $more Optional. What to append if $text needs to be trimmed. Default '…'. * @return string Trimmed text. */ function wp_trim_words( $text, $num_words = 55, $more = null ) { @@ -1922,14 +2411,34 @@ function wp_trim_words( $text, $num_words = 55, $more = null ) { $more = __( '…' ); $original_text = $text; $text = wp_strip_all_tags( $text ); - $words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY ); + /* 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( ' ', $words_array ); + $text = implode( $sep, $words_array ); $text = $text . $more; } else { - $text = implode( ' ', $words_array ); + $text = implode( $sep, $words_array ); } + /** + * Filter the text content after words have been trimmed. + * + * @since 3.3.0 + * + * @param string $text The trimmed text. + * @param int $num_words The number of words to trim the text to. Default 5. + * @param string $more An optional string to append to the end of the trimmed text, e.g. …. + * @param string $original_text The text before it was trimmed. + */ return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text ); } @@ -1943,7 +2452,16 @@ function wp_trim_words( $text, $num_words = 55, $more = null ) { */ function ent2ncr($text) { - // Allow a plugin to short-circuit and override the mappings. + /** + * Filter text before named entities are converted into numbered entities. + * + * A non-null string must be returned for the filter to be evaluated. + * + * @since 3.3.0 + * + * @param null $converted_text The text to be converted. Default null. + * @param string $text The text prior to entity conversion. + */ $filtered = apply_filters( 'pre_ent2ncr', null, $text ); if( null !== $filtered ) return $filtered; @@ -1951,7 +2469,6 @@ function ent2ncr($text) { $to_ncr = array( '"' => '"', '&' => '&', - '⁄' => '/', '<' => '<', '>' => '>', '|' => '|', @@ -2223,14 +2740,30 @@ function ent2ncr($text) { * @return string The formatted text after filter is applied. */ function wp_richedit_pre($text) { - // Filtering a blank results in an annoying
\n - if ( empty($text) ) return apply_filters('richedit_pre', ''); + if ( empty( $text ) ) { + /** + * Filter text returned for the rich text editor. + * + * This filter is first evaluated, and the value returned, if an empty string + * is passed to wp_richedit_pre(). If an empty string is passed, it results + * in a break tag and line feed. + * + * If a non-empty string is passed, the filter is evaluated on the wp_richedit_pre() + * return after being formatted. + * + * @since 2.0.0 + * + * @param string $output Text for the rich text editor. + */ + return apply_filters( 'richedit_pre', '' ); + } $output = convert_chars($text); $output = wpautop($output); - $output = htmlspecialchars($output, ENT_NOQUOTES); + $output = htmlspecialchars($output, ENT_NOQUOTES, get_option( 'blog_charset' ) ); - return apply_filters('richedit_pre', $output); + /** This filter is documented in wp-includes/formatting.php */ + return apply_filters( 'richedit_pre', $output ); } /** @@ -2246,9 +2779,16 @@ function wp_richedit_pre($text) { */ function wp_htmledit_pre($output) { if ( !empty($output) ) - $output = htmlspecialchars($output, ENT_NOQUOTES); // convert only < > & - - return apply_filters('htmledit_pre', $output); + $output = htmlspecialchars($output, ENT_NOQUOTES, get_option( 'blog_charset' ) ); // convert only < > & + + /** + * Filter the text before it is formatted for the HTML editor. + * + * @since 2.5.0 + * + * @param string $output The HTML-formatted text. + */ + return apply_filters( 'htmledit_pre', $output ); } /** @@ -2261,38 +2801,35 @@ function wp_htmledit_pre($output) { * @since 2.8.1 * @access private * - * @param string|array $search - * @param string $subject - * @return string The processed string + * @param string|array $search The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles. + * @param string $subject The string being searched and replaced on, otherwise known as the haystack. + * @return string The string with the replaced svalues. */ 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 ); - } - } + + $count = 1; + while ( $count ) { + $subject = str_replace( $search, '', $subject, $count ); } return $subject; } /** - * Escapes data for use in a MySQL query + * Escapes data for use in a MySQL query. * - * This is just a handy shortcut for $wpdb->escape(), for completeness' sake + * Usually you should prepare queries using wpdb::prepare(). + * Sometimes, spot-escaping is required or useful. One example + * is preparing an array for use in an IN clause. * * @since 2.8.0 - * @param string $sql Unescaped SQL data - * @return string The cleaned $sql + * @param string|array $data Unescaped data + * @return string|array Escaped data */ -function esc_sql( $sql ) { +function esc_sql( $data ) { global $wpdb; - return $wpdb->escape( $sql ); + return $wpdb->_escape( $data ); } /** @@ -2336,12 +2873,26 @@ function esc_url( $url, $protocols = null, $_context = 'display' ) { $url = str_replace( "'", ''', $url ); } - if ( ! is_array( $protocols ) ) - $protocols = wp_allowed_protocols(); - if ( wp_kses_bad_protocol( $url, $protocols ) != $url ) - return ''; + if ( '/' === $url[0] ) { + $good_protocol_url = $url; + } else { + 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', $url, $original_url, $_context); + /** + * Filter a string cleaned and escaped for output as a URL. + * + * @since 2.3.0 + * + * @param string $good_protocol_url The cleaned URL to be returned. + * @param string $original_url The URL prior to cleaning. + * @param string $_context If 'display', replace ampersands and single quotes only. + */ + return apply_filters( 'clean_url', $good_protocol_url, $original_url, $_context ); } /** @@ -2392,6 +2943,17 @@ function esc_js( $text ) { $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 ) ); + /** + * Filter a string cleaned and escaped for output in JavaScript. + * + * Text passed to esc_js() is stripped of invalid or special characters, + * and properly slashed for output. + * + * @since 2.0.6 + * + * @param string $safe_text The text after it has been escaped. + * @param string $text The text prior to being escaped. + */ return apply_filters( 'js_escape', $safe_text, $text ); } @@ -2406,6 +2968,17 @@ function esc_js( $text ) { function esc_html( $text ) { $safe_text = wp_check_invalid_utf8( $text ); $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES ); + /** + * Filter a string cleaned and escaped for output in HTML. + * + * Text passed to esc_html() is stripped of invalid or special characters + * before output. + * + * @since 2.8.0 + * + * @param string $safe_text The text after it has been escaped. + * @param string $text The text prior to being escaped. + */ return apply_filters( 'esc_html', $safe_text, $text ); } @@ -2420,24 +2993,43 @@ function esc_html( $text ) { function esc_attr( $text ) { $safe_text = wp_check_invalid_utf8( $text ); $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES ); + /** + * Filter a string cleaned and escaped for output in an HTML attribute. + * + * Text passed to esc_attr() is stripped of invalid or special characters + * before output. + * + * @since 2.0.6 + * + * @param string $safe_text The text after it has been escaped. + * @param string $text The text prior to being escaped. + */ return apply_filters( 'attribute_escape', $safe_text, $text ); } /** * Escaping for textarea values. * - * @since 3.1 + * @since 3.1.0 * * @param string $text * @return string */ function esc_textarea( $text ) { - $safe_text = htmlspecialchars( $text, ENT_QUOTES ); + $safe_text = htmlspecialchars( $text, ENT_QUOTES, get_option( 'blog_charset' ) ); + /** + * Filter a string cleaned and escaped for output in a textarea element. + * + * @since 3.1.0 + * + * @param string $safe_text The text after it has been escaped. + * @param string $text The text prior to being escaped. + */ return apply_filters( 'esc_textarea', $safe_text, $text ); } /** - * Escape a HTML tag name. + * Escape an HTML tag name. * * @since 2.5.0 * @@ -2446,7 +3038,15 @@ function esc_textarea( $text ) { */ 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); + /** + * Filter a string cleaned and escaped for output as an HTML tag. + * + * @since 2.8.0 + * + * @param string $safe_tag The tag name after it has been escaped. + * @param string $tag_name The text before it was escaped. + */ + return apply_filters( 'tag_escape', $safe_tag, $tag_name ); } /** @@ -2491,21 +3091,13 @@ function wp_make_link_relative( $link ) { function sanitize_option($option, $value) { switch ( $option ) { - case 'admin_email': - $value = sanitize_email($value); - if ( !is_email($value) ) { + 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('admin_email', 'invalid_admin_email', __('The email address entered did not appear to be a valid email address. Please enter a valid email address.')); - } - break; - - 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('new_admin_email', 'invalid_admin_email', __('The email address entered did not appear to be a valid email address. Please enter a valid email address.')); + 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; @@ -2515,8 +3107,6 @@ function sanitize_option($option, $value) { case 'medium_size_h': case 'large_size_w': case 'large_size_h': - case 'embed_size_h': - case 'default_post_edit_rows': case 'mailserver_port': case 'comment_max_links': case 'page_on_front': @@ -2533,11 +3123,6 @@ function sanitize_option($option, $value) { $value = absint( $value ); break; - case 'embed_size_w': - if ( '' !== $value ) - $value = absint( $value ); - break; - case 'posts_per_page': case 'posts_per_rss': $value = (int) $value; @@ -2556,9 +3141,7 @@ function sanitize_option($option, $value) { case 'blogdescription': case 'blogname': - $value = addslashes($value); - $value = wp_filter_post_kses( $value ); // calls stripslashes then addslashes - $value = stripslashes($value); + $value = wp_kses_post( $value ); $value = esc_html( $value ); break; @@ -2566,17 +3149,29 @@ function sanitize_option($option, $value) { $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 'ping_sites': case 'upload_path': - $value = strip_tags($value); - $value = addslashes($value); - $value = wp_filter_kses($value); // calls stripslashes then addslashes - $value = stripslashes($value); + $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': @@ -2609,6 +3204,32 @@ function sanitize_option($option, $value) { $value = get_option( $option ); break; + case 'illegal_names': + if ( ! is_array( $value ) ) + $value = explode( ' ', $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 ) ) { @@ -2624,9 +3245,22 @@ function sanitize_option($option, $value) { $value = esc_url_raw( $value ); $value = str_replace( 'http://', '', $value ); break; + + case 'default_role' : + if ( ! get_role( $value ) && get_role( 'subscriber' ) ) + $value = 'subscriber'; + break; } - $value = apply_filters("sanitize_option_{$option}", $value, $option); + /** + * Filter an option value following sanitization. + * + * @since 2.3.0 + * + * @param string $value The sanitized option value. + * @param string $option The option name. + */ + $value = apply_filters( "sanitize_option_{$option}", $value, $option ); return $value; } @@ -2638,7 +3272,6 @@ function sanitize_option($option, $value) { * {@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. @@ -2647,6 +3280,13 @@ function wp_parse_str( $string, &$array ) { parse_str( $string, $array ); if ( get_magic_quotes_gpc() ) $array = stripslashes_deep( $array ); + /** + * Filter the array of variables derived from a parsed string. + * + * @since 2.3.0 + * + * @param array $array The array populated with variables. + */ $array = apply_filters( 'wp_parse_str', $array ); } @@ -2691,7 +3331,7 @@ function wp_pre_kses_less_than_callback( $matches ) { * @return string The formatted string. */ function wp_sprintf( $pattern ) { - $args = func_get_args( ); + $args = func_get_args(); $len = strlen($pattern); $start = 0; $result = ''; @@ -2727,7 +3367,16 @@ function wp_sprintf( $pattern ) { $arg = isset($args[$arg_index]) ? $args[$arg_index] : ''; } - // Apply filters OR sprintf + /** + * Filter a fragment from the pattern passed to wp_sprintf(). + * + * If the fragment is unchanged, then sprintf() will be run on the fragment. + * + * @since 2.5.0 + * + * @param string $fragment A fragment from the pattern. + * @param string $arg The argument. + */ $_fragment = apply_filters( 'wp_sprintf', $fragment, $arg ); if ( $_fragment != $fragment ) $fragment = $_fragment; @@ -2764,15 +3413,23 @@ function wp_sprintf_l($pattern, $args) { if ( empty($args) ) return ''; - // Translate and filter the delimiter set (avoid ampersands and entities here) - $l = apply_filters('wp_sprintf_l', array( + /** + * Filter the translated delimiters used by wp_sprintf_l(). + * + * Please note: Ampersands and entities should be avoided here. + * + * @since 2.5.0 + * + * @param array $delimiters An array of translated delimiters. + */ + $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); @@ -2800,16 +3457,21 @@ function wp_sprintf_l($pattern, $args) { * * @since 2.5.0 * - * @param integer $str String to get the excerpt from. + * @param string $str String to get the excerpt from. * @param integer $count Maximum number of characters to take. + * @param string $more Optional. What to append if $str needs to be trimmed. Defaults to empty string. * @return string The excerpt. */ -function wp_html_excerpt( $str, $count ) { +function wp_html_excerpt( $str, $count, $more = null ) { + if ( null === $more ) + $more = ''; $str = wp_strip_all_tags( $str, true ); - $str = mb_substr( $str, 0, $count ); + $excerpt = mb_substr( $str, 0, $count ); // remove part of an entity at the end - $str = preg_replace( '/&[^;\s]{0,6}$/', '', $str ); - return $str; + $excerpt = preg_replace( '/&[^;\s]{0,6}$/', '', $excerpt ); + if ( $str != $excerpt ) + $excerpt = trim( $excerpt ) . $more; + return $excerpt; } /** @@ -2845,9 +3507,9 @@ function _links_add_base($m) { global $_links_add_base; //1 = attribute name 2 = quotation mark 3 = URL return $m[1] . '=' . $m[2] . - (strpos($m[3], 'http://') === false ? - path_join($_links_add_base, $m[3]) : - $m[3]) + ( 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]; } @@ -2870,7 +3532,7 @@ 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 ); + return preg_replace_callback( "!<($tags)([^>]*)>!i", '_links_add_target', $content ); } /** @@ -2885,14 +3547,21 @@ function links_add_target( $content, $target = '_blank', $tags = array('a') ) { function _links_add_target( $m ) { global $_links_add_target; $tag = $m[1]; - $link = preg_replace('|(target=[\'"](.*?)[\'"])|i', '', $m[2]); + $link = preg_replace('|( target=([\'"])(.*?)\2)|i', '', $m[2]); return '<' . $tag . $link . ' target="' . esc_attr( $_links_add_target ) . '">'; } -// normalize EOL characters and strip duplicate whitespace +/** + * 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 = trim( $str ); + $str = str_replace( "\r", "\n", $str ); $str = preg_replace( array( '/\n+/', '/[ \t]+/' ), array( "\n", ' ' ), $str ); return $str; } @@ -2913,7 +3582,7 @@ function wp_strip_all_tags($string, $remove_breaks = false) { if ( $remove_breaks ) $string = preg_replace('/[\r\n\t ]+/', ' ', $string); - return trim($string); + return trim( $string ); } /** @@ -2941,7 +3610,6 @@ function sanitize_text_field($str) { $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); @@ -2953,7 +3621,15 @@ function sanitize_text_field($str) { $filtered = trim( preg_replace('/ +/', ' ', $filtered) ); } - return apply_filters('sanitize_text_field', $filtered, $str); + /** + * Filter a sanitized text field string. + * + * @since 2.9.0 + * + * @param string $filtered The sanitized string. + * @param string $str The string prior to being sanitized. + */ + return apply_filters( 'sanitize_text_field', $filtered, $str ); } /** @@ -2966,7 +3642,7 @@ function sanitize_text_field($str) { * @return string */ function wp_basename( $path, $suffix = '' ) { - return urldecode( basename( str_replace( '%2F', '/', urlencode( $path ) ), $suffix ) ); + return urldecode( basename( str_replace( array( '%2F', '%5C' ), '/', urlencode( $path ) ), $suffix ) ); } /** @@ -2978,12 +3654,13 @@ function wp_basename( $path, $suffix = '' ) { */ function capital_P_dangit( $text ) { // Simple replacement for titles - if ( 'the_title' === current_filter() ) + $current_filter = current_filter(); + if ( 'the_title' === $current_filter || 'wp_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 quote'); + $dblq = _x( '“', 'opening curly double quote' ); return str_replace( array( ' Wordpress', '‘Wordpress', $dblq . 'Wordpress', '>Wordpress', '(Wordpress' ), array( ' WordPress', '‘WordPress', $dblq . 'WordPress', '>WordPress', '(WordPress' ), @@ -3001,7 +3678,103 @@ function capital_P_dangit( $text ) { */ function sanitize_mime_type( $mime_type ) { $sani_mime_type = preg_replace( '/[^-+*.a-zA-Z0-9\/]/', '', $mime_type ); + /** + * Filter a mime type following sanitization. + * + * @since 3.1.3 + * + * @param string $sani_mime_type The sanitized mime type. + * @param string $mime_type The mime type prior to sanitization. + */ 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 ); + /** + * Filter a list of trackback URLs following sanitization. + * + * The string returned here consists of a space or carriage return-delimited list + * of trackback URLs. + * + * @since 3.4.0 + * + * @param string $urls_to_ping Sanitized space or carriage return separated URLs. + * @param string $to_ping Space or carriage return separated URLs before sanitization. + */ + return apply_filters( 'sanitize_trackback_urls', $urls_to_ping, $to_ping ); +} + +/** + * Add slashes to a string or array of strings. + * + * This should be used when preparing data for core API that expects slashed data. + * This should not be used to escape data going directly into an SQL query. + * + * @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; +} + +/** + * Remove slashes from a string or array of strings. + * + * This should be used to remove slashes from data passed to core API that + * expects data to be unslashed. + * + * @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 ); +} + +/** + * Extract and return the first URL from passed content. + * + * @since 3.6.0 + * + * @param string $content A string which might contain a URL. + * @return string The found URL. + */ +function get_url_in_content( $content ) { + if ( empty( $content ) ) + return ''; + + if ( preg_match( '/]*?href=([\'"])(.+?)\1/is', $content, $matches ) ) + return esc_url_raw( $matches[2] ); + + return false; +}