X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/6359b807ff8b4ffa151d8756cdefb861c6c1d4db..446fc046ca303b25776b07134407239bdeafa3e6:/wp-includes/formatting.php diff --git a/wp-includes/formatting.php b/wp-includes/formatting.php index 0151e1fc..97a290ad 100644 --- a/wp-includes/formatting.php +++ b/wp-includes/formatting.php @@ -20,17 +20,43 @@ * * Code within certain html blocks are skipped. * + * Do not use this function before the 'init' action hook; everything will break. + * * @since 0.71 - * @uses $wp_cockneyreplace Array of formatted entities for certain common phrases + * + * @global array $wp_cockneyreplace Array of formatted entities for certain common phrases + * @global array $shortcode_tags + * @staticvar array $static_characters + * @staticvar array $static_replacements + * @staticvar array $dynamic_characters + * @staticvar array $dynamic_replacements + * @staticvar array $default_no_texturize_tags + * @staticvar array $default_no_texturize_shortcodes + * @staticvar bool $run_texturize * * @param string $text The text to be formatted - * @param bool $reset Set to true for unit testing. Translated patterns will reset. + * @param bool $reset Set to true for unit testing. Translated patterns will reset. * @return string The string replaced with html entities */ -function wptexturize($text, $reset = false) { +function wptexturize( $text, $reset = false ) { global $wp_cockneyreplace, $shortcode_tags; - static $static_characters, $static_replacements, $dynamic_characters, $dynamic_replacements, - $default_no_texturize_tags, $default_no_texturize_shortcodes, $run_texturize = true; + static $static_characters = null, + $static_replacements = null, + $dynamic_characters = null, + $dynamic_replacements = null, + $default_no_texturize_tags = null, + $default_no_texturize_shortcodes = null, + $run_texturize = true, + $apos = null, + $prime = null, + $double_prime = null, + $opening_quote = null, + $closing_quote = null, + $opening_single_quote = null, + $closing_single_quote = null, + $open_q_flag = '', + $open_sq_flag = '', + $apos_flag = ''; // If there's nothing to do, just stop. if ( empty( $text ) || false === $run_texturize ) { @@ -88,11 +114,16 @@ function wptexturize($text, $reset = false) { 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", "'em" ); - $cockneyreplace = array( $apos . "tain" . $apos . "t", $apos . "twere", $apos . "twas", $apos . "tis", $apos . "twill", $apos . "til", $apos . "bout", $apos . "nuff", $apos . "round", $apos . "cause", $apos . "em" ); } else { - $cockney = $cockneyreplace = array(); + /* translators: This is a comma-separated list of words that defy the syntax of quotations in normal use, + * for example... 'We do not have enough words yet' ... is a typical quoted phrase. But when we write + * lines of code 'til we have enough of 'em, then we need to insert apostrophes instead of quotes. + */ + $cockney = explode( ',', _x( "'tain't,'twere,'twas,'tis,'twill,'til,'bout,'nuff,'round,'cause,'em", + 'Comma-separated list of words to texturize in your language' ) ); + + $cockneyreplace = explode( ',', _x( '’tain’t,’twere,’twas,’tis,’twill,’til,’bout,’nuff,’round,’cause,’em', + 'Comma-separated list of replacement words in your language' ) ); } $static_characters = array_merge( array( '...', '``', '\'\'', ' (tm)' ), $cockney ); @@ -108,40 +139,30 @@ function wptexturize($text, $reset = false) { // '99' and '99" are ambiguous among other patterns; assume it's an abbreviated year at the end of a quotation. if ( "'" !== $apos || "'" !== $closing_single_quote ) { - $dynamic[ '/\'(\d\d)\'(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos . '$1' . $closing_single_quote; + $dynamic[ '/\'(\d\d)\'(?=\Z|[.,:;!?)}\-\]]|>|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_single_quote; } if ( "'" !== $apos || '"' !== $closing_quote ) { - $dynamic[ '/\'(\d\d)"(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos . '$1' . $closing_quote; + $dynamic[ '/\'(\d\d)"(?=\Z|[.,:;!?)}\-\]]|>|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_quote; } // '99 '99s '99's (apostrophe) But never '9 or '99% or '999 or '99.0. if ( "'" !== $apos ) { - $dynamic[ '/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/' ] = $apos; + $dynamic[ '/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/' ] = $apos_flag; } // Quoted Numbers like '0.42' if ( "'" !== $opening_single_quote && "'" !== $closing_single_quote ) { - $dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $opening_single_quote . '$1' . $closing_single_quote; + $dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $open_sq_flag . '$1' . $closing_single_quote; } // Single quote at start, or preceded by (, {, <, [, ", -, or spaces. if ( "'" !== $opening_single_quote ) { - $dynamic[ '/(?<=\A|[([{"\-]|<|' . $spaces . ')\'/' ] = $opening_single_quote; + $dynamic[ '/(?<=\A|[([{"\-]|<|' . $spaces . ')\'/' ] = $open_sq_flag; } // Apostrophe in a word. No spaces, double apostrophes, or other punctuation. if ( "'" !== $apos ) { - $dynamic[ '/(? is found. - . '-(?!->)' // Dash not followed by end of comment. - . '[^\-]*+' // Consume non-dashes. - . ')*+' // Loop possessively. - . '(?:-->)?'; // End of comment. If not found, match all input. - - $shortcode_regex = - '\[' // Find start of shortcode. - . '[\/\[]?' // Shortcodes may begin with [/ or [[ - . $tagregexp // Only match registered shortcodes, because performance. - . '(?:' - . '[^\[\]<>]+' // Shortcodes do not contain other shortcodes. Quantifier critical. - . '|' - . '<[^\[\]>]*>' // HTML elements permitted. Prevents matching ] before >. - . ')*+' // Possessive critical. - . '\]' // Find end of shortcode. - . '\]?'; // Shortcodes may end with ]] - - $regex = - '/(' // Capture the entire match. - . '<' // Find start of element. - . '(?(?=!--)' // Is this a comment? - . $comment_regex // Find end of comment. - . '|' - . '[^>]*>' // Find end of element. - . ')' - . '|' - . $shortcode_regex // Find shortcodes. - . ')/s'; + preg_match_all( '@\[/?([^<>&/\[\]\x00-\x20=]++)@', $text, $matches ); + $tagnames = array_intersect( array_keys( $shortcode_tags ), $matches[1] ); + $found_shortcodes = ! empty( $tagnames ); + $shortcode_regex = $found_shortcodes ? _get_wptexturize_shortcode_regex( $tagnames ) : ''; + $regex = _get_wptexturize_split_regex( $shortcode_regex ); $textarr = preg_split( $regex, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); foreach ( $textarr as &$curl ) { // Only call _wptexturize_pushpop_element if $curl is a delimiter. $first = $curl[0]; - if ( '<' === $first && ''; + $quote_pattern = "/$needle(?=\\Z|[.,:;!?)}\\-\\]]|>|" . $spaces . ")/"; + $prime_pattern = "/(?<=\\d)$needle/"; + $flag_after_digit = "/(?<=\\d)$flag/"; + $flag_no_digit = "/(? &$sentence ) { + if ( false === strpos( $sentence, $needle ) ) { + continue; + } elseif ( 0 !== $key && 0 === substr_count( $sentence, $close_quote ) ) { + $sentence = preg_replace( $quote_pattern, $flag, $sentence, -1, $count ); + if ( $count > 1 ) { + // This sentence appears to have multiple closing quotes. Attempt Vulcan logic. + $sentence = preg_replace( $flag_no_digit, $close_quote, $sentence, -1, $count2 ); + if ( 0 === $count2 ) { + // Try looking for a quote followed by a period. + $count2 = substr_count( $sentence, "$flag." ); + if ( $count2 > 0 ) { + // Assume the rightmost quote-period match is the end of quotation. + $pos = strrpos( $sentence, "$flag." ); + } else { + // When all else fails, make the rightmost candidate a closing quote. + // This is most likely to be problematic in the context of bug #18549. + $pos = strrpos( $sentence, $flag ); + } + $sentence = substr_replace( $sentence, $close_quote, $pos, strlen( $flag ) ); + } + // Use conventional replacement on any remaining primes and quotes. + $sentence = preg_replace( $prime_pattern, $prime, $sentence ); + $sentence = preg_replace( $flag_after_digit, $prime, $sentence ); + $sentence = str_replace( $flag, $close_quote, $sentence ); + } elseif ( 1 == $count ) { + // Found only one closing quote candidate, so give it priority over primes. + $sentence = str_replace( $flag, $close_quote, $sentence ); + $sentence = preg_replace( $prime_pattern, $prime, $sentence ); + } else { + // No closing quotes found. Just run primes pattern. + $sentence = preg_replace( $prime_pattern, $prime, $sentence ); + } + } else { + $sentence = preg_replace( $prime_pattern, $prime, $sentence ); + $sentence = preg_replace( $quote_pattern, $close_quote, $sentence ); + } + if ( '"' == $needle && false !== strpos( $sentence, '"' ) ) { + $sentence = str_replace( '"', $close_quote, $sentence ); + } + } - return $text; + return implode( $open_quote, $sentences ); } /** @@ -313,12 +369,12 @@ function wptexturize($text, $reset = false) { * @access private * * @param string $text Text to check. Must be a tag like `` or `[shortcode]`. - * @param array $stack List of open tag elements. - * @param array $disabled_elements The tag names to match against. Spaces are not allowed in tag names. + * @param array $stack List of open tag elements. + * @param array $disabled_elements The tag names to match against. Spaces are not allowed in tag names. */ -function _wptexturize_pushpop_element($text, &$stack, $disabled_elements) { +function _wptexturize_pushpop_element( $text, &$stack, $disabled_elements ) { // Is it an opening tag or closing tag? - if ( '/' !== $text[1] ) { + if ( isset( $text[1] ) && '/' !== $text[1] ) { $opening_tag = true; $name_offset = 1; } elseif ( 0 == count( $stack ) ) { @@ -370,7 +426,7 @@ function _wptexturize_pushpop_element($text, &$stack, $disabled_elements) { * after paragraphing. Default true. * @return string Text which has been converted into correct paragraph tags. */ -function wpautop($pee, $br = true) { +function wpautop( $pee, $br = true ) { $pre_tags = array(); if ( trim($pee) === '' ) @@ -408,12 +464,12 @@ function wpautop($pee, $br = true) { $pee .= $last_pee; } // Change multiple
s into two line breaks, which will turn into paragraphs. - $pee = preg_replace('|
\s*
|', "\n\n", $pee); + $pee = preg_replace('|\s*|', "\n\n", $pee); $allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)'; // Add a single line break above block-level opening tags. - $pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee); + $pee = preg_replace('!(<' . $allblocks . '[\s/>])!', "\n$1", $pee); // Add a double line break below block-level closing tags. $pee = preg_replace('!()!', "$1\n\n", $pee); @@ -465,24 +521,24 @@ function wpautop($pee, $br = true) { } // Under certain strange conditions it could create a P of entirely whitespace. - $pee = preg_replace('|

\s*

|', '', $pee); + $pee = preg_replace('|

\s*

|', '', $pee); // Add a closing

inside

,
, or
tag if missing. $pee = preg_replace('!

([^<]+)!', "

$1

", $pee); - + // If an opening or closing block element tag is wrapped in a

, unwrap it. - $pee = preg_replace('!

\s*(]*>)\s*

!', "$1", $pee); - + $pee = preg_replace('!

\s*(]*>)\s*

!', "$1", $pee); + // In some cases
  • may get wrapped in

    , fix them. - $pee = preg_replace("|

    (|", "$1", $pee); - + $pee = preg_replace("|

    (|", "$1", $pee); + // If a

    is wrapped with a

    , move it inside the

    . $pee = preg_replace('|

    ]*)>|i', "

    ", $pee); $pee = str_replace('

    ', '

    ', $pee); - + // If an opening or closing block element tag is preceded by an opening

    tag, remove it. $pee = preg_replace('!

    \s*(]*>)!', "$1", $pee); - + // If an opening or closing block element tag is followed by a closing

    tag, remove it. $pee = preg_replace('!(]*>)\s*

    !', "$1", $pee); @@ -491,8 +547,11 @@ function wpautop($pee, $br = true) { // Replace newlines that shouldn't be touched with a placeholder. $pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', '_autop_newline_preservation_helper', $pee); + // Normalize
    + $pee = str_replace( array( '
    ', '
    ' ), '
    ', $pee ); + // Replace any new line characters that aren't preceded by a
    with a
    . - $pee = preg_replace('|(?)\s*\n|', "
    \n", $pee); + $pee = preg_replace('|(?)\s*\n|', "
    \n", $pee); // Replace newline placeholders with newlines. $pee = str_replace('', "\n", $pee); @@ -500,7 +559,7 @@ function wpautop($pee, $br = true) { // If a
    tag is after an opening or closing block tag, remove it. $pee = preg_replace('!(]*>)\s*
    !', "$1", $pee); - + // If a
    tag is before a subset of opening or closing block tags, remove it. $pee = preg_replace('!
    (\s*]*>)!', '$1', $pee); $pee = preg_replace( "|\n

    $|", '

    ', $pee ); @@ -510,7 +569,9 @@ function wpautop($pee, $br = true) { $pee = str_replace(array_keys($pre_tags), array_values($pre_tags), $pee); // Restore newlines in all elements. - $pee = str_replace( " ", "\n", $pee ); + if ( false !== strpos( $pee, '' ) ) { + $pee = str_replace( array( ' ', '' ), "\n", $pee ); + } return $pee; } @@ -524,6 +585,17 @@ function wpautop($pee, $br = true) { * @return array The formatted text. */ function wp_html_split( $input ) { + return preg_split( get_html_split_regex(), $input, -1, PREG_SPLIT_DELIM_CAPTURE ); +} + +/** + * Retrieve the regular expression for an HTML element. + * + * @since 4.4.0 + * + * @return string The regular expression + */ +function get_html_split_regex() { static $regex; if ( ! isset( $regex ) ) { @@ -544,22 +616,100 @@ function wp_html_split( $input ) { . ')*+' // Loop possessively. . '(?:]]>)?'; // End of comment. If not found, match all input. + $escaped = + '(?=' // Is the element escaped? + . '!--' + . '|' + . '!\[CDATA\[' + . ')' + . '(?(?=!-)' // If yes, which type? + . $comments + . '|' + . $cdata + . ')'; + $regex = '/(' // Capture the entire match. . '<' // Find start of element. - . '(?(?=!--)' // Is this a comment? - . $comments // Find end of comment. - . '|' - . '(?(?=!\[CDATA\[)' // Is this a comment? - . $cdata // Find end of comment. - . '|' - . '[^>]*>?' // Find end of element. If not found, match all input. - . ')' + . '(?' // Conditional expression follows. + . $escaped // Find end of escaped element. + . '|' // ... else ... + . '[^>]*>?' // Find end of normal element. . ')' - . ')/s'; + . ')/'; } - return preg_split( $regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE ); + return $regex; +} + +/** + * Retrieve the combined regular expression for HTML and shortcodes. + * + * @access private + * @ignore + * @internal This function will be removed in 4.5.0 per Shortcode API Roadmap. + * @since 4.4.0 + * + * @param string $shortcode_regex The result from _get_wptexturize_shortcode_regex(). Optional. + * @return string The regular expression + */ +function _get_wptexturize_split_regex( $shortcode_regex = '' ) { + static $html_regex; + + if ( ! isset( $html_regex ) ) { + $comment_regex = + '!' // Start of comment, after the <. + . '(?:' // Unroll the loop: Consume everything until --> is found. + . '-(?!->)' // Dash not followed by end of comment. + . '[^\-]*+' // Consume non-dashes. + . ')*+' // Loop possessively. + . '(?:-->)?'; // End of comment. If not found, match all input. + + $html_regex = // Needs replaced with wp_html_split() per Shortcode API Roadmap. + '<' // Find start of element. + . '(?(?=!--)' // Is this a comment? + . $comment_regex // Find end of comment. + . '|' + . '[^>]*>?' // Find end of element. If not found, match all input. + . ')'; + } + + if ( empty( $shortcode_regex ) ) { + $regex = '/(' . $html_regex . ')/'; + } else { + $regex = '/(' . $html_regex . '|' . $shortcode_regex . ')/'; + } + + return $regex; +} + +/** + * Retrieve the regular expression for shortcodes. + * + * @access private + * @ignore + * @internal This function will be removed in 4.5.0 per Shortcode API Roadmap. + * @since 4.4.0 + * + * @param array $tagnames List of shortcodes to find. + * @return string The regular expression + */ +function _get_wptexturize_shortcode_regex( $tagnames ) { + $tagregexp = join( '|', array_map( 'preg_quote', $tagnames ) ); + $tagregexp = "(?:$tagregexp)(?=[\\s\\]\\/])"; // Excerpt of get_shortcode_regex(). + $regex = + '\[' // Find start of shortcode. + . '[\/\[]?' // Shortcodes may begin with [/ or [[ + . $tagregexp // Only match registered shortcodes, because performance. + . '(?:' + . '[^\[\]<>]+' // Shortcodes do not contain other shortcodes. Quantifier critical. + . '|' + . '<[^\[\]>]*>' // HTML elements permitted. Prevents matching ] before >. + . ')*+' // Possessive critical. + . '\]' // Find end of shortcode. + . '\]?'; // Shortcodes may end with ]] + + return $regex; } /** @@ -581,8 +731,8 @@ function wp_replace_in_html_tags( $haystack, $replace_pairs ) { // Extract $needle and $replace. foreach ( $replace_pairs as $needle => $replace ); - // Loop through delimeters (elements) only. - for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) { + // Loop through delimiters (elements) only. + for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) { if ( false !== strpos( $textarr[$i], $needle ) ) { $textarr[$i] = str_replace( $needle, $replace, $textarr[$i] ); $changed = true; @@ -592,8 +742,8 @@ function wp_replace_in_html_tags( $haystack, $replace_pairs ) { // Extract all $needles. $needles = array_keys( $replace_pairs ); - // Loop through delimeters (elements) only. - for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) { + // Loop through delimiters (elements) only. + for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) { foreach ( $needles as $needle ) { if ( false !== strpos( $textarr[$i], $needle ) ) { $textarr[$i] = strtr( $textarr[$i], $replace_pairs ); @@ -622,7 +772,7 @@ function wp_replace_in_html_tags( $haystack, $replace_pairs ) { * @return string */ function _autop_newline_preservation_helper( $matches ) { - return str_replace("\n", "", $matches[0]); + return str_replace( "\n", "", $matches[0] ); } /** @@ -632,6 +782,8 @@ function _autop_newline_preservation_helper( $matches ) { * * @since 2.9.0 * + * @global array $shortcode_tags + * * @param string $pee The content. * @return string The filtered content. */ @@ -675,7 +827,7 @@ function shortcode_unautop( $pee ) { . ')' . '(?:' . $spaces . ')*+' // optional trailing whitespace . '<\\/p>' // closing paragraph - . '/s'; + . '/'; return preg_replace( $pattern, '$1', $pee ); } @@ -692,7 +844,7 @@ function shortcode_unautop( $pee ) { * @param string $str The string to be checked * @return bool True if $str fits a UTF-8 model, false otherwise. */ -function seems_utf8($str) { +function seems_utf8( $str ) { mbstring_binary_safe_encoding(); $length = strlen($str); reset_mbstring_encoding(); @@ -724,10 +876,16 @@ function seems_utf8($str) { * @since 1.2.2 * @access private * - * @param string $string The text which is to be encoded. - * @param int $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES. - * @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. + * @staticvar string $_charset + * + * @param string $string The text which is to be encoded. + * @param int|string $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 bool $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 ) { @@ -748,7 +906,7 @@ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = fals // Store the site charset as a static to avoid multiple calls to wp_load_alloptions() if ( ! $charset ) { - static $_charset; + static $_charset = null; if ( ! isset( $_charset ) ) { $alloptions = wp_load_alloptions(); $_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : ''; @@ -768,25 +926,14 @@ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = fals $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 & + if ( ! $double_encode ) { + // Guarantee every &entity; is valid, convert &garbage; into &garbage; + // This is required for PHP < 5.4.0 because ENT_HTML401 flag is unavailable. $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, $c = count( $string ); $i < $c; $i += 2 ) { - $string[$i] = @htmlspecialchars( $string[$i], $quote_style, $charset ); - } - $string = implode( '', $string ); } + $string = @htmlspecialchars( $string, $quote_style, $charset, $double_encode ); + // Backwards compatibility if ( 'single' === $_quote_style ) $string = str_replace( "'", ''', $string ); @@ -804,8 +951,14 @@ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = fals * * @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. + * @param string $string The text which is to be decoded. + * @param string|int $quote_style Optional. Converts double quotes if set to ENT_COMPAT, + * both single and double if set to ENT_QUOTES or + * none if set to ENT_NOQUOTES. + * Also compatible with old _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 ) { @@ -861,8 +1014,11 @@ function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) { * * @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. + * @staticvar bool $is_utf8 + * @staticvar bool $utf8_pcre + * + * @param string $string The text which is to be checked. + * @param bool $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 ) { @@ -873,17 +1029,17 @@ function wp_check_invalid_utf8( $string, $strip = false ) { } // Store the site charset as a static to avoid multiple calls to get_option() - static $is_utf8; - if ( !isset( $is_utf8 ) ) { + static $is_utf8 = null; + if ( ! isset( $is_utf8 ) ) { $is_utf8 = in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ); } - if ( !$is_utf8 ) { + 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 ) ) { + static $utf8_pcre = null; + 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 @@ -910,7 +1066,7 @@ function wp_check_invalid_utf8( $string, $strip = false ) { * @since 1.5.0 * * @param string $utf8_string - * @param int $length Max length of the 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 ) { @@ -973,7 +1129,7 @@ function utf8_uri_encode( $utf8_string, $length = 0 ) { * @param string $string Text that might have accent characters * @return string Filtered string with replaced "nice" characters. */ -function remove_accents($string) { +function remove_accents( $string ) { if ( !preg_match('/[\x80-\xff]/', $string) ) return $string; @@ -1157,7 +1313,7 @@ function remove_accents($string) { // Used for locale-specific rules $locale = get_locale(); - if ( 'de_DE' == $locale ) { + if ( 'de_DE' == $locale || 'de_DE_formal' == $locale ) { $chars[ chr(195).chr(132) ] = 'Ae'; $chars[ chr(195).chr(164) ] = 'ae'; $chars[ chr(195).chr(150) ] = 'Oe'; @@ -1217,7 +1373,7 @@ function remove_accents($string) { */ function sanitize_file_name( $filename ) { $filename_raw = $filename; - $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", chr(0)); + $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0)); /** * Filter the list of characters to remove from a filename. * @@ -1290,7 +1446,7 @@ function sanitize_file_name( $filename ) { * @since 2.0.0 * * @param string $username The username to be sanitized. - * @param bool $strict If set limits $username to specific characters. Default false. + * @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 ) { @@ -1356,9 +1512,9 @@ function sanitize_key( $key ) { * * @since 1.0.0 * - * @param string $title The string to be sanitized. + * @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 + * @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' ) { @@ -1406,9 +1562,9 @@ function sanitize_title_for_query( $title ) { * * @since 1.2.0 * - * @param string $title The title to be sanitized. + * @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. + * @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' ) { @@ -1428,12 +1584,12 @@ function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'displa } $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 ); + // Convert nbsp, ndash and mdash HTML entities to hyphens + $title = str_replace( array( ' ', ' ', '–', '–', '—', '—' ), '-', $title ); // Strip these characters entirely $title = str_replace( array( @@ -1456,6 +1612,9 @@ function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'displa $title = str_replace( '%c3%97', 'x', $title ); } + $title = preg_replace('/&.+?;/', '', $title); // kill entities + $title = str_replace('.', '-', $title); + $title = preg_replace('/[^%a-z0-9 _-]/', '', $title); $title = preg_replace('/\s+/', '-', $title); $title = preg_replace('|-+|', '-', $title); @@ -1475,7 +1634,7 @@ function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'displa * @since 2.5.1 * * @param string $orderby Order by clause to be validated. - * @return string|bool Returns $orderby if valid, false otherwise. + * @return string|false Returns $orderby if valid, false otherwise. */ function sanitize_sql_orderby( $orderby ) { if ( preg_match( '/^\s*(([a-z0-9_]+|`[a-z0-9_]+`)(\s+(ASC|DESC))?\s*(,\s*(?=[a-z0-9_`])|$))+$/i', $orderby ) || preg_match( '/^\s*RAND\(\s*\)\s*$/i', $orderby ) ) { @@ -1494,7 +1653,7 @@ function sanitize_sql_orderby( $orderby ) { * * @since 2.8.0 * - * @param string $class The classname to be sanitized + * @param string $class The classname to be sanitized * @param string $fallback Optional. The value to return if the sanitization ends up as an empty string. * Defaults to an empty string. * @return string The sanitized value @@ -1506,9 +1665,9 @@ function sanitize_html_class( $class, $fallback = '' ) { //Limit to A-Z,a-z,0-9,_,- $sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $sanitized ); - if ( '' == $sanitized ) - $sanitized = $fallback; - + if ( '' == $sanitized && $fallback ) { + return sanitize_html_class( $fallback ); + } /** * Filter a sanitized HTML class string. * @@ -1522,71 +1681,73 @@ function sanitize_html_class( $class, $fallback = '' ) { } /** - * Converts a number of characters from a string. - * - * Metadata tags `` and `<category>` are removed, `<br>` and `<hr>` are - * converted into correct XHTML and Unicode characters are converted to the - * valid range. + * Converts lone & characters into `&` (a.k.a. `&`) * * @since 0.71 * - * @param string $content String of characters to be converted. + * @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 ) ) +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); + if ( strpos( $content, '&' ) !== false ) { + $content = preg_replace( '/&([^#])(?![a-z1-4]{1,8};)/i', '&$1', $content ); + } - // Converts lone & characters into & (a.k.a. &) - $content = preg_replace('/&([^#])(?![a-z1-4]{1,8};)/i', '&$1', $content); + return $content; +} - // Fix Word pasting - $content = strtr($content, $wp_htmltranswinuni); +/** + * Converts invalid Unicode references range to valid range. + * + * @since 4.3.0 + * + * @param string $content String with entities that need converting. + * @return string Converted string. + */ +function convert_invalid_entities( $content ) { + $wp_htmltranswinuni = array( + '€' => '€', // the Euro sign + '' => '', + '‚' => '‚', // these are Windows CP1252 specific characters + 'ƒ' => 'ƒ', // they would look weird on non-Windows browsers + '„' => '„', + '…' => '…', + '†' => '†', + '‡' => '‡', + 'ˆ' => 'ˆ', + '‰' => '‰', + 'Š' => 'Š', + '‹' => '‹', + 'Œ' => 'Œ', + '' => '', + 'Ž' => 'Ž', + '' => '', + '' => '', + '‘' => '‘', + '’' => '’', + '“' => '“', + '”' => '”', + '•' => '•', + '–' => '–', + '—' => '—', + '˜' => '˜', + '™' => '™', + 'š' => 'š', + '›' => '›', + 'œ' => 'œ', + '' => '', + 'ž' => 'ž', + 'Ÿ' => 'Ÿ' + ); - // Just a little XHTML help - $content = str_replace('<br>', '<br />', $content); - $content = str_replace('<hr>', '<hr />', $content); + if ( strpos( $content, '' ) !== false ) { + $content = strtr( $content, $wp_htmltranswinuni ); + } return $content; } @@ -1596,8 +1757,8 @@ function convert_chars($content, $deprecated = '') { * * @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. + * @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 ) { @@ -1653,7 +1814,7 @@ function force_balance_tags( $text ) { if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag $tag = strtolower(substr($regex[1],1)); // if too many closing tags - if( $stacksize <= 0 ) { + if ( $stacksize <= 0 ) { $tag = ''; // or close to be safe $tag = '/' . $tag; } @@ -1708,7 +1869,7 @@ function force_balance_tags( $text ) { // Attributes $attributes = $regex[2]; - if( ! empty( $attributes ) && $attributes[0] != '>' ) + if ( ! empty( $attributes ) && $attributes[0] != '>' ) $attributes = ' ' . $attributes; $tag = '<' . $tag . $attributes . '>'; @@ -1747,12 +1908,15 @@ function force_balance_tags( $text ) { * it is simply a holder for the 'format_to_edit' filter. * * @since 0.71 + * @since 4.4.0 The `$richedit` parameter was renamed to `$rich_text` for clarity. * - * @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). + * @param string $content The text about to be edited. + * @param bool $rich_text Optional. Whether `$content` should be considered rich text, + * in which case it would not be passed through esc_textarea(). + * Default false. * @return string The text after the filter (and possibly htmlspecialchars()) has been run. */ -function format_to_edit( $content, $richedit = false ) { +function format_to_edit( $content, $rich_text = false ) { /** * Filter the text to be formatted for editing. * @@ -1761,7 +1925,7 @@ function format_to_edit( $content, $richedit = false ) { * @param string $content The text, prior to formatting for editing. */ $content = apply_filters( 'format_to_edit', $content ); - if ( ! $richedit ) + if ( ! $rich_text ) $content = esc_textarea( $content ); return $content; } @@ -1779,12 +1943,12 @@ function format_to_edit( $content, $richedit = false ) { * * @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. + * @param int $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); +function zeroise( $number, $threshold ) { + return sprintf( '%0' . $threshold . 's', $number ); } /** @@ -1795,7 +1959,7 @@ function zeroise($number, $threshold) { * @param string $string Value to which backslashes will be added. * @return string String with backslashes inserted. */ -function backslashit($string) { +function backslashit( $string ) { if ( isset( $string[0] ) && $string[0] >= '0' && $string[0] <= '9' ) $string = '\\\\' . $string; return addcslashes( $string, 'A..Za..z' ); @@ -1853,55 +2017,63 @@ function addslashes_gpc($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. + * Navigates through an array, object, or scalar, and removes slashes from the values. * * @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; +function stripslashes_deep( $value ) { + return map_deep( $value, 'stripslashes_from_strings_only' ); } /** - * Navigates through an array and encodes the values to be used in a URL. + * Callback function for `stripslashes_deep()` which strips slashes from strings. * + * @since 4.4.0 + * + * @param mixed $value The array or string to be stripped. + * @return mixed $value The stripped value. + */ +function stripslashes_from_strings_only( $value ) { + return is_string( $value ) ? stripslashes( $value ) : $value; +} + +/** + * Navigates through an array, object, or scalar, 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). + * @param mixed $value The array or string to be encoded. + * @return mixed $value The encoded value. */ -function urlencode_deep($value) { - $value = is_array($value) ? array_map('urlencode_deep', $value) : urlencode($value); - return $value; +function urlencode_deep( $value ) { + return map_deep( $value, 'urlencode' ); } /** - * Navigates through an array and raw encodes the values to be used in a URL. + * Navigates through an array, object, or scalar, 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). + * @param mixed $value The array or string to be encoded. + * @return mixed $value The encoded value. */ function rawurlencode_deep( $value ) { - return is_array( $value ) ? array_map( 'rawurlencode_deep', $value ) : rawurlencode( $value ); + return map_deep( $value, 'rawurlencode' ); +} + +/** + * Navigates through an array, object, or scalar, and decodes URL-encoded values + * + * @since 4.4.0 + * + * @param mixed $value The array or string to be decoded. + * @return mixed $value The decoded value. + */ +function urldecode_deep( $value ) { + return map_deep( $value, 'urldecode' ); } /** @@ -1910,7 +2082,7 @@ function rawurlencode_deep( $value ) { * @since 0.71 * * @param string $email_address Email address. - * @param int $hex_encoding Optional. Set to 1 to enable hex encoding. + * @param int $hex_encoding Optional. Set to 1 to enable hex encoding. * @return string Converted email address. */ function antispambot( $email_address, $hex_encoding = 0 ) { @@ -1926,9 +2098,7 @@ function antispambot( $email_address, $hex_encoding = 0 ) { } } - $email_no_spam_address = str_replace( '@', '@', $email_no_spam_address ); - - return $email_no_spam_address; + return str_replace( '@', '@', $email_no_spam_address ); } /** @@ -1943,7 +2113,7 @@ function antispambot( $email_address, $hex_encoding = 0 ) { * @param array $matches Single Regex Match. * @return string HTML A element with URI address. */ -function _make_url_clickable_cb($matches) { +function _make_url_clickable_cb( $matches ) { $url = $matches[2]; if ( ')' == $matches[3] && strpos( $url, '(' ) ) { @@ -1980,19 +2150,21 @@ function _make_url_clickable_cb($matches) { * @param array $matches Single Regex Match. * @return string HTML A element with URL address. */ -function _make_web_ftp_clickable_cb($matches) { +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); } + + $dest = esc_url($dest); + if ( empty($dest) ) + return $matches[0]; + return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>$ret"; } @@ -2008,7 +2180,7 @@ function _make_web_ftp_clickable_cb($matches) { * @param array $matches Single Regex Match. * @return string HTML A element with email address. */ -function _make_email_clickable_cb($matches) { +function _make_email_clickable_cb( $matches ) { $email = $matches[2] . '@' . $matches[3]; return $matches[1] . "<a href=\"mailto:$email\">$email</a>"; } @@ -2030,9 +2202,9 @@ function make_clickable( $text ) { $nested_code_pre = 0; // Keep track of how many levels link is nested inside <pre> or <code> foreach ( $textarr as $piece ) { - if ( preg_match( '|^<code[\s>]|i', $piece ) || preg_match( '|^<pre[\s>]|i', $piece ) ) + if ( preg_match( '|^<code[\s>]|i', $piece ) || preg_match( '|^<pre[\s>]|i', $piece ) || preg_match( '|^<script[\s>]|i', $piece ) || preg_match( '|^<style[\s>]|i', $piece ) ) $nested_code_pre++; - elseif ( ( '</code>' === strtolower( $piece ) || '</pre>' === strtolower( $piece ) ) && $nested_code_pre ) + elseif ( $nested_code_pre && ( '</code>' === strtolower( $piece ) || '</pre>' === strtolower( $piece ) || '</script>' === strtolower( $piece ) || '</style>' === strtolower( $piece ) ) ) $nested_code_pre--; if ( $nested_code_pre || empty( $piece ) || ( $piece[0] === '<' && ! preg_match( '|^<\s*[\w]{1,20}+://|', $piece ) ) ) { @@ -2079,8 +2251,7 @@ function make_clickable( $text ) { } // Cleanup of accidental links within links - $r = preg_replace( '#(<a([ \r\n\t]+[^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i', "$1$3</a>", $r ); - return $r; + return preg_replace( '#(<a([ \r\n\t]+[^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i', "$1$3</a>", $r ); } /** @@ -2109,7 +2280,7 @@ function make_clickable( $text ) { * @access private * * @param string $string The string to split. - * @param int $goal The desired chunk length. + * @param int $goal The desired chunk length. * @return array Numeric array of chunks. */ function _split_str_by_whitespace( $string, $goal ) { @@ -2151,8 +2322,7 @@ function wp_rel_nofollow( $text ) { // This is a pre save filter, so text is already escaped. $text = stripslashes($text); $text = preg_replace_callback('|<a (.+?)>|i', 'wp_rel_nofollow_callback', $text); - $text = wp_slash($text); - return $text; + return wp_slash( $text ); } /** @@ -2168,8 +2338,30 @@ function wp_rel_nofollow( $text ) { */ function wp_rel_nofollow_callback( $matches ) { $text = $matches[1]; - $text = str_replace(array(' rel="nofollow"', " rel='nofollow'"), '', $text); - return "<a $text rel=\"nofollow\">"; + $atts = shortcode_parse_atts( $matches[1] ); + $rel = 'nofollow'; + + if ( preg_match( '%href=["\'](' . preg_quote( set_url_scheme( home_url(), 'http' ) ) . ')%i', $text ) || + preg_match( '%href=["\'](' . preg_quote( set_url_scheme( home_url(), 'https' ) ) . ')%i', $text ) + ) { + return "<a $text>"; + } + + if ( ! empty( $atts['rel'] ) ) { + $parts = array_map( 'trim', explode( ' ', $atts['rel'] ) ); + if ( false === array_search( 'nofollow', $parts ) ) { + $parts[] = 'nofollow'; + } + $rel = implode( ' ', $parts ); + unset( $atts['rel'] ); + + $html = ''; + foreach ( $atts as $name => $value ) { + $html .= "{$name}=\"$value\" "; + } + $text = trim( $html ); + } + return "<a $text rel=\"$rel\">"; } /** @@ -2179,9 +2371,10 @@ function wp_rel_nofollow_callback( $matches ) { * 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 * + * @global array $wpsmiliestrans + * * @param array $matches Single match. Smiley code to convert to image. * @return string Image string for smiley. */ @@ -2224,7 +2417,8 @@ function translate_smiley( $matches ) { * used in the function isn't empty. * * @since 0.71 - * @uses $wp_smiliessearch + * + * @global string|array $wp_smiliessearch * * @param string $text Content to convert smilies from text. * @return string Converted content with text smilies replaced with images. @@ -2275,8 +2469,8 @@ function convert_smilies( $text ) { * * @since 0.71 * - * @param string $email Email address to verify. - * @param boolean $deprecated Deprecated. + * @param string $email Email address to verify. + * @param bool $deprecated Deprecated. * @return string|bool Either false or the valid email address. */ function is_email( $email, $deprecated = false ) { @@ -2296,7 +2490,6 @@ function is_email( $email, $deprecated = false ) { * * @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' ); @@ -2368,14 +2561,13 @@ function is_email( $email, $deprecated = false ) { * @param string $string Subject line * @return string Converted string to ASCII */ -function wp_iso_descrambler($string) { +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; + return preg_replace_callback( '#\=([0-9a-f]{2})#i', '_wp_iso_convert', $subject ); } } @@ -2410,13 +2602,19 @@ function get_gmt_from_date( $string, $format = 'Y-m-d H:i:s' ) { $tz = get_option( 'timezone_string' ); if ( $tz ) { $datetime = date_create( $string, new DateTimeZone( $tz ) ); - if ( ! $datetime ) + if ( ! $datetime ) { return gmdate( $format, 0 ); + } $datetime->setTimezone( new DateTimeZone( 'UTC' ) ); $string_gmt = $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 gmdate( $format, 0 ); + 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 ) ) { + $datetime = strtotime( $string ); + if ( false === $datetime ) { + return gmdate( $format, 0 ); + } + return gmdate( $format, $datetime ); + } $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 ); } @@ -2462,7 +2660,7 @@ function get_date_from_gmt( $string, $format = 'Y-m-d H:i:s' ) { * @param string $timezone Either 'Z' for 0 offset or '±hhmm'. * @return int|float The offset in seconds. */ -function iso8601_timezone_to_offset($timezone) { +function iso8601_timezone_to_offset( $timezone ) { // $timezone is either 'Z' or '[+|-]hhmm' if ($timezone == 'Z') { $offset = 0; @@ -2481,10 +2679,10 @@ function iso8601_timezone_to_offset($timezone) { * @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'. + * @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') { +function iso8601_to_datetime( $date_string, $timezone = 'user' ) { $timezone = strtolower($timezone); if ($timezone == 'gmt') { @@ -2507,23 +2705,6 @@ function iso8601_to_datetime($date_string, $timezone = 'user') { } } -/** - * 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. * @@ -2635,7 +2816,7 @@ function sanitize_email( $email ) { * @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. + * @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 = '' ) { @@ -2661,13 +2842,13 @@ function human_time_diff( $from, $to = '' ) { if ( $days <= 1 ) $days = 1; $since = sprintf( _n( '%s day', '%s days', $days ), $days ); - } elseif ( $diff < 30 * DAY_IN_SECONDS && $diff >= WEEK_IN_SECONDS ) { + } elseif ( $diff < MONTH_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 ) ); + } elseif ( $diff < YEAR_IN_SECONDS && $diff >= MONTH_IN_SECONDS ) { + $months = round( $diff / MONTH_IN_SECONDS ); if ( $months <= 1 ) $months = 1; $since = sprintf( _n( '%s month', '%s months', $months ), $months ); @@ -2706,7 +2887,7 @@ function human_time_diff( $from, $to = '' ) { * @param string $text Optional. The excerpt. If set to empty, an excerpt is generated. * @return string The excerpt. */ -function wp_trim_excerpt($text = '') { +function wp_trim_excerpt( $text = '' ) { $raw_excerpt = $text; if ( '' == $text ) { $text = get_the_content(''); @@ -2755,19 +2936,25 @@ function wp_trim_excerpt($text = '') { * * @since 3.3.0 * - * @param string $text Text to trim. - * @param int $num_words Number of words. Default 55. - * @param string $more Optional. What to append if $text needs to be trimmed. Default '…'. + * @param string $text Text to trim. + * @param int $num_words Number of words. Default 55. + * @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 ) { - if ( null === $more ) + 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' ) ) ) { + + /* + * translators: If your word count is based on single characters (e.g. East Asian characters), + * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'. + * Do not translate into your own language. + */ + if ( strpos( _x( 'words', 'Word count type. Do not translate!' ), 'characters' ) === 0 && 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 ); @@ -2776,6 +2963,7 @@ function wp_trim_words( $text, $num_words = 55, $more = null ) { $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 ); @@ -2783,6 +2971,7 @@ function wp_trim_words( $text, $num_words = 55, $more = null ) { } else { $text = implode( $sep, $words_array ); } + /** * Filter the text content after words have been trimmed. * @@ -2804,7 +2993,7 @@ function wp_trim_words( $text, $num_words = 55, $more = null ) { * @param string $text The text within which entities will be converted. * @return string Text with converted entities. */ -function ent2ncr($text) { +function ent2ncr( $text ) { /** * Filter text before named entities are converted into numbered entities. @@ -2817,7 +3006,7 @@ function ent2ncr($text) { * @param string $text The text prior to entity conversion. */ $filtered = apply_filters( 'pre_ent2ncr', null, $text ); - if( null !== $filtered ) + if ( null !== $filtered ) return $filtered; $to_ncr = array( @@ -3083,66 +3272,38 @@ function ent2ncr($text) { } /** - * Formats text for the rich text editor. + * Formats text for the editor. * - * The filter 'richedit_pre' is applied here. If $text is empty the filter will - * be applied to an empty string. + * Generally the browsers treat everything inside a textarea as text, but + * it is still a good idea to HTML entity encode `<`, `>` and `&` in the content. * - * @since 2.0.0 + * The filter {@see 'format_for_editor'} is applied here. If `$text` is empty the + * filter will be applied to an empty string. * - * @param string $text The text to be formatted. - * @return string The formatted text after filter is applied. - */ -function wp_richedit_pre($text) { - 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, get_option( 'blog_charset' ) ); - - /** This filter is documented in wp-includes/formatting.php */ - return apply_filters( 'richedit_pre', $output ); -} - -/** - * Formats text for the HTML editor. + * @since 4.3.0 * - * Unless $output is empty it will pass through htmlspecialchars before the - * 'htmledit_pre' filter is applied. - * - * @since 2.5.0 + * @see _WP_Editors::editor() * - * @param string $output The text to be formatted. - * @return string Formatted text after filter applied. + * @param string $text The text to be formatted. + * @param string $default_editor The default editor for the current user. + * It is usually either 'html' or 'tinymce'. + * @return string The formatted text after filter is applied. */ -function wp_htmledit_pre($output) { - if ( !empty($output) ) - $output = htmlspecialchars($output, ENT_NOQUOTES, get_option( 'blog_charset' ) ); // convert only < > & +function format_for_editor( $text, $default_editor = null ) { + if ( $text ) { + $text = htmlspecialchars( $text, ENT_NOQUOTES, get_option( 'blog_charset' ) ); + } /** - * Filter the text before it is formatted for the HTML editor. + * Filter the text after it is formatted for the editor. * - * @since 2.5.0 + * @since 4.3.0 * - * @param string $output The HTML-formatted text. + * @param string $text The formatted text. + * @param string $default_editor The default editor for the current user. + * It is usually either 'html' or 'tinymce'. */ - return apply_filters( 'htmledit_pre', $output ); + return apply_filters( 'format_for_editor', $text, $default_editor ); } /** @@ -3155,8 +3316,9 @@ function wp_htmledit_pre($output) { * @since 2.8.1 * @access private * - * @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. + * @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 ) { @@ -3178,6 +3340,9 @@ function _deep_replace( $search, $subject ) { * is preparing an array for use in an IN clause. * * @since 2.8.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * * @param string|array $data Unescaped data * @return string|array Escaped data */ @@ -3195,10 +3360,10 @@ function esc_sql( $data ) { * * @since 2.8.0 * - * @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. + * @param string $url The URL to be cleaned. + * @param array $protocols Optional. An array of acceptable protocols. + * Defaults to return value of wp_allowed_protocols() + * @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' ) { @@ -3206,12 +3371,22 @@ function esc_url( $url, $protocols = null, $_context = 'display' ) { 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( ' ', '%20', $url ); + $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url); + + if ( '' === $url ) { + return $url; + } + + if ( 0 !== stripos( $url, 'mailto:' ) ) { + $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 + * presume it needs http:// prepended (unless a relative * link starting with /, # or ? or a php file). */ if ( strpos($url, ':') === false && ! in_array( $url[0], array( '/', '#', '?' ) ) && @@ -3225,6 +3400,43 @@ function esc_url( $url, $protocols = null, $_context = 'display' ) { $url = str_replace( "'", ''', $url ); } + if ( ( false !== strpos( $url, '[' ) ) || ( false !== strpos( $url, ']' ) ) ) { + + $parsed = wp_parse_url( $url ); + $front = ''; + + if ( isset( $parsed['scheme'] ) ) { + $front .= $parsed['scheme'] . '://'; + } elseif ( '/' === $url[0] ) { + $front .= '//'; + } + + if ( isset( $parsed['user'] ) ) { + $front .= $parsed['user']; + } + + if ( isset( $parsed['pass'] ) ) { + $front .= ':' . $parsed['pass']; + } + + if ( isset( $parsed['user'] ) || isset( $parsed['pass'] ) ) { + $front .= '@'; + } + + if ( isset( $parsed['host'] ) ) { + $front .= $parsed['host']; + } + + if ( isset( $parsed['port'] ) ) { + $front .= ':' . $parsed['port']; + } + + $end_dirty = str_replace( $front, '', $url ); + $end_clean = str_replace( array( '[', ']' ), array( '%5B', '%5D' ), $end_dirty ); + $url = str_replace( $end_dirty, $end_clean, $url ); + + } + if ( '/' === $url[0] ) { $good_protocol_url = $url; } else { @@ -3252,8 +3464,8 @@ function esc_url( $url, $protocols = null, $_context = 'display' ) { * * @since 2.8.0 * - * @param string $url The URL to be cleaned. - * @param array $protocols An array of acceptable protocols. + * @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 ) { @@ -3270,7 +3482,7 @@ function esc_url_raw( $url, $protocols = null ) { * @param string $myHTML The text to be converted. * @return string Converted text. */ -function htmlentities2($myHTML) { +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) ); @@ -3387,7 +3599,7 @@ function esc_textarea( $text ) { * @param string $tag_name * @return string */ -function tag_escape($tag_name) { +function tag_escape( $tag_name ) { $safe_tag = strtolower( preg_replace('/[^a-zA-Z0-9_:]/', '', $tag_name) ); /** * Filter a string cleaned and escaped for output as an HTML tag. @@ -3413,7 +3625,7 @@ function tag_escape($tag_name) { * @return string Absolute path. */ function wp_make_link_relative( $link ) { - return preg_replace( '|^(https?:)?//[^/]+(/.*)|i', '$2', $link ); + return preg_replace( '|^(https?:)?//[^/]+(/?.*)|i', '$2', $link ); } /** @@ -3424,12 +3636,16 @@ function wp_make_link_relative( $link ) { * * @since 2.0.5 * + * @global wpdb $wpdb WordPress database abstraction object. + * * @param string $option The name of the option. - * @param string $value The unsanitised value. + * @param string $value The unsanitised value. * @return string Sanitized value. */ -function sanitize_option($option, $value) { +function sanitize_option( $option, $value ) { global $wpdb; + + $original_value = $value; $error = ''; switch ( $option ) { @@ -3450,6 +3666,8 @@ function sanitize_option($option, $value) { case 'thumbnail_size_h': case 'medium_size_w': case 'medium_size_h': + case 'medium_large_size_w': + case 'medium_large_size_h': case 'large_size_w': case 'large_size_h': case 'mailserver_port': @@ -3465,6 +3683,7 @@ function sanitize_option($option, $value) { case 'thread_comments_depth': case 'users_can_register': case 'start_of_week': + case 'site_icon': $value = absint( $value ); break; @@ -3490,7 +3709,6 @@ function sanitize_option($option, $value) { if ( is_wp_error( $value ) ) { $error = $value->get_error_message(); } else { - $value = wp_kses_post( $value ); $value = esc_html( $value ); } break; @@ -3655,11 +3873,39 @@ function sanitize_option($option, $value) { * Filter an option value following sanitization. * * @since 2.3.0 + * @since 4.3.0 Added the `$original_value` parameter. * - * @param string $value The sanitized option value. - * @param string $option The option name. + * @param string $value The sanitized option value. + * @param string $option The option name. + * @param string $original_value The original value passed to the function. */ - $value = apply_filters( "sanitize_option_{$option}", $value, $option ); + return apply_filters( "sanitize_option_{$option}", $value, $option, $original_value ); +} + +/** + * Maps a function to all non-iterable elements of an array or an object. + * + * This is similar to `array_walk_recursive()` but acts upon objects too. + * + * @since 4.4.0 + * + * @param mixed $value The array, object, or scalar. + * @param callable $callback The function to map onto $value. + * @return mixed The value with the callback applied to all non-arrays and non-objects inside it. + */ +function map_deep( $value, $callback ) { + if ( is_array( $value ) ) { + foreach ( $value as $index => $item ) { + $value[ $index ] = map_deep( $item, $callback ); + } + } elseif ( is_object( $value ) ) { + $object_vars = get_object_vars( $value ); + foreach ( $object_vars as $property_name => $property_value ) { + $value->$property_name = map_deep( $property_value, $callback ); + } + } else { + $value = call_user_func( $callback, $value ); + } return $value; } @@ -3673,7 +3919,7 @@ function sanitize_option($option, $value) { * @since 2.2.1 * * @param string $string The string to be parsed. - * @param array $array Variables will be stored in this array. + * @param array $array Variables will be stored in this array. */ function wp_parse_str( $string, &$array ) { parse_str( $string, $array ); @@ -3723,7 +3969,7 @@ function wp_pre_kses_less_than_callback( $matches ) { * @since 2.5.0 * @link http://www.php.net/sprintf * - * @param string $pattern The string which formatted args are inserted. + * @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. */ @@ -3798,10 +4044,10 @@ function wp_sprintf( $pattern ) { * @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'. + * @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) { +function wp_sprintf_l( $pattern, $args ) { // Not a match if ( substr($pattern, 0, 2) != '%l' ) return $pattern; @@ -3856,9 +4102,9 @@ function wp_sprintf_l($pattern, $args) { * * @since 2.5.0 * - * @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. + * @param string $str String to get the excerpt from. + * @param int $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, $more = null ) { @@ -3881,9 +4127,11 @@ function wp_html_excerpt( $str, $count, $more = null ) { * * @since 2.7.0 * + * @global string $_links_add_base + * * @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. + * @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') ) { @@ -3899,16 +4147,18 @@ function links_add_base_url( $content, $base, $attrs = array('src', 'href') ) { * @since 2.7.0 * @access private * + * @global string $_links_add_base + * * @param string $m The matched link. * @return string The processed link. */ -function _links_add_base($m) { +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] : - WP_HTTP::make_absolute_url( $m[3], $_links_add_base ) + WP_Http::make_absolute_url( $m[3], $_links_add_base ) ) . $m[2]; } @@ -3923,9 +4173,11 @@ function _links_add_base($m) { * * @since 2.7.0 * + * @global string $_links_add_target + * * @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. + * @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') ) { @@ -3941,6 +4193,8 @@ function links_add_target( $content, $target = '_blank', $tags = array('a') ) { * @since 2.7.0 * @access private * + * @global string $_links_add_target + * * @param string $m The matched link. * @return string The processed link. */ @@ -3975,8 +4229,8 @@ function normalize_whitespace( $str ) { * * @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 + * @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) { @@ -4003,7 +4257,7 @@ function wp_strip_all_tags($string, $remove_breaks = false) { * @param string $str * @return string */ -function sanitize_text_field($str) { +function sanitize_text_field( $str ) { $filtered = wp_check_invalid_utf8( $str ); if ( strpos($filtered, '<') !== false ) { @@ -4041,7 +4295,7 @@ function sanitize_text_field($str) { * * @since 3.1.0 * - * @param string $path A path. + * @param string $path A path. * @param string $suffix If the filename ends in suffix this will also be cut off. * @return string */ @@ -4055,6 +4309,11 @@ function wp_basename( $path, $suffix = '' ) { * Violating our coding standards for a good function name. * * @since 3.0.0 + * + * @staticvar string|false $dblq + * + * @param string $text The text to be modified. + * @return string The modified text. */ function capital_P_dangit( $text ) { // Simple replacement for titles @@ -4063,13 +4322,13 @@ function capital_P_dangit( $text ) { return str_replace( 'Wordpress', 'WordPress', $text ); // Still here? Use the more judicious replacement static $dblq = false; - if ( false === $dblq ) + 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 ); - } /** @@ -4171,7 +4430,7 @@ function wp_unslash( $value ) { * @since 3.6.0 * * @param string $content A string which might contain a URL. - * @return string The found URL. + * @return string|false The found URL. */ function get_url_in_content( $content ) { if ( empty( $content ) ) { @@ -4194,10 +4453,12 @@ function get_url_in_content( $content ) { * * @since 4.0.0 * + * @staticvar string $spaces + * * @return string The spaces regexp. */ function wp_spaces_regexp() { - static $spaces; + static $spaces = ''; if ( empty( $spaces ) ) { /** @@ -4222,6 +4483,8 @@ function wp_spaces_regexp() { * Print the important emoji-related styles. * * @since 4.2.0 + * + * @staticvar bool $printed */ function print_emoji_styles() { static $printed = false; @@ -4249,6 +4512,11 @@ img.emoji { <?php } +/** + * + * @global string $wp_version + * @staticvar bool $printed + */ function print_emoji_detection_script() { global $wp_version; static $printed = false; @@ -4267,7 +4535,7 @@ function print_emoji_detection_script() { * * @param string The emoji base URL. */ - 'baseUrl' => apply_filters( 'emoji_url', set_url_scheme( '//s.w.org/images/core/emoji/72x72/' ) ), + 'baseUrl' => apply_filters( 'emoji_url', 'https://s.w.org/images/core/emoji/72x72/' ), /** * Filter the extension of the emoji files. @@ -4280,9 +4548,8 @@ function print_emoji_detection_script() { ); $version = 'ver=' . $wp_version; - $develop_src = false !== strpos( $wp_version, '-src' ); - if ( $develop_src || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ) { + if ( SCRIPT_DEBUG ) { $settings['source'] = array( /** This filter is documented in wp-includes/class.wp-scripts.php */ 'wpemoji' => apply_filters( 'script_loader_src', includes_url( "js/wp-emoji.js?$version" ), 'wpemoji' ), @@ -4315,7 +4582,7 @@ function print_emoji_detection_script() { ?> <script type="text/javascript"> window._wpemojiSettings = <?php echo wp_json_encode( $settings ); ?>; - !function(a,b,c){function d(a){var c=b.createElement("canvas"),d=c.getContext&&c.getContext("2d");return d&&d.fillText?(d.textBaseline="top",d.font="600 32px Arial","flag"===a?(d.fillText(String.fromCharCode(55356,56812,55356,56807),0,0),c.toDataURL().length>3e3):(d.fillText(String.fromCharCode(55357,56835),0,0),0!==d.getImageData(16,16,1,1).data[0])):!1}function e(a){var c=b.createElement("script");c.src=a,c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var f,g;c.supports={simple:d("simple"),flag:d("flag")},c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.simple&&c.supports.flag||(g=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",g,!1),a.addEventListener("load",g,!1)):(a.attachEvent("onload",g),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),f=c.source||{},f.concatemoji?e(f.concatemoji):f.wpemoji&&f.twemoji&&(e(f.twemoji),e(f.wpemoji)))}(window,document,window._wpemojiSettings); + !function(a,b,c){function d(a){var c,d,e,f=b.createElement("canvas"),g=f.getContext&&f.getContext("2d"),h=String.fromCharCode;if(!g||!g.fillText)return!1;switch(g.textBaseline="top",g.font="600 32px Arial",a){case"flag":return g.fillText(h(55356,56806,55356,56826),0,0),f.toDataURL().length>3e3;case"diversity":return g.fillText(h(55356,57221),0,0),c=g.getImageData(16,16,1,1).data,d=c[0]+","+c[1]+","+c[2]+","+c[3],g.fillText(h(55356,57221,55356,57343),0,0),c=g.getImageData(16,16,1,1).data,e=c[0]+","+c[1]+","+c[2]+","+c[3],d!==e;case"simple":return g.fillText(h(55357,56835),0,0),0!==g.getImageData(16,16,1,1).data[0];case"unicode8":return g.fillText(h(55356,57135),0,0),0!==g.getImageData(16,16,1,1).data[0]}return!1}function e(a){var c=b.createElement("script");c.src=a,c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var f,g,h,i;for(i=Array("simple","flag","unicode8","diversity"),c.supports={everything:!0,everythingExceptFlag:!0},h=0;h<i.length;h++)c.supports[i[h]]=d(i[h]),c.supports.everything=c.supports.everything&&c.supports[i[h]],"flag"!==i[h]&&(c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&c.supports[i[h]]);c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&!c.supports.flag,c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.everything||(g=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",g,!1),a.addEventListener("load",g,!1)):(a.attachEvent("onload",g),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),f=c.source||{},f.concatemoji?e(f.concatemoji):f.wpemoji&&f.twemoji&&(e(f.twemoji),e(f.wpemoji)))}(window,document,window._wpemojiSettings); </script> <?php } @@ -4349,7 +4616,7 @@ function wp_encode_emoji( $content ) { $matches = array(); if ( preg_match_all( $regex, $content, $matches ) ) { if ( ! empty( $matches[1] ) ) { - foreach( $matches[1] as $emoji ) { + foreach ( $matches[1] as $emoji ) { /* * UTF-32's hex encoding is the same as HTML's hex encoding. * So, by converting the emoji from UTF-8 to UTF-32, we magically @@ -4380,7 +4647,7 @@ function wp_staticize_emoji( $text ) { $text = wp_encode_emoji( $text ); /** This filter is documented in wp-includes/formatting.php */ - $cdn_url = apply_filters( 'emoji_url', set_url_scheme( '//s.w.org/images/core/emoji/72x72/' ) ); + $cdn_url = apply_filters( 'emoji_url', 'https://s.w.org/images/core/emoji/72x72/' ); /** This filter is documented in wp-includes/formatting.php */ $ext = apply_filters( 'emoji_ext', '.png' ); @@ -4516,3 +4783,23 @@ function wp_staticize_emoji_for_email( $mail ) { return $mail; } + +/** + * Shorten a URL, to be used as link text. + * + * @since 1.2.0 + * @since 4.4.0 Moved to wp-includes/formatting.php from wp-admin/includes/misc.php and added $length param. + * + * @param string $url URL to shorten. + * @param int $length Optional. Maximum length of the shortened URL. Default 35 characters. + * @return string Shortened URL. + */ +function url_shorten( $url, $length = 35 ) { + $stripped = str_replace( array( 'https://', 'http://', 'www.' ), '', $url ); + $short_url = untrailingslashit( $stripped ); + + if ( strlen( $short_url ) > $length ) { + $short_url = substr( $short_url, 0, $length - 3 ) . '…'; + } + return $short_url; +}