· 7 years ago · Feb 05, 2019, 08:00 PM
1<?php
2/**
3 * Main WordPress Formatting API.
4 *
5 * Handles many functions for formatting output.
6 *
7 * @package WordPress
8 */
9
10/**
11 * Replaces common plain text characters into formatted entities
12 *
13 * As an example,
14 *
15 * 'cause today's effort makes it worth tomorrow's "holiday" ...
16 *
17 * Becomes:
18 *
19 * ’cause today’s effort makes it worth tomorrow’s “holiday” …
20 *
21 * Code within certain html blocks are skipped.
22 *
23 * Do not use this function before the {@see 'init'} action hook; everything will break.
24 *
25 * @since 0.71
26 *
27 * @global array $wp_cockneyreplace Array of formatted entities for certain common phrases
28 * @global array $shortcode_tags
29 * @staticvar array $static_characters
30 * @staticvar array $static_replacements
31 * @staticvar array $dynamic_characters
32 * @staticvar array $dynamic_replacements
33 * @staticvar array $default_no_texturize_tags
34 * @staticvar array $default_no_texturize_shortcodes
35 * @staticvar bool $run_texturize
36 * @staticvar string $apos
37 * @staticvar string $prime
38 * @staticvar string $double_prime
39 * @staticvar string $opening_quote
40 * @staticvar string $closing_quote
41 * @staticvar string $opening_single_quote
42 * @staticvar string $closing_single_quote
43 * @staticvar string $open_q_flag
44 * @staticvar string $open_sq_flag
45 * @staticvar string $apos_flag
46 *
47 * @param string $text The text to be formatted
48 * @param bool $reset Set to true for unit testing. Translated patterns will reset.
49 * @return string The string replaced with html entities
50 */
51function wptexturize( $text, $reset = false ) {
52 global $wp_cockneyreplace, $shortcode_tags;
53 static $static_characters = null,
54 $static_replacements = null,
55 $dynamic_characters = null,
56 $dynamic_replacements = null,
57 $default_no_texturize_tags = null,
58 $default_no_texturize_shortcodes = null,
59 $run_texturize = true,
60 $apos = null,
61 $prime = null,
62 $double_prime = null,
63 $opening_quote = null,
64 $closing_quote = null,
65 $opening_single_quote = null,
66 $closing_single_quote = null,
67 $open_q_flag = '<!--oq-->',
68 $open_sq_flag = '<!--osq-->',
69 $apos_flag = '<!--apos-->';
70
71 // If there's nothing to do, just stop.
72 if ( empty( $text ) || false === $run_texturize ) {
73 return $text;
74 }
75
76 // Set up static variables. Run once only.
77 if ( $reset || ! isset( $static_characters ) ) {
78 /**
79 * Filters whether to skip running wptexturize().
80 *
81 * Passing false to the filter will effectively short-circuit wptexturize().
82 * returning the original text passed to the function instead.
83 *
84 * The filter runs only once, the first time wptexturize() is called.
85 *
86 * @since 4.0.0
87 *
88 * @see wptexturize()
89 *
90 * @param bool $run_texturize Whether to short-circuit wptexturize().
91 */
92 $run_texturize = apply_filters( 'run_wptexturize', $run_texturize );
93 if ( false === $run_texturize ) {
94 return $text;
95 }
96
97 /* translators: opening curly double quote */
98 $opening_quote = _x( '“', 'opening curly double quote' );
99 /* translators: closing curly double quote */
100 $closing_quote = _x( '”', 'closing curly double quote' );
101
102 /* translators: apostrophe, for example in 'cause or can't */
103 $apos = _x( '’', 'apostrophe' );
104
105 /* translators: prime, for example in 9' (nine feet) */
106 $prime = _x( '′', 'prime' );
107 /* translators: double prime, for example in 9" (nine inches) */
108 $double_prime = _x( '″', 'double prime' );
109
110 /* translators: opening curly single quote */
111 $opening_single_quote = _x( '‘', 'opening curly single quote' );
112 /* translators: closing curly single quote */
113 $closing_single_quote = _x( '’', 'closing curly single quote' );
114
115 /* translators: en dash */
116 $en_dash = _x( '–', 'en dash' );
117 /* translators: em dash */
118 $em_dash = _x( '—', 'em dash' );
119
120 $default_no_texturize_tags = array('pre', 'code', 'kbd', 'style', 'script', 'tt');
121 $default_no_texturize_shortcodes = array('code');
122
123 // if a plugin has provided an autocorrect array, use it
124 if ( isset($wp_cockneyreplace) ) {
125 $cockney = array_keys( $wp_cockneyreplace );
126 $cockneyreplace = array_values( $wp_cockneyreplace );
127 } else {
128 /* translators: This is a comma-separated list of words that defy the syntax of quotations in normal use,
129 * for example... 'We do not have enough words yet' ... is a typical quoted phrase. But when we write
130 * lines of code 'til we have enough of 'em, then we need to insert apostrophes instead of quotes.
131 */
132 $cockney = explode( ',', _x( "'tain't,'twere,'twas,'tis,'twill,'til,'bout,'nuff,'round,'cause,'em",
133 'Comma-separated list of words to texturize in your language' ) );
134
135 $cockneyreplace = explode( ',', _x( '’tain’t,’twere,’twas,’tis,’twill,’til,’bout,’nuff,’round,’cause,’em',
136 'Comma-separated list of replacement words in your language' ) );
137 }
138
139 $static_characters = array_merge( array( '...', '``', '\'\'', ' (tm)' ), $cockney );
140 $static_replacements = array_merge( array( '…', $opening_quote, $closing_quote, ' ™' ), $cockneyreplace );
141
142
143 // Pattern-based replacements of characters.
144 // Sort the remaining patterns into several arrays for performance tuning.
145 $dynamic_characters = array( 'apos' => array(), 'quote' => array(), 'dash' => array() );
146 $dynamic_replacements = array( 'apos' => array(), 'quote' => array(), 'dash' => array() );
147 $dynamic = array();
148 $spaces = wp_spaces_regexp();
149
150 // '99' and '99" are ambiguous among other patterns; assume it's an abbreviated year at the end of a quotation.
151 if ( "'" !== $apos || "'" !== $closing_single_quote ) {
152 $dynamic[ '/\'(\d\d)\'(?=\Z|[.,:;!?)}\-\]]|>|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_single_quote;
153 }
154 if ( "'" !== $apos || '"' !== $closing_quote ) {
155 $dynamic[ '/\'(\d\d)"(?=\Z|[.,:;!?)}\-\]]|>|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_quote;
156 }
157
158 // '99 '99s '99's (apostrophe) But never '9 or '99% or '999 or '99.0.
159 if ( "'" !== $apos ) {
160 $dynamic[ '/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/' ] = $apos_flag;
161 }
162
163 // Quoted Numbers like '0.42'
164 if ( "'" !== $opening_single_quote && "'" !== $closing_single_quote ) {
165 $dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $open_sq_flag . '$1' . $closing_single_quote;
166 }
167
168 // Single quote at start, or preceded by (, {, <, [, ", -, or spaces.
169 if ( "'" !== $opening_single_quote ) {
170 $dynamic[ '/(?<=\A|[([{"\-]|<|' . $spaces . ')\'/' ] = $open_sq_flag;
171 }
172
173 // Apostrophe in a word. No spaces, double apostrophes, or other punctuation.
174 if ( "'" !== $apos ) {
175 $dynamic[ '/(?<!' . $spaces . ')\'(?!\Z|[.,:;!?"\'(){}[\]\-]|&[lg]t;|' . $spaces . ')/' ] = $apos_flag;
176 }
177
178 $dynamic_characters['apos'] = array_keys( $dynamic );
179 $dynamic_replacements['apos'] = array_values( $dynamic );
180 $dynamic = array();
181
182 // Quoted Numbers like "42"
183 if ( '"' !== $opening_quote && '"' !== $closing_quote ) {
184 $dynamic[ '/(?<=\A|' . $spaces . ')"(\d[.,\d]*)"/' ] = $open_q_flag . '$1' . $closing_quote;
185 }
186
187 // Double quote at start, or preceded by (, {, <, [, -, or spaces, and not followed by spaces.
188 if ( '"' !== $opening_quote ) {
189 $dynamic[ '/(?<=\A|[([{\-]|<|' . $spaces . ')"(?!' . $spaces . ')/' ] = $open_q_flag;
190 }
191
192 $dynamic_characters['quote'] = array_keys( $dynamic );
193 $dynamic_replacements['quote'] = array_values( $dynamic );
194 $dynamic = array();
195
196 // Dashes and spaces
197 $dynamic[ '/---/' ] = $em_dash;
198 $dynamic[ '/(?<=^|' . $spaces . ')--(?=$|' . $spaces . ')/' ] = $em_dash;
199 $dynamic[ '/(?<!xn)--/' ] = $en_dash;
200 $dynamic[ '/(?<=^|' . $spaces . ')-(?=$|' . $spaces . ')/' ] = $en_dash;
201
202 $dynamic_characters['dash'] = array_keys( $dynamic );
203 $dynamic_replacements['dash'] = array_values( $dynamic );
204 }
205
206 // Must do this every time in case plugins use these filters in a context sensitive manner
207 /**
208 * Filters the list of HTML elements not to texturize.
209 *
210 * @since 2.8.0
211 *
212 * @param array $default_no_texturize_tags An array of HTML element names.
213 */
214 $no_texturize_tags = apply_filters( 'no_texturize_tags', $default_no_texturize_tags );
215 /**
216 * Filters the list of shortcodes not to texturize.
217 *
218 * @since 2.8.0
219 *
220 * @param array $default_no_texturize_shortcodes An array of shortcode names.
221 */
222 $no_texturize_shortcodes = apply_filters( 'no_texturize_shortcodes', $default_no_texturize_shortcodes );
223
224 $no_texturize_tags_stack = array();
225 $no_texturize_shortcodes_stack = array();
226
227 // Look for shortcodes and HTML elements.
228
229 preg_match_all( '@\[/?([^<>&/\[\]\x00-\x20=]++)@', $text, $matches );
230 $tagnames = array_intersect( array_keys( $shortcode_tags ), $matches[1] );
231 $found_shortcodes = ! empty( $tagnames );
232 $shortcode_regex = $found_shortcodes ? _get_wptexturize_shortcode_regex( $tagnames ) : '';
233 $regex = _get_wptexturize_split_regex( $shortcode_regex );
234
235 $textarr = preg_split( $regex, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
236
237 foreach ( $textarr as &$curl ) {
238 // Only call _wptexturize_pushpop_element if $curl is a delimiter.
239 $first = $curl[0];
240 if ( '<' === $first ) {
241 if ( '<!--' === substr( $curl, 0, 4 ) ) {
242 // This is an HTML comment delimiter.
243 continue;
244 } else {
245 // This is an HTML element delimiter.
246
247 // Replace each & with & unless it already looks like an entity.
248 $curl = preg_replace( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', '&', $curl );
249
250 _wptexturize_pushpop_element( $curl, $no_texturize_tags_stack, $no_texturize_tags );
251 }
252
253 } elseif ( '' === trim( $curl ) ) {
254 // This is a newline between delimiters. Performance improves when we check this.
255 continue;
256
257 } elseif ( '[' === $first && $found_shortcodes && 1 === preg_match( '/^' . $shortcode_regex . '$/', $curl ) ) {
258 // This is a shortcode delimiter.
259
260 if ( '[[' !== substr( $curl, 0, 2 ) && ']]' !== substr( $curl, -2 ) ) {
261 // Looks like a normal shortcode.
262 _wptexturize_pushpop_element( $curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes );
263 } else {
264 // Looks like an escaped shortcode.
265 continue;
266 }
267
268 } elseif ( empty( $no_texturize_shortcodes_stack ) && empty( $no_texturize_tags_stack ) ) {
269 // This is neither a delimiter, nor is this content inside of no_texturize pairs. Do texturize.
270
271 $curl = str_replace( $static_characters, $static_replacements, $curl );
272
273 if ( false !== strpos( $curl, "'" ) ) {
274 $curl = preg_replace( $dynamic_characters['apos'], $dynamic_replacements['apos'], $curl );
275 $curl = wptexturize_primes( $curl, "'", $prime, $open_sq_flag, $closing_single_quote );
276 $curl = str_replace( $apos_flag, $apos, $curl );
277 $curl = str_replace( $open_sq_flag, $opening_single_quote, $curl );
278 }
279 if ( false !== strpos( $curl, '"' ) ) {
280 $curl = preg_replace( $dynamic_characters['quote'], $dynamic_replacements['quote'], $curl );
281 $curl = wptexturize_primes( $curl, '"', $double_prime, $open_q_flag, $closing_quote );
282 $curl = str_replace( $open_q_flag, $opening_quote, $curl );
283 }
284 if ( false !== strpos( $curl, '-' ) ) {
285 $curl = preg_replace( $dynamic_characters['dash'], $dynamic_replacements['dash'], $curl );
286 }
287
288 // 9x9 (times), but never 0x9999
289 if ( 1 === preg_match( '/(?<=\d)x\d/', $curl ) ) {
290 // Searching for a digit is 10 times more expensive than for the x, so we avoid doing this one!
291 $curl = preg_replace( '/\b(\d(?(?<=0)[\d\.,]+|[\d\.,]*))x(\d[\d\.,]*)\b/', '$1×$2', $curl );
292 }
293
294 // Replace each & with & unless it already looks like an entity.
295 $curl = preg_replace( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', '&', $curl );
296 }
297 }
298
299 return implode( '', $textarr );
300}
301
302/**
303 * Implements a logic tree to determine whether or not "7'." represents seven feet,
304 * then converts the special char into either a prime char or a closing quote char.
305 *
306 * @since 4.3.0
307 *
308 * @param string $haystack The plain text to be searched.
309 * @param string $needle The character to search for such as ' or ".
310 * @param string $prime The prime char to use for replacement.
311 * @param string $open_quote The opening quote char. Opening quote replacement must be
312 * accomplished already.
313 * @param string $close_quote The closing quote char to use for replacement.
314 * @return string The $haystack value after primes and quotes replacements.
315 */
316function wptexturize_primes( $haystack, $needle, $prime, $open_quote, $close_quote ) {
317 $spaces = wp_spaces_regexp();
318 $flag = '<!--wp-prime-or-quote-->';
319 $quote_pattern = "/$needle(?=\\Z|[.,:;!?)}\\-\\]]|>|" . $spaces . ")/";
320 $prime_pattern = "/(?<=\\d)$needle/";
321 $flag_after_digit = "/(?<=\\d)$flag/";
322 $flag_no_digit = "/(?<!\\d)$flag/";
323
324 $sentences = explode( $open_quote, $haystack );
325
326 foreach ( $sentences as $key => &$sentence ) {
327 if ( false === strpos( $sentence, $needle ) ) {
328 continue;
329 } elseif ( 0 !== $key && 0 === substr_count( $sentence, $close_quote ) ) {
330 $sentence = preg_replace( $quote_pattern, $flag, $sentence, -1, $count );
331 if ( $count > 1 ) {
332 // This sentence appears to have multiple closing quotes. Attempt Vulcan logic.
333 $sentence = preg_replace( $flag_no_digit, $close_quote, $sentence, -1, $count2 );
334 if ( 0 === $count2 ) {
335 // Try looking for a quote followed by a period.
336 $count2 = substr_count( $sentence, "$flag." );
337 if ( $count2 > 0 ) {
338 // Assume the rightmost quote-period match is the end of quotation.
339 $pos = strrpos( $sentence, "$flag." );
340 } else {
341 // When all else fails, make the rightmost candidate a closing quote.
342 // This is most likely to be problematic in the context of bug #18549.
343 $pos = strrpos( $sentence, $flag );
344 }
345 $sentence = substr_replace( $sentence, $close_quote, $pos, strlen( $flag ) );
346 }
347 // Use conventional replacement on any remaining primes and quotes.
348 $sentence = preg_replace( $prime_pattern, $prime, $sentence );
349 $sentence = preg_replace( $flag_after_digit, $prime, $sentence );
350 $sentence = str_replace( $flag, $close_quote, $sentence );
351 } elseif ( 1 == $count ) {
352 // Found only one closing quote candidate, so give it priority over primes.
353 $sentence = str_replace( $flag, $close_quote, $sentence );
354 $sentence = preg_replace( $prime_pattern, $prime, $sentence );
355 } else {
356 // No closing quotes found. Just run primes pattern.
357 $sentence = preg_replace( $prime_pattern, $prime, $sentence );
358 }
359 } else {
360 $sentence = preg_replace( $prime_pattern, $prime, $sentence );
361 $sentence = preg_replace( $quote_pattern, $close_quote, $sentence );
362 }
363 if ( '"' == $needle && false !== strpos( $sentence, '"' ) ) {
364 $sentence = str_replace( '"', $close_quote, $sentence );
365 }
366 }
367
368 return implode( $open_quote, $sentences );
369}
370
371/**
372 * Search for disabled element tags. Push element to stack on tag open and pop
373 * on tag close.
374 *
375 * Assumes first char of $text is tag opening and last char is tag closing.
376 * Assumes second char of $text is optionally '/' to indicate closing as in </html>.
377 *
378 * @since 2.9.0
379 * @access private
380 *
381 * @param string $text Text to check. Must be a tag like `<html>` or `[shortcode]`.
382 * @param array $stack List of open tag elements.
383 * @param array $disabled_elements The tag names to match against. Spaces are not allowed in tag names.
384 */
385function _wptexturize_pushpop_element( $text, &$stack, $disabled_elements ) {
386 // Is it an opening tag or closing tag?
387 if ( isset( $text[1] ) && '/' !== $text[1] ) {
388 $opening_tag = true;
389 $name_offset = 1;
390 } elseif ( 0 == count( $stack ) ) {
391 // Stack is empty. Just stop.
392 return;
393 } else {
394 $opening_tag = false;
395 $name_offset = 2;
396 }
397
398 // Parse out the tag name.
399 $space = strpos( $text, ' ' );
400 if ( false === $space ) {
401 $space = -1;
402 } else {
403 $space -= $name_offset;
404 }
405 $tag = substr( $text, $name_offset, $space );
406
407 // Handle disabled tags.
408 if ( in_array( $tag, $disabled_elements ) ) {
409 if ( $opening_tag ) {
410 /*
411 * This disables texturize until we find a closing tag of our type
412 * (e.g. <pre>) even if there was invalid nesting before that
413 *
414 * Example: in the case <pre>sadsadasd</code>"baba"</pre>
415 * "baba" won't be texturize
416 */
417
418 array_push( $stack, $tag );
419 } elseif ( end( $stack ) == $tag ) {
420 array_pop( $stack );
421 }
422 }
423}
424
425/**
426 * Replaces double line-breaks with paragraph elements.
427 *
428 * A group of regex replaces used to identify text formatted with newlines and
429 * replace double line-breaks with HTML paragraph tags. The remaining line-breaks
430 * after conversion become <<br />> tags, unless $br is set to '0' or 'false'.
431 *
432 * @since 0.71
433 *
434 * @param string $pee The text which has to be formatted.
435 * @param bool $br Optional. If set, this will convert all remaining line-breaks
436 * after paragraphing. Default true.
437 * @return string Text which has been converted into correct paragraph tags.
438 */
439function wpautop( $pee, $br = true ) {
440 $pre_tags = array();
441
442 if ( trim($pee) === '' )
443 return '';
444
445 // Just to make things a little easier, pad the end.
446 $pee = $pee . "\n";
447
448 /*
449 * Pre tags shouldn't be touched by autop.
450 * Replace pre tags with placeholders and bring them back after autop.
451 */
452 if ( strpos($pee, '<pre') !== false ) {
453 $pee_parts = explode( '</pre>', $pee );
454 $last_pee = array_pop($pee_parts);
455 $pee = '';
456 $i = 0;
457
458 foreach ( $pee_parts as $pee_part ) {
459 $start = strpos($pee_part, '<pre');
460
461 // Malformed html?
462 if ( $start === false ) {
463 $pee .= $pee_part;
464 continue;
465 }
466
467 $name = "<pre wp-pre-tag-$i></pre>";
468 $pre_tags[$name] = substr( $pee_part, $start ) . '</pre>';
469
470 $pee .= substr( $pee_part, 0, $start ) . $name;
471 $i++;
472 }
473
474 $pee .= $last_pee;
475 }
476 // Change multiple <br>s into two line breaks, which will turn into paragraphs.
477 $pee = preg_replace('|<br\s*/?>\s*<br\s*/?>|', "\n\n", $pee);
478
479 $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)';
480
481 // Add a double line break above block-level opening tags.
482 $pee = preg_replace('!(<' . $allblocks . '[\s/>])!', "\n\n$1", $pee);
483
484 // Add a double line break below block-level closing tags.
485 $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
486
487 // Standardize newline characters to "\n".
488 $pee = str_replace(array("\r\n", "\r"), "\n", $pee);
489
490 // Find newlines in all elements and add placeholders.
491 $pee = wp_replace_in_html_tags( $pee, array( "\n" => " <!-- wpnl --> " ) );
492
493 // Collapse line breaks before and after <option> elements so they don't get autop'd.
494 if ( strpos( $pee, '<option' ) !== false ) {
495 $pee = preg_replace( '|\s*<option|', '<option', $pee );
496 $pee = preg_replace( '|</option>\s*|', '</option>', $pee );
497 }
498
499 /*
500 * Collapse line breaks inside <object> elements, before <param> and <embed> elements
501 * so they don't get autop'd.
502 */
503 if ( strpos( $pee, '</object>' ) !== false ) {
504 $pee = preg_replace( '|(<object[^>]*>)\s*|', '$1', $pee );
505 $pee = preg_replace( '|\s*</object>|', '</object>', $pee );
506 $pee = preg_replace( '%\s*(</?(?:param|embed)[^>]*>)\s*%', '$1', $pee );
507 }
508
509 /*
510 * Collapse line breaks inside <audio> and <video> elements,
511 * before and after <source> and <track> elements.
512 */
513 if ( strpos( $pee, '<source' ) !== false || strpos( $pee, '<track' ) !== false ) {
514 $pee = preg_replace( '%([<\[](?:audio|video)[^>\]]*[>\]])\s*%', '$1', $pee );
515 $pee = preg_replace( '%\s*([<\[]/(?:audio|video)[>\]])%', '$1', $pee );
516 $pee = preg_replace( '%\s*(<(?:source|track)[^>]*>)\s*%', '$1', $pee );
517 }
518
519 // Collapse line breaks before and after <figcaption> elements.
520 if ( strpos( $pee, '<figcaption' ) !== false ) {
521 $pee = preg_replace( '|\s*(<figcaption[^>]*>)|', '$1', $pee );
522 $pee = preg_replace( '|</figcaption>\s*|', '</figcaption>', $pee );
523 }
524
525 // Remove more than two contiguous line breaks.
526 $pee = preg_replace("/\n\n+/", "\n\n", $pee);
527
528 // Split up the contents into an array of strings, separated by double line breaks.
529 $pees = preg_split('/\n\s*\n/', $pee, -1, PREG_SPLIT_NO_EMPTY);
530
531 // Reset $pee prior to rebuilding.
532 $pee = '';
533
534 // Rebuild the content as a string, wrapping every bit with a <p>.
535 foreach ( $pees as $tinkle ) {
536 $pee .= '<p>' . trim($tinkle, "\n") . "</p>\n";
537 }
538
539 // Under certain strange conditions it could create a P of entirely whitespace.
540 $pee = preg_replace('|<p>\s*</p>|', '', $pee);
541
542 // Add a closing <p> inside <div>, <address>, or <form> tag if missing.
543 $pee = preg_replace('!<p>([^<]+)</(div|address|form)>!', "<p>$1</p></$2>", $pee);
544
545 // If an opening or closing block element tag is wrapped in a <p>, unwrap it.
546 $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
547
548 // In some cases <li> may get wrapped in <p>, fix them.
549 $pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee);
550
551 // If a <blockquote> is wrapped with a <p>, move it inside the <blockquote>.
552 $pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
553 $pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
554
555 // If an opening or closing block element tag is preceded by an opening <p> tag, remove it.
556 $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
557
558 // If an opening or closing block element tag is followed by a closing <p> tag, remove it.
559 $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
560
561 // Optionally insert line breaks.
562 if ( $br ) {
563 // Replace newlines that shouldn't be touched with a placeholder.
564 $pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', '_autop_newline_preservation_helper', $pee);
565
566 // Normalize <br>
567 $pee = str_replace( array( '<br>', '<br/>' ), '<br />', $pee );
568
569 // Replace any new line characters that aren't preceded by a <br /> with a <br />.
570 $pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee);
571
572 // Replace newline placeholders with newlines.
573 $pee = str_replace('<WPPreserveNewline />', "\n", $pee);
574 }
575
576 // If a <br /> tag is after an opening or closing block tag, remove it.
577 $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee);
578
579 // If a <br /> tag is before a subset of opening or closing block tags, remove it.
580 $pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
581 $pee = preg_replace( "|\n</p>$|", '</p>', $pee );
582
583 // Replace placeholder <pre> tags with their original content.
584 if ( !empty($pre_tags) )
585 $pee = str_replace(array_keys($pre_tags), array_values($pre_tags), $pee);
586
587 // Restore newlines in all elements.
588 if ( false !== strpos( $pee, '<!-- wpnl -->' ) ) {
589 $pee = str_replace( array( ' <!-- wpnl --> ', '<!-- wpnl -->' ), "\n", $pee );
590 }
591
592 return $pee;
593}
594
595/**
596 * Separate HTML elements and comments from the text.
597 *
598 * @since 4.2.4
599 *
600 * @param string $input The text which has to be formatted.
601 * @return array The formatted text.
602 */
603function wp_html_split( $input ) {
604 return preg_split( get_html_split_regex(), $input, -1, PREG_SPLIT_DELIM_CAPTURE );
605}
606
607/**
608 * Retrieve the regular expression for an HTML element.
609 *
610 * @since 4.4.0
611 *
612 * @staticvar string $regex
613 *
614 * @return string The regular expression
615 */
616function get_html_split_regex() {
617 static $regex;
618
619 if ( ! isset( $regex ) ) {
620 $comments =
621 '!' // Start of comment, after the <.
622 . '(?:' // Unroll the loop: Consume everything until --> is found.
623 . '-(?!->)' // Dash not followed by end of comment.
624 . '[^\-]*+' // Consume non-dashes.
625 . ')*+' // Loop possessively.
626 . '(?:-->)?'; // End of comment. If not found, match all input.
627
628 $cdata =
629 '!\[CDATA\[' // Start of comment, after the <.
630 . '[^\]]*+' // Consume non-].
631 . '(?:' // Unroll the loop: Consume everything until ]]> is found.
632 . '](?!]>)' // One ] not followed by end of comment.
633 . '[^\]]*+' // Consume non-].
634 . ')*+' // Loop possessively.
635 . '(?:]]>)?'; // End of comment. If not found, match all input.
636
637 $escaped =
638 '(?=' // Is the element escaped?
639 . '!--'
640 . '|'
641 . '!\[CDATA\['
642 . ')'
643 . '(?(?=!-)' // If yes, which type?
644 . $comments
645 . '|'
646 . $cdata
647 . ')';
648
649 $regex =
650 '/(' // Capture the entire match.
651 . '<' // Find start of element.
652 . '(?' // Conditional expression follows.
653 . $escaped // Find end of escaped element.
654 . '|' // ... else ...
655 . '[^>]*>?' // Find end of normal element.
656 . ')'
657 . ')/';
658 }
659
660 return $regex;
661}
662
663/**
664 * Retrieve the combined regular expression for HTML and shortcodes.
665 *
666 * @access private
667 * @ignore
668 * @internal This function will be removed in 4.5.0 per Shortcode API Roadmap.
669 * @since 4.4.0
670 *
671 * @staticvar string $html_regex
672 *
673 * @param string $shortcode_regex The result from _get_wptexturize_shortcode_regex(). Optional.
674 * @return string The regular expression
675 */
676function _get_wptexturize_split_regex( $shortcode_regex = '' ) {
677 static $html_regex;
678
679 if ( ! isset( $html_regex ) ) {
680 $comment_regex =
681 '!' // Start of comment, after the <.
682 . '(?:' // Unroll the loop: Consume everything until --> is found.
683 . '-(?!->)' // Dash not followed by end of comment.
684 . '[^\-]*+' // Consume non-dashes.
685 . ')*+' // Loop possessively.
686 . '(?:-->)?'; // End of comment. If not found, match all input.
687
688 $html_regex = // Needs replaced with wp_html_split() per Shortcode API Roadmap.
689 '<' // Find start of element.
690 . '(?(?=!--)' // Is this a comment?
691 . $comment_regex // Find end of comment.
692 . '|'
693 . '[^>]*>?' // Find end of element. If not found, match all input.
694 . ')';
695 }
696
697 if ( empty( $shortcode_regex ) ) {
698 $regex = '/(' . $html_regex . ')/';
699 } else {
700 $regex = '/(' . $html_regex . '|' . $shortcode_regex . ')/';
701 }
702
703 return $regex;
704}
705
706/**
707 * Retrieve the regular expression for shortcodes.
708 *
709 * @access private
710 * @ignore
711 * @internal This function will be removed in 4.5.0 per Shortcode API Roadmap.
712 * @since 4.4.0
713 *
714 * @param array $tagnames List of shortcodes to find.
715 * @return string The regular expression
716 */
717function _get_wptexturize_shortcode_regex( $tagnames ) {
718 $tagregexp = join( '|', array_map( 'preg_quote', $tagnames ) );
719 $tagregexp = "(?:$tagregexp)(?=[\\s\\]\\/])"; // Excerpt of get_shortcode_regex().
720 $regex =
721 '\[' // Find start of shortcode.
722 . '[\/\[]?' // Shortcodes may begin with [/ or [[
723 . $tagregexp // Only match registered shortcodes, because performance.
724 . '(?:'
725 . '[^\[\]<>]+' // Shortcodes do not contain other shortcodes. Quantifier critical.
726 . '|'
727 . '<[^\[\]>]*>' // HTML elements permitted. Prevents matching ] before >.
728 . ')*+' // Possessive critical.
729 . '\]' // Find end of shortcode.
730 . '\]?'; // Shortcodes may end with ]]
731
732 return $regex;
733}
734
735/**
736 * Replace characters or phrases within HTML elements only.
737 *
738 * @since 4.2.3
739 *
740 * @param string $haystack The text which has to be formatted.
741 * @param array $replace_pairs In the form array('from' => 'to', ...).
742 * @return string The formatted text.
743 */
744function wp_replace_in_html_tags( $haystack, $replace_pairs ) {
745 // Find all elements.
746 $textarr = wp_html_split( $haystack );
747 $changed = false;
748
749 // Optimize when searching for one item.
750 if ( 1 === count( $replace_pairs ) ) {
751 // Extract $needle and $replace.
752 foreach ( $replace_pairs as $needle => $replace );
753
754 // Loop through delimiters (elements) only.
755 for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
756 if ( false !== strpos( $textarr[$i], $needle ) ) {
757 $textarr[$i] = str_replace( $needle, $replace, $textarr[$i] );
758 $changed = true;
759 }
760 }
761 } else {
762 // Extract all $needles.
763 $needles = array_keys( $replace_pairs );
764
765 // Loop through delimiters (elements) only.
766 for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
767 foreach ( $needles as $needle ) {
768 if ( false !== strpos( $textarr[$i], $needle ) ) {
769 $textarr[$i] = strtr( $textarr[$i], $replace_pairs );
770 $changed = true;
771 // After one strtr() break out of the foreach loop and look at next element.
772 break;
773 }
774 }
775 }
776 }
777
778 if ( $changed ) {
779 $haystack = implode( $textarr );
780 }
781
782 return $haystack;
783}
784
785/**
786 * Newline preservation help function for wpautop
787 *
788 * @since 3.1.0
789 * @access private
790 *
791 * @param array $matches preg_replace_callback matches array
792 * @return string
793 */
794function _autop_newline_preservation_helper( $matches ) {
795 return str_replace( "\n", "<WPPreserveNewline />", $matches[0] );
796}
797
798/**
799 * Don't auto-p wrap shortcodes that stand alone
800 *
801 * Ensures that shortcodes are not wrapped in `<p>...</p>`.
802 *
803 * @since 2.9.0
804 *
805 * @global array $shortcode_tags
806 *
807 * @param string $pee The content.
808 * @return string The filtered content.
809 */
810function shortcode_unautop( $pee ) {
811 global $shortcode_tags;
812
813 if ( empty( $shortcode_tags ) || !is_array( $shortcode_tags ) ) {
814 return $pee;
815 }
816
817 $tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) );
818 $spaces = wp_spaces_regexp();
819
820 $pattern =
821 '/'
822 . '<p>' // Opening paragraph
823 . '(?:' . $spaces . ')*+' // Optional leading whitespace
824 . '(' // 1: The shortcode
825 . '\\[' // Opening bracket
826 . "($tagregexp)" // 2: Shortcode name
827 . '(?![\\w-])' // Not followed by word character or hyphen
828 // Unroll the loop: Inside the opening shortcode tag
829 . '[^\\]\\/]*' // Not a closing bracket or forward slash
830 . '(?:'
831 . '\\/(?!\\])' // A forward slash not followed by a closing bracket
832 . '[^\\]\\/]*' // Not a closing bracket or forward slash
833 . ')*?'
834 . '(?:'
835 . '\\/\\]' // Self closing tag and closing bracket
836 . '|'
837 . '\\]' // Closing bracket
838 . '(?:' // Unroll the loop: Optionally, anything between the opening and closing shortcode tags
839 . '[^\\[]*+' // Not an opening bracket
840 . '(?:'
841 . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag
842 . '[^\\[]*+' // Not an opening bracket
843 . ')*+'
844 . '\\[\\/\\2\\]' // Closing shortcode tag
845 . ')?'
846 . ')'
847 . ')'
848 . '(?:' . $spaces . ')*+' // optional trailing whitespace
849 . '<\\/p>' // closing paragraph
850 . '/';
851
852 return preg_replace( $pattern, '$1', $pee );
853}
854
855/**
856 * Checks to see if a string is utf8 encoded.
857 *
858 * NOTE: This function checks for 5-Byte sequences, UTF8
859 * has Bytes Sequences with a maximum length of 4.
860 *
861 * @author bmorel at ssi dot fr (modified)
862 * @since 1.2.1
863 *
864 * @param string $str The string to be checked
865 * @return bool True if $str fits a UTF-8 model, false otherwise.
866 */
867function seems_utf8( $str ) {
868 mbstring_binary_safe_encoding();
869 $length = strlen($str);
870 reset_mbstring_encoding();
871 for ($i=0; $i < $length; $i++) {
872 $c = ord($str[$i]);
873 if ($c < 0x80) $n = 0; // 0bbbbbbb
874 elseif (($c & 0xE0) == 0xC0) $n=1; // 110bbbbb
875 elseif (($c & 0xF0) == 0xE0) $n=2; // 1110bbbb
876 elseif (($c & 0xF8) == 0xF0) $n=3; // 11110bbb
877 elseif (($c & 0xFC) == 0xF8) $n=4; // 111110bb
878 elseif (($c & 0xFE) == 0xFC) $n=5; // 1111110b
879 else return false; // Does not match any model
880 for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
881 if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
882 return false;
883 }
884 }
885 return true;
886}
887
888/**
889 * Converts a number of special characters into their HTML entities.
890 *
891 * Specifically deals with: &, <, >, ", and '.
892 *
893 * $quote_style can be set to ENT_COMPAT to encode " to
894 * ", or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded.
895 *
896 * @since 1.2.2
897 * @access private
898 *
899 * @staticvar string $_charset
900 *
901 * @param string $string The text which is to be encoded.
902 * @param int|string $quote_style Optional. Converts double quotes if set to ENT_COMPAT,
903 * both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES.
904 * Also compatible with old values; converting single quotes if set to 'single',
905 * double if set to 'double' or both if otherwise set.
906 * Default is ENT_NOQUOTES.
907 * @param string $charset Optional. The character encoding of the string. Default is false.
908 * @param bool $double_encode Optional. Whether to encode existing html entities. Default is false.
909 * @return string The encoded text with HTML entities.
910 */
911function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) {
912 $string = (string) $string;
913
914 if ( 0 === strlen( $string ) )
915 return '';
916
917 // Don't bother if there are no specialchars - saves some processing
918 if ( ! preg_match( '/[&<>"\']/', $string ) )
919 return $string;
920
921 // Account for the previous behaviour of the function when the $quote_style is not an accepted value
922 if ( empty( $quote_style ) )
923 $quote_style = ENT_NOQUOTES;
924 elseif ( ! in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) )
925 $quote_style = ENT_QUOTES;
926
927 // Store the site charset as a static to avoid multiple calls to wp_load_alloptions()
928 if ( ! $charset ) {
929 static $_charset = null;
930 if ( ! isset( $_charset ) ) {
931 $alloptions = wp_load_alloptions();
932 $_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : '';
933 }
934 $charset = $_charset;
935 }
936
937 if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) )
938 $charset = 'UTF-8';
939
940 $_quote_style = $quote_style;
941
942 if ( $quote_style === 'double' ) {
943 $quote_style = ENT_COMPAT;
944 $_quote_style = ENT_COMPAT;
945 } elseif ( $quote_style === 'single' ) {
946 $quote_style = ENT_NOQUOTES;
947 }
948
949 if ( ! $double_encode ) {
950 // Guarantee every &entity; is valid, convert &garbage; into &garbage;
951 // This is required for PHP < 5.4.0 because ENT_HTML401 flag is unavailable.
952 $string = wp_kses_normalize_entities( $string );
953 }
954
955 $string = @htmlspecialchars( $string, $quote_style, $charset, $double_encode );
956
957 // Back-compat.
958 if ( 'single' === $_quote_style )
959 $string = str_replace( "'", ''', $string );
960
961 return $string;
962}
963
964/**
965 * Converts a number of HTML entities into their special characters.
966 *
967 * Specifically deals with: &, <, >, ", and '.
968 *
969 * $quote_style can be set to ENT_COMPAT to decode " entities,
970 * or ENT_QUOTES to do both " and '. Default is ENT_NOQUOTES where no quotes are decoded.
971 *
972 * @since 2.8.0
973 *
974 * @param string $string The text which is to be decoded.
975 * @param string|int $quote_style Optional. Converts double quotes if set to ENT_COMPAT,
976 * both single and double if set to ENT_QUOTES or
977 * none if set to ENT_NOQUOTES.
978 * Also compatible with old _wp_specialchars() values;
979 * converting single quotes if set to 'single',
980 * double if set to 'double' or both if otherwise set.
981 * Default is ENT_NOQUOTES.
982 * @return string The decoded text without HTML entities.
983 */
984function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) {
985 $string = (string) $string;
986
987 if ( 0 === strlen( $string ) ) {
988 return '';
989 }
990
991 // Don't bother if there are no entities - saves a lot of processing
992 if ( strpos( $string, '&' ) === false ) {
993 return $string;
994 }
995
996 // Match the previous behaviour of _wp_specialchars() when the $quote_style is not an accepted value
997 if ( empty( $quote_style ) ) {
998 $quote_style = ENT_NOQUOTES;
999 } elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
1000 $quote_style = ENT_QUOTES;
1001 }
1002
1003 // More complete than get_html_translation_table( HTML_SPECIALCHARS )
1004 $single = array( ''' => '\'', ''' => '\'' );
1005 $single_preg = array( '/�*39;/' => ''', '/�*27;/i' => ''' );
1006 $double = array( '"' => '"', '"' => '"', '"' => '"' );
1007 $double_preg = array( '/�*34;/' => '"', '/�*22;/i' => '"' );
1008 $others = array( '<' => '<', '<' => '<', '>' => '>', '>' => '>', '&' => '&', '&' => '&', '&' => '&' );
1009 $others_preg = array( '/�*60;/' => '<', '/�*62;/' => '>', '/�*38;/' => '&', '/�*26;/i' => '&' );
1010
1011 if ( $quote_style === ENT_QUOTES ) {
1012 $translation = array_merge( $single, $double, $others );
1013 $translation_preg = array_merge( $single_preg, $double_preg, $others_preg );
1014 } elseif ( $quote_style === ENT_COMPAT || $quote_style === 'double' ) {
1015 $translation = array_merge( $double, $others );
1016 $translation_preg = array_merge( $double_preg, $others_preg );
1017 } elseif ( $quote_style === 'single' ) {
1018 $translation = array_merge( $single, $others );
1019 $translation_preg = array_merge( $single_preg, $others_preg );
1020 } elseif ( $quote_style === ENT_NOQUOTES ) {
1021 $translation = $others;
1022 $translation_preg = $others_preg;
1023 }
1024
1025 // Remove zero padding on numeric entities
1026 $string = preg_replace( array_keys( $translation_preg ), array_values( $translation_preg ), $string );
1027
1028 // Replace characters according to translation table
1029 return strtr( $string, $translation );
1030}
1031
1032/**
1033 * Checks for invalid UTF8 in a string.
1034 *
1035 * @since 2.8.0
1036 *
1037 * @staticvar bool $is_utf8
1038 * @staticvar bool $utf8_pcre
1039 *
1040 * @param string $string The text which is to be checked.
1041 * @param bool $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false.
1042 * @return string The checked text.
1043 */
1044function wp_check_invalid_utf8( $string, $strip = false ) {
1045 $string = (string) $string;
1046
1047 if ( 0 === strlen( $string ) ) {
1048 return '';
1049 }
1050
1051 // Store the site charset as a static to avoid multiple calls to get_option()
1052 static $is_utf8 = null;
1053 if ( ! isset( $is_utf8 ) ) {
1054 $is_utf8 = in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) );
1055 }
1056 if ( ! $is_utf8 ) {
1057 return $string;
1058 }
1059
1060 // Check for support for utf8 in the installed PCRE library once and store the result in a static
1061 static $utf8_pcre = null;
1062 if ( ! isset( $utf8_pcre ) ) {
1063 $utf8_pcre = @preg_match( '/^./u', 'a' );
1064 }
1065 // We can't demand utf8 in the PCRE installation, so just return the string in those cases
1066 if ( !$utf8_pcre ) {
1067 return $string;
1068 }
1069
1070 // preg_match fails when it encounters invalid UTF8 in $string
1071 if ( 1 === @preg_match( '/^./us', $string ) ) {
1072 return $string;
1073 }
1074
1075 // Attempt to strip the bad chars if requested (not recommended)
1076 if ( $strip && function_exists( 'iconv' ) ) {
1077 return iconv( 'utf-8', 'utf-8', $string );
1078 }
1079
1080 return '';
1081}
1082
1083/**
1084 * Encode the Unicode values to be used in the URI.
1085 *
1086 * @since 1.5.0
1087 *
1088 * @param string $utf8_string
1089 * @param int $length Max length of the string
1090 * @return string String with Unicode encoded for URI.
1091 */
1092function utf8_uri_encode( $utf8_string, $length = 0 ) {
1093 $unicode = '';
1094 $values = array();
1095 $num_octets = 1;
1096 $unicode_length = 0;
1097
1098 mbstring_binary_safe_encoding();
1099 $string_length = strlen( $utf8_string );
1100 reset_mbstring_encoding();
1101
1102 for ($i = 0; $i < $string_length; $i++ ) {
1103
1104 $value = ord( $utf8_string[ $i ] );
1105
1106 if ( $value < 128 ) {
1107 if ( $length && ( $unicode_length >= $length ) )
1108 break;
1109 $unicode .= chr($value);
1110 $unicode_length++;
1111 } else {
1112 if ( count( $values ) == 0 ) {
1113 if ( $value < 224 ) {
1114 $num_octets = 2;
1115 } elseif ( $value < 240 ) {
1116 $num_octets = 3;
1117 } else {
1118 $num_octets = 4;
1119 }
1120 }
1121
1122 $values[] = $value;
1123
1124 if ( $length && ( $unicode_length + ($num_octets * 3) ) > $length )
1125 break;
1126 if ( count( $values ) == $num_octets ) {
1127 for ( $j = 0; $j < $num_octets; $j++ ) {
1128 $unicode .= '%' . dechex( $values[ $j ] );
1129 }
1130
1131 $unicode_length += $num_octets * 3;
1132
1133 $values = array();
1134 $num_octets = 1;
1135 }
1136 }
1137 }
1138
1139 return $unicode;
1140}
1141
1142/**
1143 * Converts all accent characters to ASCII characters.
1144 *
1145 * If there are no accent characters, then the string given is just returned.
1146 *
1147 * **Accent characters converted:**
1148 *
1149 * Currency signs:
1150 *
1151 * | Code | Glyph | Replacement | Description |
1152 * | -------- | ----- | ----------- | ------------------- |
1153 * | U+00A3 | £ | (empty) | British Pound sign |
1154 * | U+20AC | € | E | Euro sign |
1155 *
1156 * Decompositions for Latin-1 Supplement:
1157 *
1158 * | Code | Glyph | Replacement | Description |
1159 * | ------- | ----- | ----------- | -------------------------------------- |
1160 * | U+00AA | ª | a | Feminine ordinal indicator |
1161 * | U+00BA | º | o | Masculine ordinal indicator |
1162 * | U+00C0 | À | A | Latin capital letter A with grave |
1163 * | U+00C1 | Ã | A | Latin capital letter A with acute |
1164 * | U+00C2 | Â | A | Latin capital letter A with circumflex |
1165 * | U+00C3 | Ã | A | Latin capital letter A with tilde |
1166 * | U+00C4 | Ä | A | Latin capital letter A with diaeresis |
1167 * | U+00C5 | Ã… | A | Latin capital letter A with ring above |
1168 * | U+00C6 | Æ | AE | Latin capital letter AE |
1169 * | U+00C7 | Ç | C | Latin capital letter C with cedilla |
1170 * | U+00C8 | È | E | Latin capital letter E with grave |
1171 * | U+00C9 | É | E | Latin capital letter E with acute |
1172 * | U+00CA | Ê | E | Latin capital letter E with circumflex |
1173 * | U+00CB | Ë | E | Latin capital letter E with diaeresis |
1174 * | U+00CC | Ì | I | Latin capital letter I with grave |
1175 * | U+00CD | Ã | I | Latin capital letter I with acute |
1176 * | U+00CE | ÃŽ | I | Latin capital letter I with circumflex |
1177 * | U+00CF | Ã | I | Latin capital letter I with diaeresis |
1178 * | U+00D0 | Ã | D | Latin capital letter Eth |
1179 * | U+00D1 | Ñ | N | Latin capital letter N with tilde |
1180 * | U+00D2 | Ã’ | O | Latin capital letter O with grave |
1181 * | U+00D3 | Ó | O | Latin capital letter O with acute |
1182 * | U+00D4 | Ô | O | Latin capital letter O with circumflex |
1183 * | U+00D5 | Õ | O | Latin capital letter O with tilde |
1184 * | U+00D6 | Ö | O | Latin capital letter O with diaeresis |
1185 * | U+00D8 | Ø | O | Latin capital letter O with stroke |
1186 * | U+00D9 | Ù | U | Latin capital letter U with grave |
1187 * | U+00DA | Ú | U | Latin capital letter U with acute |
1188 * | U+00DB | Û | U | Latin capital letter U with circumflex |
1189 * | U+00DC | Ü | U | Latin capital letter U with diaeresis |
1190 * | U+00DD | Ã | Y | Latin capital letter Y with acute |
1191 * | U+00DE | Þ | TH | Latin capital letter Thorn |
1192 * | U+00DF | ß | s | Latin small letter sharp s |
1193 * | U+00E0 | Ã | a | Latin small letter a with grave |
1194 * | U+00E1 | á | a | Latin small letter a with acute |
1195 * | U+00E2 | â | a | Latin small letter a with circumflex |
1196 * | U+00E3 | ã | a | Latin small letter a with tilde |
1197 * | U+00E4 | ä | a | Latin small letter a with diaeresis |
1198 * | U+00E5 | å | a | Latin small letter a with ring above |
1199 * | U+00E6 | æ | ae | Latin small letter ae |
1200 * | U+00E7 | ç | c | Latin small letter c with cedilla |
1201 * | U+00E8 | è | e | Latin small letter e with grave |
1202 * | U+00E9 | é | e | Latin small letter e with acute |
1203 * | U+00EA | ê | e | Latin small letter e with circumflex |
1204 * | U+00EB | ë | e | Latin small letter e with diaeresis |
1205 * | U+00EC | ì | i | Latin small letter i with grave |
1206 * | U+00ED | Ã | i | Latin small letter i with acute |
1207 * | U+00EE | î | i | Latin small letter i with circumflex |
1208 * | U+00EF | ï | i | Latin small letter i with diaeresis |
1209 * | U+00F0 | ð | d | Latin small letter Eth |
1210 * | U+00F1 | ñ | n | Latin small letter n with tilde |
1211 * | U+00F2 | ò | o | Latin small letter o with grave |
1212 * | U+00F3 | ó | o | Latin small letter o with acute |
1213 * | U+00F4 | ô | o | Latin small letter o with circumflex |
1214 * | U+00F5 | õ | o | Latin small letter o with tilde |
1215 * | U+00F6 | ö | o | Latin small letter o with diaeresis |
1216 * | U+00F8 | ø | o | Latin small letter o with stroke |
1217 * | U+00F9 | ù | u | Latin small letter u with grave |
1218 * | U+00FA | ú | u | Latin small letter u with acute |
1219 * | U+00FB | û | u | Latin small letter u with circumflex |
1220 * | U+00FC | ü | u | Latin small letter u with diaeresis |
1221 * | U+00FD | ý | y | Latin small letter y with acute |
1222 * | U+00FE | þ | th | Latin small letter Thorn |
1223 * | U+00FF | ÿ | y | Latin small letter y with diaeresis |
1224 *
1225 * Decompositions for Latin Extended-A:
1226 *
1227 * | Code | Glyph | Replacement | Description |
1228 * | ------- | ----- | ----------- | ------------------------------------------------- |
1229 * | U+0100 | Ā | A | Latin capital letter A with macron |
1230 * | U+0101 | Ä | a | Latin small letter a with macron |
1231 * | U+0102 | Ä‚ | A | Latin capital letter A with breve |
1232 * | U+0103 | ă | a | Latin small letter a with breve |
1233 * | U+0104 | Ä„ | A | Latin capital letter A with ogonek |
1234 * | U+0105 | Ä… | a | Latin small letter a with ogonek |
1235 * | U+01006 | Ć | C | Latin capital letter C with acute |
1236 * | U+0107 | ć | c | Latin small letter c with acute |
1237 * | U+0108 | Ĉ | C | Latin capital letter C with circumflex |
1238 * | U+0109 | ĉ | c | Latin small letter c with circumflex |
1239 * | U+010A | ÄŠ | C | Latin capital letter C with dot above |
1240 * | U+010B | Ä‹ | c | Latin small letter c with dot above |
1241 * | U+010C | Č | C | Latin capital letter C with caron |
1242 * | U+010D | Ä | c | Latin small letter c with caron |
1243 * | U+010E | ÄŽ | D | Latin capital letter D with caron |
1244 * | U+010F | Ä | d | Latin small letter d with caron |
1245 * | U+0110 | Ä | D | Latin capital letter D with stroke |
1246 * | U+0111 | Ä‘ | d | Latin small letter d with stroke |
1247 * | U+0112 | Ä’ | E | Latin capital letter E with macron |
1248 * | U+0113 | Ä“ | e | Latin small letter e with macron |
1249 * | U+0114 | Ä” | E | Latin capital letter E with breve |
1250 * | U+0115 | Ä• | e | Latin small letter e with breve |
1251 * | U+0116 | Ä– | E | Latin capital letter E with dot above |
1252 * | U+0117 | Ä— | e | Latin small letter e with dot above |
1253 * | U+0118 | Ę | E | Latin capital letter E with ogonek |
1254 * | U+0119 | Ä™ | e | Latin small letter e with ogonek |
1255 * | U+011A | Äš | E | Latin capital letter E with caron |
1256 * | U+011B | Ä› | e | Latin small letter e with caron |
1257 * | U+011C | Ĝ | G | Latin capital letter G with circumflex |
1258 * | U+011D | Ä | g | Latin small letter g with circumflex |
1259 * | U+011E | Äž | G | Latin capital letter G with breve |
1260 * | U+011F | ÄŸ | g | Latin small letter g with breve |
1261 * | U+0120 | Ä | G | Latin capital letter G with dot above |
1262 * | U+0121 | Ä¡ | g | Latin small letter g with dot above |
1263 * | U+0122 | Ģ | G | Latin capital letter G with cedilla |
1264 * | U+0123 | ģ | g | Latin small letter g with cedilla |
1265 * | U+0124 | Ĥ | H | Latin capital letter H with circumflex |
1266 * | U+0125 | ĥ | h | Latin small letter h with circumflex |
1267 * | U+0126 | Ħ | H | Latin capital letter H with stroke |
1268 * | U+0127 | ħ | h | Latin small letter h with stroke |
1269 * | U+0128 | Ĩ | I | Latin capital letter I with tilde |
1270 * | U+0129 | Ä© | i | Latin small letter i with tilde |
1271 * | U+012A | Ī | I | Latin capital letter I with macron |
1272 * | U+012B | Ä« | i | Latin small letter i with macron |
1273 * | U+012C | Ĭ | I | Latin capital letter I with breve |
1274 * | U+012D | Ä | i | Latin small letter i with breve |
1275 * | U+012E | Ä® | I | Latin capital letter I with ogonek |
1276 * | U+012F | į | i | Latin small letter i with ogonek |
1277 * | U+0130 | İ | I | Latin capital letter I with dot above |
1278 * | U+0131 | ı | i | Latin small letter dotless i |
1279 * | U+0132 | IJ | IJ | Latin capital ligature IJ |
1280 * | U+0133 | ij | ij | Latin small ligature ij |
1281 * | U+0134 | Ä´ | J | Latin capital letter J with circumflex |
1282 * | U+0135 | ĵ | j | Latin small letter j with circumflex |
1283 * | U+0136 | Ķ | K | Latin capital letter K with cedilla |
1284 * | U+0137 | Ä· | k | Latin small letter k with cedilla |
1285 * | U+0138 | ĸ | k | Latin small letter Kra |
1286 * | U+0139 | Ĺ | L | Latin capital letter L with acute |
1287 * | U+013A | ĺ | l | Latin small letter l with acute |
1288 * | U+013B | Ä» | L | Latin capital letter L with cedilla |
1289 * | U+013C | ļ | l | Latin small letter l with cedilla |
1290 * | U+013D | Ľ | L | Latin capital letter L with caron |
1291 * | U+013E | ľ | l | Latin small letter l with caron |
1292 * | U+013F | Ä¿ | L | Latin capital letter L with middle dot |
1293 * | U+0140 | ŀ | l | Latin small letter l with middle dot |
1294 * | U+0141 | Å | L | Latin capital letter L with stroke |
1295 * | U+0142 | Å‚ | l | Latin small letter l with stroke |
1296 * | U+0143 | Ń | N | Latin capital letter N with acute |
1297 * | U+0144 | Å„ | n | Latin small letter N with acute |
1298 * | U+0145 | Å… | N | Latin capital letter N with cedilla |
1299 * | U+0146 | ņ | n | Latin small letter n with cedilla |
1300 * | U+0147 | Ň | N | Latin capital letter N with caron |
1301 * | U+0148 | ň | n | Latin small letter n with caron |
1302 * | U+0149 | ʼn | n | Latin small letter n preceded by apostrophe |
1303 * | U+014A | ÅŠ | N | Latin capital letter Eng |
1304 * | U+014B | Å‹ | n | Latin small letter Eng |
1305 * | U+014C | Ō | O | Latin capital letter O with macron |
1306 * | U+014D | Å | o | Latin small letter o with macron |
1307 * | U+014E | ÅŽ | O | Latin capital letter O with breve |
1308 * | U+014F | Å | o | Latin small letter o with breve |
1309 * | U+0150 | Å | O | Latin capital letter O with double acute |
1310 * | U+0151 | Å‘ | o | Latin small letter o with double acute |
1311 * | U+0152 | Å’ | OE | Latin capital ligature OE |
1312 * | U+0153 | Å“ | oe | Latin small ligature oe |
1313 * | U+0154 | Å” | R | Latin capital letter R with acute |
1314 * | U+0155 | Å• | r | Latin small letter r with acute |
1315 * | U+0156 | Å– | R | Latin capital letter R with cedilla |
1316 * | U+0157 | Å— | r | Latin small letter r with cedilla |
1317 * | U+0158 | Ř | R | Latin capital letter R with caron |
1318 * | U+0159 | Å™ | r | Latin small letter r with caron |
1319 * | U+015A | Åš | S | Latin capital letter S with acute |
1320 * | U+015B | Å› | s | Latin small letter s with acute |
1321 * | U+015C | Ŝ | S | Latin capital letter S with circumflex |
1322 * | U+015D | Å | s | Latin small letter s with circumflex |
1323 * | U+015E | Åž | S | Latin capital letter S with cedilla |
1324 * | U+015F | ÅŸ | s | Latin small letter s with cedilla |
1325 * | U+0160 | Å | S | Latin capital letter S with caron |
1326 * | U+0161 | Å¡ | s | Latin small letter s with caron |
1327 * | U+0162 | Ţ | T | Latin capital letter T with cedilla |
1328 * | U+0163 | ţ | t | Latin small letter t with cedilla |
1329 * | U+0164 | Ť | T | Latin capital letter T with caron |
1330 * | U+0165 | ť | t | Latin small letter t with caron |
1331 * | U+0166 | Ŧ | T | Latin capital letter T with stroke |
1332 * | U+0167 | ŧ | t | Latin small letter t with stroke |
1333 * | U+0168 | Ũ | U | Latin capital letter U with tilde |
1334 * | U+0169 | Å© | u | Latin small letter u with tilde |
1335 * | U+016A | Ū | U | Latin capital letter U with macron |
1336 * | U+016B | Å« | u | Latin small letter u with macron |
1337 * | U+016C | Ŭ | U | Latin capital letter U with breve |
1338 * | U+016D | Å | u | Latin small letter u with breve |
1339 * | U+016E | Å® | U | Latin capital letter U with ring above |
1340 * | U+016F | ů | u | Latin small letter u with ring above |
1341 * | U+0170 | Ű | U | Latin capital letter U with double acute |
1342 * | U+0171 | ű | u | Latin small letter u with double acute |
1343 * | U+0172 | Ų | U | Latin capital letter U with ogonek |
1344 * | U+0173 | ų | u | Latin small letter u with ogonek |
1345 * | U+0174 | Å´ | W | Latin capital letter W with circumflex |
1346 * | U+0175 | ŵ | w | Latin small letter w with circumflex |
1347 * | U+0176 | Ŷ | Y | Latin capital letter Y with circumflex |
1348 * | U+0177 | Å· | y | Latin small letter y with circumflex |
1349 * | U+0178 | Ÿ | Y | Latin capital letter Y with diaeresis |
1350 * | U+0179 | Ź | Z | Latin capital letter Z with acute |
1351 * | U+017A | ź | z | Latin small letter z with acute |
1352 * | U+017B | Å» | Z | Latin capital letter Z with dot above |
1353 * | U+017C | ż | z | Latin small letter z with dot above |
1354 * | U+017D | Ž | Z | Latin capital letter Z with caron |
1355 * | U+017E | ž | z | Latin small letter z with caron |
1356 * | U+017F | Å¿ | s | Latin small letter long s |
1357 * | U+01A0 | Æ | O | Latin capital letter O with horn |
1358 * | U+01A1 | Æ¡ | o | Latin small letter o with horn |
1359 * | U+01AF | Ư | U | Latin capital letter U with horn |
1360 * | U+01B0 | ư | u | Latin small letter u with horn |
1361 * | U+01CD | Ç | A | Latin capital letter A with caron |
1362 * | U+01CE | ÇŽ | a | Latin small letter a with caron |
1363 * | U+01CF | Ç | I | Latin capital letter I with caron |
1364 * | U+01D0 | Ç | i | Latin small letter i with caron |
1365 * | U+01D1 | Ç‘ | O | Latin capital letter O with caron |
1366 * | U+01D2 | Ç’ | o | Latin small letter o with caron |
1367 * | U+01D3 | Ç“ | U | Latin capital letter U with caron |
1368 * | U+01D4 | Ç” | u | Latin small letter u with caron |
1369 * | U+01D5 | Ç• | U | Latin capital letter U with diaeresis and macron |
1370 * | U+01D6 | Ç– | u | Latin small letter u with diaeresis and macron |
1371 * | U+01D7 | Ç— | U | Latin capital letter U with diaeresis and acute |
1372 * | U+01D8 | ǘ | u | Latin small letter u with diaeresis and acute |
1373 * | U+01D9 | Ç™ | U | Latin capital letter U with diaeresis and caron |
1374 * | U+01DA | Çš | u | Latin small letter u with diaeresis and caron |
1375 * | U+01DB | Ç› | U | Latin capital letter U with diaeresis and grave |
1376 * | U+01DC | ǜ | u | Latin small letter u with diaeresis and grave |
1377 *
1378 * Decompositions for Latin Extended-B:
1379 *
1380 * | Code | Glyph | Replacement | Description |
1381 * | -------- | ----- | ----------- | ----------------------------------------- |
1382 * | U+0218 | Ș | S | Latin capital letter S with comma below |
1383 * | U+0219 | È™ | s | Latin small letter s with comma below |
1384 * | U+021A | Èš | T | Latin capital letter T with comma below |
1385 * | U+021B | È› | t | Latin small letter t with comma below |
1386 *
1387 * Vowels with diacritic (Chinese, Hanyu Pinyin):
1388 *
1389 * | Code | Glyph | Replacement | Description |
1390 * | -------- | ----- | ----------- | ----------------------------------------------------- |
1391 * | U+0251 | É‘ | a | Latin small letter alpha |
1392 * | U+1EA0 | Ạ| A | Latin capital letter A with dot below |
1393 * | U+1EA1 | ạ | a | Latin small letter a with dot below |
1394 * | U+1EA2 | Ả | A | Latin capital letter A with hook above |
1395 * | U+1EA3 | ả | a | Latin small letter a with hook above |
1396 * | U+1EA4 | Ấ | A | Latin capital letter A with circumflex and acute |
1397 * | U+1EA5 | ấ | a | Latin small letter a with circumflex and acute |
1398 * | U+1EA6 | Ầ | A | Latin capital letter A with circumflex and grave |
1399 * | U+1EA7 | ầ | a | Latin small letter a with circumflex and grave |
1400 * | U+1EA8 | Ẩ | A | Latin capital letter A with circumflex and hook above |
1401 * | U+1EA9 | ẩ | a | Latin small letter a with circumflex and hook above |
1402 * | U+1EAA | Ẫ | A | Latin capital letter A with circumflex and tilde |
1403 * | U+1EAB | ẫ | a | Latin small letter a with circumflex and tilde |
1404 * | U+1EA6 | Ậ | A | Latin capital letter A with circumflex and dot below |
1405 * | U+1EAD | Ạ| a | Latin small letter a with circumflex and dot below |
1406 * | U+1EAE | Ắ | A | Latin capital letter A with breve and acute |
1407 * | U+1EAF | ắ | a | Latin small letter a with breve and acute |
1408 * | U+1EB0 | Ằ | A | Latin capital letter A with breve and grave |
1409 * | U+1EB1 | ằ | a | Latin small letter a with breve and grave |
1410 * | U+1EB2 | Ẳ | A | Latin capital letter A with breve and hook above |
1411 * | U+1EB3 | ẳ | a | Latin small letter a with breve and hook above |
1412 * | U+1EB4 | Ẵ | A | Latin capital letter A with breve and tilde |
1413 * | U+1EB5 | ẵ | a | Latin small letter a with breve and tilde |
1414 * | U+1EB6 | Ặ | A | Latin capital letter A with breve and dot below |
1415 * | U+1EB7 | ặ | a | Latin small letter a with breve and dot below |
1416 * | U+1EB8 | Ẹ | E | Latin capital letter E with dot below |
1417 * | U+1EB9 | ẹ | e | Latin small letter e with dot below |
1418 * | U+1EBA | Ẻ | E | Latin capital letter E with hook above |
1419 * | U+1EBB | ẻ | e | Latin small letter e with hook above |
1420 * | U+1EBC | Ẽ | E | Latin capital letter E with tilde |
1421 * | U+1EBD | ẽ | e | Latin small letter e with tilde |
1422 * | U+1EBE | Ế | E | Latin capital letter E with circumflex and acute |
1423 * | U+1EBF | ế | e | Latin small letter e with circumflex and acute |
1424 * | U+1EC0 | Ề | E | Latin capital letter E with circumflex and grave |
1425 * | U+1EC1 | á» | e | Latin small letter e with circumflex and grave |
1426 * | U+1EC2 | Ể | E | Latin capital letter E with circumflex and hook above |
1427 * | U+1EC3 | ể | e | Latin small letter e with circumflex and hook above |
1428 * | U+1EC4 | Ễ | E | Latin capital letter E with circumflex and tilde |
1429 * | U+1EC5 | á»… | e | Latin small letter e with circumflex and tilde |
1430 * | U+1EC6 | Ệ | E | Latin capital letter E with circumflex and dot below |
1431 * | U+1EC7 | ệ | e | Latin small letter e with circumflex and dot below |
1432 * | U+1EC8 | Ỉ | I | Latin capital letter I with hook above |
1433 * | U+1EC9 | ỉ | i | Latin small letter i with hook above |
1434 * | U+1ECA | Ị | I | Latin capital letter I with dot below |
1435 * | U+1ECB | ị | i | Latin small letter i with dot below |
1436 * | U+1ECC | Ọ | O | Latin capital letter O with dot below |
1437 * | U+1ECD | á» | o | Latin small letter o with dot below |
1438 * | U+1ECE | Ỏ | O | Latin capital letter O with hook above |
1439 * | U+1ECF | á» | o | Latin small letter o with hook above |
1440 * | U+1ED0 | á» | O | Latin capital letter O with circumflex and acute |
1441 * | U+1ED1 | ố | o | Latin small letter o with circumflex and acute |
1442 * | U+1ED2 | á»’ | O | Latin capital letter O with circumflex and grave |
1443 * | U+1ED3 | ồ | o | Latin small letter o with circumflex and grave |
1444 * | U+1ED4 | á»” | O | Latin capital letter O with circumflex and hook above |
1445 * | U+1ED5 | ổ | o | Latin small letter o with circumflex and hook above |
1446 * | U+1ED6 | á»– | O | Latin capital letter O with circumflex and tilde |
1447 * | U+1ED7 | á»— | o | Latin small letter o with circumflex and tilde |
1448 * | U+1ED8 | Ộ | O | Latin capital letter O with circumflex and dot below |
1449 * | U+1ED9 | á»™ | o | Latin small letter o with circumflex and dot below |
1450 * | U+1EDA | Ớ | O | Latin capital letter O with horn and acute |
1451 * | U+1EDB | á»› | o | Latin small letter o with horn and acute |
1452 * | U+1EDC | Ờ | O | Latin capital letter O with horn and grave |
1453 * | U+1EDD | á» | o | Latin small letter o with horn and grave |
1454 * | U+1EDE | Ở | O | Latin capital letter O with horn and hook above |
1455 * | U+1EDF | ở | o | Latin small letter o with horn and hook above |
1456 * | U+1EE0 | á» | O | Latin capital letter O with horn and tilde |
1457 * | U+1EE1 | ỡ | o | Latin small letter o with horn and tilde |
1458 * | U+1EE2 | Ợ | O | Latin capital letter O with horn and dot below |
1459 * | U+1EE3 | ợ | o | Latin small letter o with horn and dot below |
1460 * | U+1EE4 | Ụ | U | Latin capital letter U with dot below |
1461 * | U+1EE5 | ụ | u | Latin small letter u with dot below |
1462 * | U+1EE6 | Ủ | U | Latin capital letter U with hook above |
1463 * | U+1EE7 | á»§ | u | Latin small letter u with hook above |
1464 * | U+1EE8 | Ứ | U | Latin capital letter U with horn and acute |
1465 * | U+1EE9 | ứ | u | Latin small letter u with horn and acute |
1466 * | U+1EEA | Ừ | U | Latin capital letter U with horn and grave |
1467 * | U+1EEB | ừ | u | Latin small letter u with horn and grave |
1468 * | U+1EEC | Ử | U | Latin capital letter U with horn and hook above |
1469 * | U+1EED | á» | u | Latin small letter u with horn and hook above |
1470 * | U+1EEE | á»® | U | Latin capital letter U with horn and tilde |
1471 * | U+1EEF | ữ | u | Latin small letter u with horn and tilde |
1472 * | U+1EF0 | á»° | U | Latin capital letter U with horn and dot below |
1473 * | U+1EF1 | á»± | u | Latin small letter u with horn and dot below |
1474 * | U+1EF2 | Ỳ | Y | Latin capital letter Y with grave |
1475 * | U+1EF3 | ỳ | y | Latin small letter y with grave |
1476 * | U+1EF4 | á»´ | Y | Latin capital letter Y with dot below |
1477 * | U+1EF5 | ỵ | y | Latin small letter y with dot below |
1478 * | U+1EF6 | á»¶ | Y | Latin capital letter Y with hook above |
1479 * | U+1EF7 | á»· | y | Latin small letter y with hook above |
1480 * | U+1EF8 | Ỹ | Y | Latin capital letter Y with tilde |
1481 * | U+1EF9 | ỹ | y | Latin small letter y with tilde |
1482 *
1483 * German (`de_DE`), German formal (`de_DE_formal`), German (Switzerland) formal (`de_CH`),
1484 * and German (Switzerland) informal (`de_CH_informal`) locales:
1485 *
1486 * | Code | Glyph | Replacement | Description |
1487 * | -------- | ----- | ----------- | --------------------------------------- |
1488 * | U+00C4 | Ä | Ae | Latin capital letter A with diaeresis |
1489 * | U+00E4 | ä | ae | Latin small letter a with diaeresis |
1490 * | U+00D6 | Ö | Oe | Latin capital letter O with diaeresis |
1491 * | U+00F6 | ö | oe | Latin small letter o with diaeresis |
1492 * | U+00DC | Ü | Ue | Latin capital letter U with diaeresis |
1493 * | U+00FC | ü | ue | Latin small letter u with diaeresis |
1494 * | U+00DF | ß | ss | Latin small letter sharp s |
1495 *
1496 * Danish (`da_DK`) locale:
1497 *
1498 * | Code | Glyph | Replacement | Description |
1499 * | -------- | ----- | ----------- | --------------------------------------- |
1500 * | U+00C6 | Æ | Ae | Latin capital letter AE |
1501 * | U+00E6 | æ | ae | Latin small letter ae |
1502 * | U+00D8 | Ø | Oe | Latin capital letter O with stroke |
1503 * | U+00F8 | ø | oe | Latin small letter o with stroke |
1504 * | U+00C5 | Ã… | Aa | Latin capital letter A with ring above |
1505 * | U+00E5 | å | aa | Latin small letter a with ring above |
1506 *
1507 * Catalan (`ca`) locale:
1508 *
1509 * | Code | Glyph | Replacement | Description |
1510 * | -------- | ----- | ----------- | --------------------------------------- |
1511 * | U+00B7 | l·l | ll | Flown dot (between two Ls) |
1512 *
1513 * Serbian (`sr_RS`) and Bosnian (`bs_BA`) locales:
1514 *
1515 * | Code | Glyph | Replacement | Description |
1516 * | -------- | ----- | ----------- | --------------------------------------- |
1517 * | U+0110 | Ä | DJ | Latin capital letter D with stroke |
1518 * | U+0111 | Ä‘ | dj | Latin small letter d with stroke |
1519 *
1520 * @since 1.2.1
1521 * @since 4.6.0 Added locale support for `de_CH`, `de_CH_informal`, and `ca`.
1522 * @since 4.7.0 Added locale support for `sr_RS`.
1523 * @since 4.8.0 Added locale support for `bs_BA`.
1524 *
1525 * @param string $string Text that might have accent characters
1526 * @return string Filtered string with replaced "nice" characters.
1527 */
1528function remove_accents( $string ) {
1529 if ( !preg_match('/[\x80-\xff]/', $string) )
1530 return $string;
1531
1532 if (seems_utf8($string)) {
1533 $chars = array(
1534 // Decompositions for Latin-1 Supplement
1535 'ª' => 'a', 'º' => 'o',
1536 'À' => 'A', 'Ã' => 'A',
1537 'Â' => 'A', 'Ã' => 'A',
1538 'Ä' => 'A', 'Å' => 'A',
1539 'Æ' => 'AE','Ç' => 'C',
1540 'È' => 'E', 'É' => 'E',
1541 'Ê' => 'E', 'Ë' => 'E',
1542 'ÃŒ' => 'I', 'Ã' => 'I',
1543 'ÃŽ' => 'I', 'Ã' => 'I',
1544 'Ã' => 'D', 'Ñ' => 'N',
1545 'Ò' => 'O', 'Ó' => 'O',
1546 'Ô' => 'O', 'Õ' => 'O',
1547 'Ö' => 'O', 'Ù' => 'U',
1548 'Ú' => 'U', 'Û' => 'U',
1549 'Ü' => 'U', 'Ã' => 'Y',
1550 'Þ' => 'TH','ß' => 's',
1551 'à ' => 'a', 'á' => 'a',
1552 'â' => 'a', 'ã' => 'a',
1553 'ä' => 'a', 'å' => 'a',
1554 'æ' => 'ae','ç' => 'c',
1555 'è' => 'e', 'é' => 'e',
1556 'ê' => 'e', 'ë' => 'e',
1557 'ì' => 'i', 'Ã' => 'i',
1558 'î' => 'i', 'ï' => 'i',
1559 'ð' => 'd', 'ñ' => 'n',
1560 'ò' => 'o', 'ó' => 'o',
1561 'ô' => 'o', 'õ' => 'o',
1562 'ö' => 'o', 'ø' => 'o',
1563 'ù' => 'u', 'ú' => 'u',
1564 'û' => 'u', 'ü' => 'u',
1565 'ý' => 'y', 'þ' => 'th',
1566 'ÿ' => 'y', 'Ø' => 'O',
1567 // Decompositions for Latin Extended-A
1568 'Ä€' => 'A', 'Ä' => 'a',
1569 'Ă' => 'A', 'ă' => 'a',
1570 'Ä„' => 'A', 'Ä…' => 'a',
1571 'Ć' => 'C', 'ć' => 'c',
1572 'Ĉ' => 'C', 'ĉ' => 'c',
1573 'ÄŠ' => 'C', 'Ä‹' => 'c',
1574 'ÄŒ' => 'C', 'Ä' => 'c',
1575 'ÄŽ' => 'D', 'Ä' => 'd',
1576 'Ä' => 'D', 'Ä‘' => 'd',
1577 'Ä’' => 'E', 'Ä“' => 'e',
1578 'Ä”' => 'E', 'Ä•' => 'e',
1579 'Ä–' => 'E', 'Ä—' => 'e',
1580 'Ę' => 'E', 'ę' => 'e',
1581 'Äš' => 'E', 'Ä›' => 'e',
1582 'Äœ' => 'G', 'Ä' => 'g',
1583 'Äž' => 'G', 'ÄŸ' => 'g',
1584 'Ä ' => 'G', 'Ä¡' => 'g',
1585 'Ä¢' => 'G', 'Ä£' => 'g',
1586 'Ĥ' => 'H', 'ĥ' => 'h',
1587 'Ħ' => 'H', 'ħ' => 'h',
1588 'Ĩ' => 'I', 'ĩ' => 'i',
1589 'Ī' => 'I', 'ī' => 'i',
1590 'Ĭ' => 'I', 'Ä' => 'i',
1591 'Į' => 'I', 'į' => 'i',
1592 'İ' => 'I', 'ı' => 'i',
1593 'IJ' => 'IJ','ij' => 'ij',
1594 'Ĵ' => 'J', 'ĵ' => 'j',
1595 'Ķ' => 'K', 'ķ' => 'k',
1596 'ĸ' => 'k', 'Ĺ' => 'L',
1597 'ĺ' => 'l', 'Ļ' => 'L',
1598 'ļ' => 'l', 'Ľ' => 'L',
1599 'ľ' => 'l', 'Ŀ' => 'L',
1600 'Å€' => 'l', 'Å' => 'L',
1601 'ł' => 'l', 'Ń' => 'N',
1602 'Å„' => 'n', 'Å…' => 'N',
1603 'ņ' => 'n', 'Ň' => 'N',
1604 'ň' => 'n', 'ʼn' => 'n',
1605 'ÅŠ' => 'N', 'Å‹' => 'n',
1606 'ÅŒ' => 'O', 'Å' => 'o',
1607 'ÅŽ' => 'O', 'Å' => 'o',
1608 'Å' => 'O', 'Å‘' => 'o',
1609 'Å’' => 'OE','Å“' => 'oe',
1610 'Å”' => 'R','Å•' => 'r',
1611 'Å–' => 'R','Å—' => 'r',
1612 'Ř' => 'R','ř' => 'r',
1613 'Åš' => 'S','Å›' => 's',
1614 'Åœ' => 'S','Å' => 's',
1615 'Åž' => 'S','ÅŸ' => 's',
1616 'Å ' => 'S', 'Å¡' => 's',
1617 'Å¢' => 'T', 'Å£' => 't',
1618 'Ť' => 'T', 'ť' => 't',
1619 'Ŧ' => 'T', 'ŧ' => 't',
1620 'Ũ' => 'U', 'ũ' => 'u',
1621 'Ū' => 'U', 'ū' => 'u',
1622 'Ŭ' => 'U', 'Å' => 'u',
1623 'Ů' => 'U', 'ů' => 'u',
1624 'Ű' => 'U', 'ű' => 'u',
1625 'Ų' => 'U', 'ų' => 'u',
1626 'Ŵ' => 'W', 'ŵ' => 'w',
1627 'Ŷ' => 'Y', 'ŷ' => 'y',
1628 'Ÿ' => 'Y', 'Ź' => 'Z',
1629 'ź' => 'z', 'Ż' => 'Z',
1630 'ż' => 'z', 'Ž' => 'Z',
1631 'ž' => 'z', 'ſ' => 's',
1632 // Decompositions for Latin Extended-B
1633 'Ș' => 'S', 'ș' => 's',
1634 'Èš' => 'T', 'È›' => 't',
1635 // Euro Sign
1636 '€' => 'E',
1637 // GBP (Pound) Sign
1638 '£' => '',
1639 // Vowels with diacritic (Vietnamese)
1640 // unmarked
1641 'Æ ' => 'O', 'Æ¡' => 'o',
1642 'Ư' => 'U', 'ư' => 'u',
1643 // grave accent
1644 'Ầ' => 'A', 'ầ' => 'a',
1645 'Ằ' => 'A', 'ằ' => 'a',
1646 'Ề' => 'E', 'á»' => 'e',
1647 'Ồ' => 'O', 'ồ' => 'o',
1648 'Ờ' => 'O', 'á»' => 'o',
1649 'Ừ' => 'U', 'ừ' => 'u',
1650 'Ỳ' => 'Y', 'ỳ' => 'y',
1651 // hook
1652 'Ả' => 'A', 'ả' => 'a',
1653 'Ẩ' => 'A', 'ẩ' => 'a',
1654 'Ẳ' => 'A', 'ẳ' => 'a',
1655 'Ẻ' => 'E', 'ẻ' => 'e',
1656 'Ể' => 'E', 'ể' => 'e',
1657 'Ỉ' => 'I', 'ỉ' => 'i',
1658 'Ỏ' => 'O', 'á»' => 'o',
1659 'Ổ' => 'O', 'ổ' => 'o',
1660 'Ở' => 'O', 'ở' => 'o',
1661 'Ủ' => 'U', 'ủ' => 'u',
1662 'Ử' => 'U', 'á»' => 'u',
1663 'á»¶' => 'Y', 'á»·' => 'y',
1664 // tilde
1665 'Ẫ' => 'A', 'ẫ' => 'a',
1666 'Ẵ' => 'A', 'ẵ' => 'a',
1667 'Ẽ' => 'E', 'ẽ' => 'e',
1668 'Ễ' => 'E', 'ễ' => 'e',
1669 'á»–' => 'O', 'á»—' => 'o',
1670 'Ỡ' => 'O', 'ỡ' => 'o',
1671 'Ữ' => 'U', 'ữ' => 'u',
1672 'Ỹ' => 'Y', 'ỹ' => 'y',
1673 // acute accent
1674 'Ấ' => 'A', 'ấ' => 'a',
1675 'Ắ' => 'A', 'ắ' => 'a',
1676 'Ế' => 'E', 'ế' => 'e',
1677 'á»' => 'O', 'ố' => 'o',
1678 'Ớ' => 'O', 'ớ' => 'o',
1679 'Ứ' => 'U', 'ứ' => 'u',
1680 // dot below
1681 'Ạ' => 'A', 'ạ' => 'a',
1682 'Ậ' => 'A', 'áº' => 'a',
1683 'Ặ' => 'A', 'ặ' => 'a',
1684 'Ẹ' => 'E', 'ẹ' => 'e',
1685 'Ệ' => 'E', 'ệ' => 'e',
1686 'Ị' => 'I', 'ị' => 'i',
1687 'Ọ' => 'O', 'á»' => 'o',
1688 'Ộ' => 'O', 'ộ' => 'o',
1689 'Ợ' => 'O', 'ợ' => 'o',
1690 'Ụ' => 'U', 'ụ' => 'u',
1691 'á»°' => 'U', 'á»±' => 'u',
1692 'Ỵ' => 'Y', 'ỵ' => 'y',
1693 // Vowels with diacritic (Chinese, Hanyu Pinyin)
1694 'É‘' => 'a',
1695 // macron
1696 'Ç•' => 'U', 'Ç–' => 'u',
1697 // acute accent
1698 'Ǘ' => 'U', 'ǘ' => 'u',
1699 // caron
1700 'Ç' => 'A', 'ÇŽ' => 'a',
1701 'Ç' => 'I', 'Ç' => 'i',
1702 'Ç‘' => 'O', 'Ç’' => 'o',
1703 'Ç“' => 'U', 'Ç”' => 'u',
1704 'Ç™' => 'U', 'Çš' => 'u',
1705 // grave accent
1706 'Ǜ' => 'U', 'ǜ' => 'u',
1707 );
1708
1709 // Used for locale-specific rules
1710 $locale = get_locale();
1711
1712 if ( 'de_DE' == $locale || 'de_DE_formal' == $locale || 'de_CH' == $locale || 'de_CH_informal' == $locale ) {
1713 $chars[ 'Ä' ] = 'Ae';
1714 $chars[ 'ä' ] = 'ae';
1715 $chars[ 'Ö' ] = 'Oe';
1716 $chars[ 'ö' ] = 'oe';
1717 $chars[ 'Ü' ] = 'Ue';
1718 $chars[ 'ü' ] = 'ue';
1719 $chars[ 'ß' ] = 'ss';
1720 } elseif ( 'da_DK' === $locale ) {
1721 $chars[ 'Æ' ] = 'Ae';
1722 $chars[ 'æ' ] = 'ae';
1723 $chars[ 'Ø' ] = 'Oe';
1724 $chars[ 'ø' ] = 'oe';
1725 $chars[ 'Ã…' ] = 'Aa';
1726 $chars[ 'Ã¥' ] = 'aa';
1727 } elseif ( 'ca' === $locale ) {
1728 $chars[ 'l·l' ] = 'll';
1729 } elseif ( 'sr_RS' === $locale || 'bs_BA' === $locale ) {
1730 $chars[ 'Ä' ] = 'DJ';
1731 $chars[ 'Ä‘' ] = 'dj';
1732 }
1733
1734 $string = strtr($string, $chars);
1735 } else {
1736 $chars = array();
1737 // Assume ISO-8859-1 if not UTF-8
1738 $chars['in'] = "\x80\x83\x8a\x8e\x9a\x9e"
1739 ."\x9f\xa2\xa5\xb5\xc0\xc1\xc2"
1740 ."\xc3\xc4\xc5\xc7\xc8\xc9\xca"
1741 ."\xcb\xcc\xcd\xce\xcf\xd1\xd2"
1742 ."\xd3\xd4\xd5\xd6\xd8\xd9\xda"
1743 ."\xdb\xdc\xdd\xe0\xe1\xe2\xe3"
1744 ."\xe4\xe5\xe7\xe8\xe9\xea\xeb"
1745 ."\xec\xed\xee\xef\xf1\xf2\xf3"
1746 ."\xf4\xf5\xf6\xf8\xf9\xfa\xfb"
1747 ."\xfc\xfd\xff";
1748
1749 $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
1750
1751 $string = strtr($string, $chars['in'], $chars['out']);
1752 $double_chars = array();
1753 $double_chars['in'] = array("\x8c", "\x9c", "\xc6", "\xd0", "\xde", "\xdf", "\xe6", "\xf0", "\xfe");
1754 $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
1755 $string = str_replace($double_chars['in'], $double_chars['out'], $string);
1756 }
1757
1758 return $string;
1759}
1760
1761/**
1762 * Sanitizes a filename, replacing whitespace with dashes.
1763 *
1764 * Removes special characters that are illegal in filenames on certain
1765 * operating systems and special characters requiring special escaping
1766 * to manipulate at the command line. Replaces spaces and consecutive
1767 * dashes with a single dash. Trims period, dash and underscore from beginning
1768 * and end of filename. It is not guaranteed that this function will return a
1769 * filename that is allowed to be uploaded.
1770 *
1771 * @since 2.1.0
1772 *
1773 * @param string $filename The filename to be sanitized
1774 * @return string The sanitized filename
1775 */
1776function sanitize_file_name( $filename ) {
1777 $filename_raw = $filename;
1778 $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
1779 /**
1780 * Filters the list of characters to remove from a filename.
1781 *
1782 * @since 2.8.0
1783 *
1784 * @param array $special_chars Characters to remove.
1785 * @param string $filename_raw Filename as it was passed into sanitize_file_name().
1786 */
1787 $special_chars = apply_filters( 'sanitize_file_name_chars', $special_chars, $filename_raw );
1788 $filename = preg_replace( "#\x{00a0}#siu", ' ', $filename );
1789 $filename = str_replace( $special_chars, '', $filename );
1790 $filename = str_replace( array( '%20', '+' ), '-', $filename );
1791 $filename = preg_replace( '/[\r\n\t -]+/', '-', $filename );
1792 $filename = trim( $filename, '.-_' );
1793
1794 if ( false === strpos( $filename, '.' ) ) {
1795 $mime_types = wp_get_mime_types();
1796 $filetype = wp_check_filetype( 'test.' . $filename, $mime_types );
1797 if ( $filetype['ext'] === $filename ) {
1798 $filename = 'unnamed-file.' . $filetype['ext'];
1799 }
1800 }
1801
1802 // Split the filename into a base and extension[s]
1803 $parts = explode('.', $filename);
1804
1805 // Return if only one extension
1806 if ( count( $parts ) <= 2 ) {
1807 /**
1808 * Filters a sanitized filename string.
1809 *
1810 * @since 2.8.0
1811 *
1812 * @param string $filename Sanitized filename.
1813 * @param string $filename_raw The filename prior to sanitization.
1814 */
1815 return apply_filters( 'sanitize_file_name', $filename, $filename_raw );
1816 }
1817
1818 // Process multiple extensions
1819 $filename = array_shift($parts);
1820 $extension = array_pop($parts);
1821 $mimes = get_allowed_mime_types();
1822
1823 /*
1824 * Loop over any intermediate extensions. Postfix them with a trailing underscore
1825 * if they are a 2 - 5 character long alpha string not in the extension whitelist.
1826 */
1827 foreach ( (array) $parts as $part) {
1828 $filename .= '.' . $part;
1829
1830 if ( preg_match("/^[a-zA-Z]{2,5}\d?$/", $part) ) {
1831 $allowed = false;
1832 foreach ( $mimes as $ext_preg => $mime_match ) {
1833 $ext_preg = '!^(' . $ext_preg . ')$!i';
1834 if ( preg_match( $ext_preg, $part ) ) {
1835 $allowed = true;
1836 break;
1837 }
1838 }
1839 if ( !$allowed )
1840 $filename .= '_';
1841 }
1842 }
1843 $filename .= '.' . $extension;
1844 /** This filter is documented in wp-includes/formatting.php */
1845 return apply_filters('sanitize_file_name', $filename, $filename_raw);
1846}
1847
1848/**
1849 * Sanitizes a username, stripping out unsafe characters.
1850 *
1851 * Removes tags, octets, entities, and if strict is enabled, will only keep
1852 * alphanumeric, _, space, ., -, @. After sanitizing, it passes the username,
1853 * raw username (the username in the parameter), and the value of $strict as
1854 * parameters for the {@see 'sanitize_user'} filter.
1855 *
1856 * @since 2.0.0
1857 *
1858 * @param string $username The username to be sanitized.
1859 * @param bool $strict If set limits $username to specific characters. Default false.
1860 * @return string The sanitized username, after passing through filters.
1861 */
1862function sanitize_user( $username, $strict = false ) {
1863 $raw_username = $username;
1864 $username = wp_strip_all_tags( $username );
1865 $username = remove_accents( $username );
1866 // Kill octets
1867 $username = preg_replace( '|%([a-fA-F0-9][a-fA-F0-9])|', '', $username );
1868 $username = preg_replace( '/&.+?;/', '', $username ); // Kill entities
1869
1870 // If strict, reduce to ASCII for max portability.
1871 if ( $strict )
1872 $username = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $username );
1873
1874 $username = trim( $username );
1875 // Consolidate contiguous whitespace
1876 $username = preg_replace( '|\s+|', ' ', $username );
1877
1878 /**
1879 * Filters a sanitized username string.
1880 *
1881 * @since 2.0.1
1882 *
1883 * @param string $username Sanitized username.
1884 * @param string $raw_username The username prior to sanitization.
1885 * @param bool $strict Whether to limit the sanitization to specific characters. Default false.
1886 */
1887 return apply_filters( 'sanitize_user', $username, $raw_username, $strict );
1888}
1889
1890/**
1891 * Sanitizes a string key.
1892 *
1893 * Keys are used as internal identifiers. Lowercase alphanumeric characters, dashes and underscores are allowed.
1894 *
1895 * @since 3.0.0
1896 *
1897 * @param string $key String key
1898 * @return string Sanitized key
1899 */
1900function sanitize_key( $key ) {
1901 $raw_key = $key;
1902 $key = strtolower( $key );
1903 $key = preg_replace( '/[^a-z0-9_\-]/', '', $key );
1904
1905 /**
1906 * Filters a sanitized key string.
1907 *
1908 * @since 3.0.0
1909 *
1910 * @param string $key Sanitized key.
1911 * @param string $raw_key The key prior to sanitization.
1912 */
1913 return apply_filters( 'sanitize_key', $key, $raw_key );
1914}
1915
1916/**
1917 * Sanitizes a title, or returns a fallback title.
1918 *
1919 * Specifically, HTML and PHP tags are stripped. Further actions can be added
1920 * via the plugin API. If $title is empty and $fallback_title is set, the latter
1921 * will be used.
1922 *
1923 * @since 1.0.0
1924 *
1925 * @param string $title The string to be sanitized.
1926 * @param string $fallback_title Optional. A title to use if $title is empty.
1927 * @param string $context Optional. The operation for which the string is sanitized
1928 * @return string The sanitized string.
1929 */
1930function sanitize_title( $title, $fallback_title = '', $context = 'save' ) {
1931 $raw_title = $title;
1932
1933 if ( 'save' == $context )
1934 $title = remove_accents($title);
1935
1936 /**
1937 * Filters a sanitized title string.
1938 *
1939 * @since 1.2.0
1940 *
1941 * @param string $title Sanitized title.
1942 * @param string $raw_title The title prior to sanitization.
1943 * @param string $context The context for which the title is being sanitized.
1944 */
1945 $title = apply_filters( 'sanitize_title', $title, $raw_title, $context );
1946
1947 if ( '' === $title || false === $title )
1948 $title = $fallback_title;
1949
1950 return $title;
1951}
1952
1953/**
1954 * Sanitizes a title with the 'query' context.
1955 *
1956 * Used for querying the database for a value from URL.
1957 *
1958 * @since 3.1.0
1959 *
1960 * @param string $title The string to be sanitized.
1961 * @return string The sanitized string.
1962 */
1963function sanitize_title_for_query( $title ) {
1964 return sanitize_title( $title, '', 'query' );
1965}
1966
1967/**
1968 * Sanitizes a title, replacing whitespace and a few other characters with dashes.
1969 *
1970 * Limits the output to alphanumeric characters, underscore (_) and dash (-).
1971 * Whitespace becomes a dash.
1972 *
1973 * @since 1.2.0
1974 *
1975 * @param string $title The title to be sanitized.
1976 * @param string $raw_title Optional. Not used.
1977 * @param string $context Optional. The operation for which the string is sanitized.
1978 * @return string The sanitized title.
1979 */
1980function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'display' ) {
1981 $title = strip_tags($title);
1982 // Preserve escaped octets.
1983 $title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title);
1984 // Remove percent signs that are not part of an octet.
1985 $title = str_replace('%', '', $title);
1986 // Restore octets.
1987 $title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title);
1988
1989 if (seems_utf8($title)) {
1990 if (function_exists('mb_strtolower')) {
1991 $title = mb_strtolower($title, 'UTF-8');
1992 }
1993 $title = utf8_uri_encode($title, 200);
1994 }
1995
1996 $title = strtolower($title);
1997
1998 if ( 'save' == $context ) {
1999 // Convert nbsp, ndash and mdash to hyphens
2000 $title = str_replace( array( '%c2%a0', '%e2%80%93', '%e2%80%94' ), '-', $title );
2001 // Convert nbsp, ndash and mdash HTML entities to hyphens
2002 $title = str_replace( array( ' ', ' ', '–', '–', '—', '—' ), '-', $title );
2003 // Convert forward slash to hyphen
2004 $title = str_replace( '/', '-', $title );
2005
2006 // Strip these characters entirely
2007 $title = str_replace( array(
2008 // iexcl and iquest
2009 '%c2%a1', '%c2%bf',
2010 // angle quotes
2011 '%c2%ab', '%c2%bb', '%e2%80%b9', '%e2%80%ba',
2012 // curly quotes
2013 '%e2%80%98', '%e2%80%99', '%e2%80%9c', '%e2%80%9d',
2014 '%e2%80%9a', '%e2%80%9b', '%e2%80%9e', '%e2%80%9f',
2015 // copy, reg, deg, hellip and trade
2016 '%c2%a9', '%c2%ae', '%c2%b0', '%e2%80%a6', '%e2%84%a2',
2017 // acute accents
2018 '%c2%b4', '%cb%8a', '%cc%81', '%cd%81',
2019 // grave accent, macron, caron
2020 '%cc%80', '%cc%84', '%cc%8c',
2021 ), '', $title );
2022
2023 // Convert times to x
2024 $title = str_replace( '%c3%97', 'x', $title );
2025 }
2026
2027 $title = preg_replace('/&.+?;/', '', $title); // kill entities
2028 $title = str_replace('.', '-', $title);
2029
2030 $title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
2031 $title = preg_replace('/\s+/', '-', $title);
2032 $title = preg_replace('|-+|', '-', $title);
2033 $title = trim($title, '-');
2034
2035 return $title;
2036}
2037
2038/**
2039 * Ensures a string is a valid SQL 'order by' clause.
2040 *
2041 * Accepts one or more columns, with or without a sort order (ASC / DESC).
2042 * e.g. 'column_1', 'column_1, column_2', 'column_1 ASC, column_2 DESC' etc.
2043 *
2044 * Also accepts 'RAND()'.
2045 *
2046 * @since 2.5.1
2047 *
2048 * @param string $orderby Order by clause to be validated.
2049 * @return string|false Returns $orderby if valid, false otherwise.
2050 */
2051function sanitize_sql_orderby( $orderby ) {
2052 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 ) ) {
2053 return $orderby;
2054 }
2055 return false;
2056}
2057
2058/**
2059 * Sanitizes an HTML classname to ensure it only contains valid characters.
2060 *
2061 * Strips the string down to A-Z,a-z,0-9,_,-. If this results in an empty
2062 * string then it will return the alternative value supplied.
2063 *
2064 * @todo Expand to support the full range of CDATA that a class attribute can contain.
2065 *
2066 * @since 2.8.0
2067 *
2068 * @param string $class The classname to be sanitized
2069 * @param string $fallback Optional. The value to return if the sanitization ends up as an empty string.
2070 * Defaults to an empty string.
2071 * @return string The sanitized value
2072 */
2073function sanitize_html_class( $class, $fallback = '' ) {
2074 //Strip out any % encoded octets
2075 $sanitized = preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $class );
2076
2077 //Limit to A-Z,a-z,0-9,_,-
2078 $sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $sanitized );
2079
2080 if ( '' == $sanitized && $fallback ) {
2081 return sanitize_html_class( $fallback );
2082 }
2083 /**
2084 * Filters a sanitized HTML class string.
2085 *
2086 * @since 2.8.0
2087 *
2088 * @param string $sanitized The sanitized HTML class.
2089 * @param string $class HTML class before sanitization.
2090 * @param string $fallback The fallback string.
2091 */
2092 return apply_filters( 'sanitize_html_class', $sanitized, $class, $fallback );
2093}
2094
2095/**
2096 * Converts lone & characters into `&` (a.k.a. `&`)
2097 *
2098 * @since 0.71
2099 *
2100 * @param string $content String of characters to be converted.
2101 * @param string $deprecated Not used.
2102 * @return string Converted string.
2103 */
2104function convert_chars( $content, $deprecated = '' ) {
2105 if ( ! empty( $deprecated ) ) {
2106 _deprecated_argument( __FUNCTION__, '0.71' );
2107 }
2108
2109 if ( strpos( $content, '&' ) !== false ) {
2110 $content = preg_replace( '/&([^#])(?![a-z1-4]{1,8};)/i', '&$1', $content );
2111 }
2112
2113 return $content;
2114}
2115
2116/**
2117 * Converts invalid Unicode references range to valid range.
2118 *
2119 * @since 4.3.0
2120 *
2121 * @param string $content String with entities that need converting.
2122 * @return string Converted string.
2123 */
2124function convert_invalid_entities( $content ) {
2125 $wp_htmltranswinuni = array(
2126 '€' => '€', // the Euro sign
2127 '' => '',
2128 '‚' => '‚', // these are Windows CP1252 specific characters
2129 'ƒ' => 'ƒ', // they would look weird on non-Windows browsers
2130 '„' => '„',
2131 '…' => '…',
2132 '†' => '†',
2133 '‡' => '‡',
2134 'ˆ' => 'ˆ',
2135 '‰' => '‰',
2136 'Š' => 'Š',
2137 '‹' => '‹',
2138 'Œ' => 'Œ',
2139 '' => '',
2140 'Ž' => 'Ž',
2141 '' => '',
2142 '' => '',
2143 '‘' => '‘',
2144 '’' => '’',
2145 '“' => '“',
2146 '”' => '”',
2147 '•' => '•',
2148 '–' => '–',
2149 '—' => '—',
2150 '˜' => '˜',
2151 '™' => '™',
2152 'š' => 'š',
2153 '›' => '›',
2154 'œ' => 'œ',
2155 '' => '',
2156 'ž' => 'ž',
2157 'Ÿ' => 'Ÿ'
2158 );
2159
2160 if ( strpos( $content, '' ) !== false ) {
2161 $content = strtr( $content, $wp_htmltranswinuni );
2162 }
2163
2164 return $content;
2165}
2166
2167/**
2168 * Balances tags if forced to, or if the 'use_balanceTags' option is set to true.
2169 *
2170 * @since 0.71
2171 *
2172 * @param string $text Text to be balanced
2173 * @param bool $force If true, forces balancing, ignoring the value of the option. Default false.
2174 * @return string Balanced text
2175 */
2176function balanceTags( $text, $force = false ) {
2177 if ( $force || get_option('use_balanceTags') == 1 ) {
2178 return force_balance_tags( $text );
2179 } else {
2180 return $text;
2181 }
2182}
2183
2184/**
2185 * Balances tags of string using a modified stack.
2186 *
2187 * @since 2.0.4
2188 *
2189 * @author Leonard Lin <leonard@acm.org>
2190 * @license GPL
2191 * @copyright November 4, 2001
2192 * @version 1.1
2193 * @todo Make better - change loop condition to $text in 1.2
2194 * @internal Modified by Scott Reilly (coffee2code) 02 Aug 2004
2195 * 1.1 Fixed handling of append/stack pop order of end text
2196 * Added Cleaning Hooks
2197 * 1.0 First Version
2198 *
2199 * @param string $text Text to be balanced.
2200 * @return string Balanced text.
2201 */
2202function force_balance_tags( $text ) {
2203 $tagstack = array();
2204 $stacksize = 0;
2205 $tagqueue = '';
2206 $newtext = '';
2207 // Known single-entity/self-closing tags
2208 $single_tags = array( 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param', 'source' );
2209 // Tags that can be immediately nested within themselves
2210 $nestable_tags = array( 'blockquote', 'div', 'object', 'q', 'span' );
2211
2212 // WP bug fix for comments - in case you REALLY meant to type '< !--'
2213 $text = str_replace('< !--', '< !--', $text);
2214 // WP bug fix for LOVE <3 (and other situations with '<' before a number)
2215 $text = preg_replace('#<([0-9]{1})#', '<$1', $text);
2216
2217 while ( preg_match("/<(\/?[\w:]*)\s*([^>]*)>/", $text, $regex) ) {
2218 $newtext .= $tagqueue;
2219
2220 $i = strpos($text, $regex[0]);
2221 $l = strlen($regex[0]);
2222
2223 // clear the shifter
2224 $tagqueue = '';
2225 // Pop or Push
2226 if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag
2227 $tag = strtolower(substr($regex[1],1));
2228 // if too many closing tags
2229 if ( $stacksize <= 0 ) {
2230 $tag = '';
2231 // or close to be safe $tag = '/' . $tag;
2232 }
2233 // if stacktop value = tag close value then pop
2234 elseif ( $tagstack[$stacksize - 1] == $tag ) { // found closing tag
2235 $tag = '</' . $tag . '>'; // Close Tag
2236 // Pop
2237 array_pop( $tagstack );
2238 $stacksize--;
2239 } else { // closing tag not at top, search for it
2240 for ( $j = $stacksize-1; $j >= 0; $j-- ) {
2241 if ( $tagstack[$j] == $tag ) {
2242 // add tag to tagqueue
2243 for ( $k = $stacksize-1; $k >= $j; $k--) {
2244 $tagqueue .= '</' . array_pop( $tagstack ) . '>';
2245 $stacksize--;
2246 }
2247 break;
2248 }
2249 }
2250 $tag = '';
2251 }
2252 } else { // Begin Tag
2253 $tag = strtolower($regex[1]);
2254
2255 // Tag Cleaning
2256
2257 // If it's an empty tag "< >", do nothing
2258 if ( '' == $tag ) {
2259 // do nothing
2260 }
2261 // ElseIf it presents itself as a self-closing tag...
2262 elseif ( substr( $regex[2], -1 ) == '/' ) {
2263 // ...but it isn't a known single-entity self-closing tag, then don't let it be treated as such and
2264 // immediately close it with a closing tag (the tag will encapsulate no text as a result)
2265 if ( ! in_array( $tag, $single_tags ) )
2266 $regex[2] = trim( substr( $regex[2], 0, -1 ) ) . "></$tag";
2267 }
2268 // ElseIf it's a known single-entity tag but it doesn't close itself, do so
2269 elseif ( in_array($tag, $single_tags) ) {
2270 $regex[2] .= '/';
2271 }
2272 // Else it's not a single-entity tag
2273 else {
2274 // If the top of the stack is the same as the tag we want to push, close previous tag
2275 if ( $stacksize > 0 && !in_array($tag, $nestable_tags) && $tagstack[$stacksize - 1] == $tag ) {
2276 $tagqueue = '</' . array_pop( $tagstack ) . '>';
2277 $stacksize--;
2278 }
2279 $stacksize = array_push( $tagstack, $tag );
2280 }
2281
2282 // Attributes
2283 $attributes = $regex[2];
2284 if ( ! empty( $attributes ) && $attributes[0] != '>' )
2285 $attributes = ' ' . $attributes;
2286
2287 $tag = '<' . $tag . $attributes . '>';
2288 //If already queuing a close tag, then put this tag on, too
2289 if ( !empty($tagqueue) ) {
2290 $tagqueue .= $tag;
2291 $tag = '';
2292 }
2293 }
2294 $newtext .= substr($text, 0, $i) . $tag;
2295 $text = substr($text, $i + $l);
2296 }
2297
2298 // Clear Tag Queue
2299 $newtext .= $tagqueue;
2300
2301 // Add Remaining text
2302 $newtext .= $text;
2303
2304 // Empty Stack
2305 while( $x = array_pop($tagstack) )
2306 $newtext .= '</' . $x . '>'; // Add remaining tags to close
2307
2308 // WP fix for the bug with HTML comments
2309 $newtext = str_replace("< !--","<!--",$newtext);
2310 $newtext = str_replace("< !--","< !--",$newtext);
2311
2312 return $newtext;
2313}
2314
2315/**
2316 * Acts on text which is about to be edited.
2317 *
2318 * The $content is run through esc_textarea(), which uses htmlspecialchars()
2319 * to convert special characters to HTML entities. If `$richedit` is set to true,
2320 * it is simply a holder for the {@see 'format_to_edit'} filter.
2321 *
2322 * @since 0.71
2323 * @since 4.4.0 The `$richedit` parameter was renamed to `$rich_text` for clarity.
2324 *
2325 * @param string $content The text about to be edited.
2326 * @param bool $rich_text Optional. Whether `$content` should be considered rich text,
2327 * in which case it would not be passed through esc_textarea().
2328 * Default false.
2329 * @return string The text after the filter (and possibly htmlspecialchars()) has been run.
2330 */
2331function format_to_edit( $content, $rich_text = false ) {
2332 /**
2333 * Filters the text to be formatted for editing.
2334 *
2335 * @since 1.2.0
2336 *
2337 * @param string $content The text, prior to formatting for editing.
2338 */
2339 $content = apply_filters( 'format_to_edit', $content );
2340 if ( ! $rich_text )
2341 $content = esc_textarea( $content );
2342 return $content;
2343}
2344
2345/**
2346 * Add leading zeros when necessary.
2347 *
2348 * If you set the threshold to '4' and the number is '10', then you will get
2349 * back '0010'. If you set the threshold to '4' and the number is '5000', then you
2350 * will get back '5000'.
2351 *
2352 * Uses sprintf to append the amount of zeros based on the $threshold parameter
2353 * and the size of the number. If the number is large enough, then no zeros will
2354 * be appended.
2355 *
2356 * @since 0.71
2357 *
2358 * @param int $number Number to append zeros to if not greater than threshold.
2359 * @param int $threshold Digit places number needs to be to not have zeros added.
2360 * @return string Adds leading zeros to number if needed.
2361 */
2362function zeroise( $number, $threshold ) {
2363 return sprintf( '%0' . $threshold . 's', $number );
2364}
2365
2366/**
2367 * Adds backslashes before letters and before a number at the start of a string.
2368 *
2369 * @since 0.71
2370 *
2371 * @param string $string Value to which backslashes will be added.
2372 * @return string String with backslashes inserted.
2373 */
2374function backslashit( $string ) {
2375 if ( isset( $string[0] ) && $string[0] >= '0' && $string[0] <= '9' )
2376 $string = '\\\\' . $string;
2377 return addcslashes( $string, 'A..Za..z' );
2378}
2379
2380/**
2381 * Appends a trailing slash.
2382 *
2383 * Will remove trailing forward and backslashes if it exists already before adding
2384 * a trailing forward slash. This prevents double slashing a string or path.
2385 *
2386 * The primary use of this is for paths and thus should be used for paths. It is
2387 * not restricted to paths and offers no specific path support.
2388 *
2389 * @since 1.2.0
2390 *
2391 * @param string $string What to add the trailing slash to.
2392 * @return string String with trailing slash added.
2393 */
2394function trailingslashit( $string ) {
2395 return untrailingslashit( $string ) . '/';
2396}
2397
2398/**
2399 * Removes trailing forward slashes and backslashes if they exist.
2400 *
2401 * The primary use of this is for paths and thus should be used for paths. It is
2402 * not restricted to paths and offers no specific path support.
2403 *
2404 * @since 2.2.0
2405 *
2406 * @param string $string What to remove the trailing slashes from.
2407 * @return string String without the trailing slashes.
2408 */
2409function untrailingslashit( $string ) {
2410 return rtrim( $string, '/\\' );
2411}
2412
2413/**
2414 * Adds slashes to escape strings.
2415 *
2416 * Slashes will first be removed if magic_quotes_gpc is set, see {@link
2417 * https://secure.php.net/magic_quotes} for more details.
2418 *
2419 * @since 0.71
2420 *
2421 * @param string $gpc The string returned from HTTP request data.
2422 * @return string Returns a string escaped with slashes.
2423 */
2424function addslashes_gpc($gpc) {
2425 if ( get_magic_quotes_gpc() )
2426 $gpc = stripslashes($gpc);
2427
2428 return wp_slash($gpc);
2429}
2430
2431/**
2432 * Navigates through an array, object, or scalar, and removes slashes from the values.
2433 *
2434 * @since 2.0.0
2435 *
2436 * @param mixed $value The value to be stripped.
2437 * @return mixed Stripped value.
2438 */
2439function stripslashes_deep( $value ) {
2440 return map_deep( $value, 'stripslashes_from_strings_only' );
2441}
2442
2443/**
2444 * Callback function for `stripslashes_deep()` which strips slashes from strings.
2445 *
2446 * @since 4.4.0
2447 *
2448 * @param mixed $value The array or string to be stripped.
2449 * @return mixed $value The stripped value.
2450 */
2451function stripslashes_from_strings_only( $value ) {
2452 return is_string( $value ) ? stripslashes( $value ) : $value;
2453}
2454
2455/**
2456 * Navigates through an array, object, or scalar, and encodes the values to be used in a URL.
2457 *
2458 * @since 2.2.0
2459 *
2460 * @param mixed $value The array or string to be encoded.
2461 * @return mixed $value The encoded value.
2462 */
2463function urlencode_deep( $value ) {
2464 return map_deep( $value, 'urlencode' );
2465}
2466
2467/**
2468 * Navigates through an array, object, or scalar, and raw-encodes the values to be used in a URL.
2469 *
2470 * @since 3.4.0
2471 *
2472 * @param mixed $value The array or string to be encoded.
2473 * @return mixed $value The encoded value.
2474 */
2475function rawurlencode_deep( $value ) {
2476 return map_deep( $value, 'rawurlencode' );
2477}
2478
2479/**
2480 * Navigates through an array, object, or scalar, and decodes URL-encoded values
2481 *
2482 * @since 4.4.0
2483 *
2484 * @param mixed $value The array or string to be decoded.
2485 * @return mixed $value The decoded value.
2486 */
2487function urldecode_deep( $value ) {
2488 return map_deep( $value, 'urldecode' );
2489}
2490
2491/**
2492 * Converts email addresses characters to HTML entities to block spam bots.
2493 *
2494 * @since 0.71
2495 *
2496 * @param string $email_address Email address.
2497 * @param int $hex_encoding Optional. Set to 1 to enable hex encoding.
2498 * @return string Converted email address.
2499 */
2500function antispambot( $email_address, $hex_encoding = 0 ) {
2501 $email_no_spam_address = '';
2502 for ( $i = 0, $len = strlen( $email_address ); $i < $len; $i++ ) {
2503 $j = rand( 0, 1 + $hex_encoding );
2504 if ( $j == 0 ) {
2505 $email_no_spam_address .= '&#' . ord( $email_address[$i] ) . ';';
2506 } elseif ( $j == 1 ) {
2507 $email_no_spam_address .= $email_address[$i];
2508 } elseif ( $j == 2 ) {
2509 $email_no_spam_address .= '%' . zeroise( dechex( ord( $email_address[$i] ) ), 2 );
2510 }
2511 }
2512
2513 return str_replace( '@', '@', $email_no_spam_address );
2514}
2515
2516/**
2517 * Callback to convert URI match to HTML A element.
2518 *
2519 * This function was backported from 2.5.0 to 2.3.2. Regex callback for make_clickable().
2520 *
2521 * @since 2.3.2
2522 * @access private
2523 *
2524 * @param array $matches Single Regex Match.
2525 * @return string HTML A element with URI address.
2526 */
2527function _make_url_clickable_cb( $matches ) {
2528 $url = $matches[2];
2529
2530 if ( ')' == $matches[3] && strpos( $url, '(' ) ) {
2531 // If the trailing character is a closing parethesis, and the URL has an opening parenthesis in it, add the closing parenthesis to the URL.
2532 // Then we can let the parenthesis balancer do its thing below.
2533 $url .= $matches[3];
2534 $suffix = '';
2535 } else {
2536 $suffix = $matches[3];
2537 }
2538
2539 // Include parentheses in the URL only if paired
2540 while ( substr_count( $url, '(' ) < substr_count( $url, ')' ) ) {
2541 $suffix = strrchr( $url, ')' ) . $suffix;
2542 $url = substr( $url, 0, strrpos( $url, ')' ) );
2543 }
2544
2545 $url = esc_url($url);
2546 if ( empty($url) )
2547 return $matches[0];
2548
2549 return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>" . $suffix;
2550}
2551
2552/**
2553 * Callback to convert URL match to HTML A element.
2554 *
2555 * This function was backported from 2.5.0 to 2.3.2. Regex callback for make_clickable().
2556 *
2557 * @since 2.3.2
2558 * @access private
2559 *
2560 * @param array $matches Single Regex Match.
2561 * @return string HTML A element with URL address.
2562 */
2563function _make_web_ftp_clickable_cb( $matches ) {
2564 $ret = '';
2565 $dest = $matches[2];
2566 $dest = 'http://' . $dest;
2567
2568 // removed trailing [.,;:)] from URL
2569 if ( in_array( substr($dest, -1), array('.', ',', ';', ':', ')') ) === true ) {
2570 $ret = substr($dest, -1);
2571 $dest = substr($dest, 0, strlen($dest)-1);
2572 }
2573
2574 $dest = esc_url($dest);
2575 if ( empty($dest) )
2576 return $matches[0];
2577
2578 return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>$ret";
2579}
2580
2581/**
2582 * Callback to convert email address match to HTML A element.
2583 *
2584 * This function was backported from 2.5.0 to 2.3.2. Regex callback for make_clickable().
2585 *
2586 * @since 2.3.2
2587 * @access private
2588 *
2589 * @param array $matches Single Regex Match.
2590 * @return string HTML A element with email address.
2591 */
2592function _make_email_clickable_cb( $matches ) {
2593 $email = $matches[2] . '@' . $matches[3];
2594 return $matches[1] . "<a href=\"mailto:$email\">$email</a>";
2595}
2596
2597/**
2598 * Convert plaintext URI to HTML links.
2599 *
2600 * Converts URI, www and ftp, and email addresses. Finishes by fixing links
2601 * within links.
2602 *
2603 * @since 0.71
2604 *
2605 * @param string $text Content to convert URIs.
2606 * @return string Content with converted URIs.
2607 */
2608function make_clickable( $text ) {
2609 $r = '';
2610 $textarr = preg_split( '/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // split out HTML tags
2611 $nested_code_pre = 0; // Keep track of how many levels link is nested inside <pre> or <code>
2612 foreach ( $textarr as $piece ) {
2613
2614 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 ) )
2615 $nested_code_pre++;
2616 elseif ( $nested_code_pre && ( '</code>' === strtolower( $piece ) || '</pre>' === strtolower( $piece ) || '</script>' === strtolower( $piece ) || '</style>' === strtolower( $piece ) ) )
2617 $nested_code_pre--;
2618
2619 if ( $nested_code_pre || empty( $piece ) || ( $piece[0] === '<' && ! preg_match( '|^<\s*[\w]{1,20}+://|', $piece ) ) ) {
2620 $r .= $piece;
2621 continue;
2622 }
2623
2624 // Long strings might contain expensive edge cases ...
2625 if ( 10000 < strlen( $piece ) ) {
2626 // ... break it up
2627 foreach ( _split_str_by_whitespace( $piece, 2100 ) as $chunk ) { // 2100: Extra room for scheme and leading and trailing paretheses
2628 if ( 2101 < strlen( $chunk ) ) {
2629 $r .= $chunk; // Too big, no whitespace: bail.
2630 } else {
2631 $r .= make_clickable( $chunk );
2632 }
2633 }
2634 } else {
2635 $ret = " $piece "; // Pad with whitespace to simplify the regexes
2636
2637 $url_clickable = '~
2638 ([\\s(<.,;:!?]) # 1: Leading whitespace, or punctuation
2639 ( # 2: URL
2640 [\\w]{1,20}+:// # Scheme and hier-part prefix
2641 (?=\S{1,2000}\s) # Limit to URLs less than about 2000 characters long
2642 [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]*+ # Non-punctuation URL character
2643 (?: # Unroll the Loop: Only allow puctuation URL character if followed by a non-punctuation URL character
2644 [\'.,;:!?)] # Punctuation URL character
2645 [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]++ # Non-punctuation URL character
2646 )*
2647 )
2648 (\)?) # 3: Trailing closing parenthesis (for parethesis balancing post processing)
2649 ~xS'; // The regex is a non-anchored pattern and does not have a single fixed starting character.
2650 // Tell PCRE to spend more time optimizing since, when used on a page load, it will probably be used several times.
2651
2652 $ret = preg_replace_callback( $url_clickable, '_make_url_clickable_cb', $ret );
2653
2654 $ret = preg_replace_callback( '#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret );
2655 $ret = preg_replace_callback( '#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret );
2656
2657 $ret = substr( $ret, 1, -1 ); // Remove our whitespace padding.
2658 $r .= $ret;
2659 }
2660 }
2661
2662 // Cleanup of accidental links within links
2663 return preg_replace( '#(<a([ \r\n\t]+[^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i', "$1$3</a>", $r );
2664}
2665
2666/**
2667 * Breaks a string into chunks by splitting at whitespace characters.
2668 * The length of each returned chunk is as close to the specified length goal as possible,
2669 * with the caveat that each chunk includes its trailing delimiter.
2670 * Chunks longer than the goal are guaranteed to not have any inner whitespace.
2671 *
2672 * Joining the returned chunks with empty delimiters reconstructs the input string losslessly.
2673 *
2674 * Input string must have no null characters (or eventual transformations on output chunks must not care about null characters)
2675 *
2676 * _split_str_by_whitespace( "1234 67890 1234 67890a cd 1234 890 123456789 1234567890a 45678 1 3 5 7 90 ", 10 ) ==
2677 * array (
2678 * 0 => '1234 67890 ', // 11 characters: Perfect split
2679 * 1 => '1234 ', // 5 characters: '1234 67890a' was too long
2680 * 2 => '67890a cd ', // 10 characters: '67890a cd 1234' was too long
2681 * 3 => '1234 890 ', // 11 characters: Perfect split
2682 * 4 => '123456789 ', // 10 characters: '123456789 1234567890a' was too long
2683 * 5 => '1234567890a ', // 12 characters: Too long, but no inner whitespace on which to split
2684 * 6 => ' 45678 ', // 11 characters: Perfect split
2685 * 7 => '1 3 5 7 90 ', // 11 characters: End of $string
2686 * );
2687 *
2688 * @since 3.4.0
2689 * @access private
2690 *
2691 * @param string $string The string to split.
2692 * @param int $goal The desired chunk length.
2693 * @return array Numeric array of chunks.
2694 */
2695function _split_str_by_whitespace( $string, $goal ) {
2696 $chunks = array();
2697
2698 $string_nullspace = strtr( $string, "\r\n\t\v\f ", "\000\000\000\000\000\000" );
2699
2700 while ( $goal < strlen( $string_nullspace ) ) {
2701 $pos = strrpos( substr( $string_nullspace, 0, $goal + 1 ), "\000" );
2702
2703 if ( false === $pos ) {
2704 $pos = strpos( $string_nullspace, "\000", $goal + 1 );
2705 if ( false === $pos ) {
2706 break;
2707 }
2708 }
2709
2710 $chunks[] = substr( $string, 0, $pos + 1 );
2711 $string = substr( $string, $pos + 1 );
2712 $string_nullspace = substr( $string_nullspace, $pos + 1 );
2713 }
2714
2715 if ( $string ) {
2716 $chunks[] = $string;
2717 }
2718
2719 return $chunks;
2720}
2721
2722/**
2723 * Adds rel nofollow string to all HTML A elements in content.
2724 *
2725 * @since 1.5.0
2726 *
2727 * @param string $text Content that may contain HTML A elements.
2728 * @return string Converted content.
2729 */
2730function wp_rel_nofollow( $text ) {
2731 // This is a pre save filter, so text is already escaped.
2732 $text = stripslashes($text);
2733 $text = preg_replace_callback('|<a (.+?)>|i', 'wp_rel_nofollow_callback', $text);
2734 return wp_slash( $text );
2735}
2736
2737/**
2738 * Callback to add rel=nofollow string to HTML A element.
2739 *
2740 * Will remove already existing rel="nofollow" and rel='nofollow' from the
2741 * string to prevent from invalidating (X)HTML.
2742 *
2743 * @since 2.3.0
2744 *
2745 * @param array $matches Single Match
2746 * @return string HTML A Element with rel nofollow.
2747 */
2748function wp_rel_nofollow_callback( $matches ) {
2749 $text = $matches[1];
2750 $atts = shortcode_parse_atts( $matches[1] );
2751 $rel = 'nofollow';
2752
2753 if ( preg_match( '%href=["\'](' . preg_quote( set_url_scheme( home_url(), 'http' ) ) . ')%i', $text ) ||
2754 preg_match( '%href=["\'](' . preg_quote( set_url_scheme( home_url(), 'https' ) ) . ')%i', $text )
2755 ) {
2756 return "<a $text>";
2757 }
2758
2759 if ( ! empty( $atts['rel'] ) ) {
2760 $parts = array_map( 'trim', explode( ' ', $atts['rel'] ) );
2761 if ( false === array_search( 'nofollow', $parts ) ) {
2762 $parts[] = 'nofollow';
2763 }
2764 $rel = implode( ' ', $parts );
2765 unset( $atts['rel'] );
2766
2767 $html = '';
2768 foreach ( $atts as $name => $value ) {
2769 $html .= "{$name}=\"$value\" ";
2770 }
2771 $text = trim( $html );
2772 }
2773 return "<a $text rel=\"$rel\">";
2774}
2775
2776/**
2777 * Convert one smiley code to the icon graphic file equivalent.
2778 *
2779 * Callback handler for convert_smilies().
2780 *
2781 * Looks up one smiley code in the $wpsmiliestrans global array and returns an
2782 * `<img>` string for that smiley.
2783 *
2784 * @since 2.8.0
2785 *
2786 * @global array $wpsmiliestrans
2787 *
2788 * @param array $matches Single match. Smiley code to convert to image.
2789 * @return string Image string for smiley.
2790 */
2791function translate_smiley( $matches ) {
2792 global $wpsmiliestrans;
2793
2794 if ( count( $matches ) == 0 )
2795 return '';
2796
2797 $smiley = trim( reset( $matches ) );
2798 $img = $wpsmiliestrans[ $smiley ];
2799
2800 $matches = array();
2801 $ext = preg_match( '/\.([^.]+)$/', $img, $matches ) ? strtolower( $matches[1] ) : false;
2802 $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png' );
2803
2804 // Don't convert smilies that aren't images - they're probably emoji.
2805 if ( ! in_array( $ext, $image_exts ) ) {
2806 return $img;
2807 }
2808
2809 /**
2810 * Filters the Smiley image URL before it's used in the image element.
2811 *
2812 * @since 2.9.0
2813 *
2814 * @param string $smiley_url URL for the smiley image.
2815 * @param string $img Filename for the smiley image.
2816 * @param string $site_url Site URL, as returned by site_url().
2817 */
2818 $src_url = apply_filters( 'smilies_src', includes_url( "images/smilies/$img" ), $img, site_url() );
2819
2820 return sprintf( '<img src="%s" alt="%s" class="wp-smiley" style="height: 1em; max-height: 1em;" />', esc_url( $src_url ), esc_attr( $smiley ) );
2821}
2822
2823/**
2824 * Convert text equivalent of smilies to images.
2825 *
2826 * Will only convert smilies if the option 'use_smilies' is true and the global
2827 * used in the function isn't empty.
2828 *
2829 * @since 0.71
2830 *
2831 * @global string|array $wp_smiliessearch
2832 *
2833 * @param string $text Content to convert smilies from text.
2834 * @return string Converted content with text smilies replaced with images.
2835 */
2836function convert_smilies( $text ) {
2837 global $wp_smiliessearch;
2838 $output = '';
2839 if ( get_option( 'use_smilies' ) && ! empty( $wp_smiliessearch ) ) {
2840 // HTML loop taken from texturize function, could possible be consolidated
2841 $textarr = preg_split( '/(<.*>)/U', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // capture the tags as well as in between
2842 $stop = count( $textarr );// loop stuff
2843
2844 // Ignore proessing of specific tags
2845 $tags_to_ignore = 'code|pre|style|script|textarea';
2846 $ignore_block_element = '';
2847
2848 for ( $i = 0; $i < $stop; $i++ ) {
2849 $content = $textarr[$i];
2850
2851 // If we're in an ignore block, wait until we find its closing tag
2852 if ( '' == $ignore_block_element && preg_match( '/^<(' . $tags_to_ignore . ')>/', $content, $matches ) ) {
2853 $ignore_block_element = $matches[1];
2854 }
2855
2856 // If it's not a tag and not in ignore block
2857 if ( '' == $ignore_block_element && strlen( $content ) > 0 && '<' != $content[0] ) {
2858 $content = preg_replace_callback( $wp_smiliessearch, 'translate_smiley', $content );
2859 }
2860
2861 // did we exit ignore block
2862 if ( '' != $ignore_block_element && '</' . $ignore_block_element . '>' == $content ) {
2863 $ignore_block_element = '';
2864 }
2865
2866 $output .= $content;
2867 }
2868 } else {
2869 // return default text.
2870 $output = $text;
2871 }
2872 return $output;
2873}
2874
2875/**
2876 * Verifies that an email is valid.
2877 *
2878 * Does not grok i18n domains. Not RFC compliant.
2879 *
2880 * @since 0.71
2881 *
2882 * @param string $email Email address to verify.
2883 * @param bool $deprecated Deprecated.
2884 * @return string|bool Either false or the valid email address.
2885 */
2886function is_email( $email, $deprecated = false ) {
2887 if ( ! empty( $deprecated ) )
2888 _deprecated_argument( __FUNCTION__, '3.0.0' );
2889
2890 // Test for the minimum length the email can be
2891 if ( strlen( $email ) < 6 ) {
2892 /**
2893 * Filters whether an email address is valid.
2894 *
2895 * This filter is evaluated under several different contexts, such as 'email_too_short',
2896 * 'email_no_at', 'local_invalid_chars', 'domain_period_sequence', 'domain_period_limits',
2897 * 'domain_no_periods', 'sub_hyphen_limits', 'sub_invalid_chars', or no specific context.
2898 *
2899 * @since 2.8.0
2900 *
2901 * @param bool $is_email Whether the email address has passed the is_email() checks. Default false.
2902 * @param string $email The email address being checked.
2903 * @param string $context Context under which the email was tested.
2904 */
2905 return apply_filters( 'is_email', false, $email, 'email_too_short' );
2906 }
2907
2908 // Test for an @ character after the first position
2909 if ( strpos( $email, '@', 1 ) === false ) {
2910 /** This filter is documented in wp-includes/formatting.php */
2911 return apply_filters( 'is_email', false, $email, 'email_no_at' );
2912 }
2913
2914 // Split out the local and domain parts
2915 list( $local, $domain ) = explode( '@', $email, 2 );
2916
2917 // LOCAL PART
2918 // Test for invalid characters
2919 if ( !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local ) ) {
2920 /** This filter is documented in wp-includes/formatting.php */
2921 return apply_filters( 'is_email', false, $email, 'local_invalid_chars' );
2922 }
2923
2924 // DOMAIN PART
2925 // Test for sequences of periods
2926 if ( preg_match( '/\.{2,}/', $domain ) ) {
2927 /** This filter is documented in wp-includes/formatting.php */
2928 return apply_filters( 'is_email', false, $email, 'domain_period_sequence' );
2929 }
2930
2931 // Test for leading and trailing periods and whitespace
2932 if ( trim( $domain, " \t\n\r\0\x0B." ) !== $domain ) {
2933 /** This filter is documented in wp-includes/formatting.php */
2934 return apply_filters( 'is_email', false, $email, 'domain_period_limits' );
2935 }
2936
2937 // Split the domain into subs
2938 $subs = explode( '.', $domain );
2939
2940 // Assume the domain will have at least two subs
2941 if ( 2 > count( $subs ) ) {
2942 /** This filter is documented in wp-includes/formatting.php */
2943 return apply_filters( 'is_email', false, $email, 'domain_no_periods' );
2944 }
2945
2946 // Loop through each sub
2947 foreach ( $subs as $sub ) {
2948 // Test for leading and trailing hyphens and whitespace
2949 if ( trim( $sub, " \t\n\r\0\x0B-" ) !== $sub ) {
2950 /** This filter is documented in wp-includes/formatting.php */
2951 return apply_filters( 'is_email', false, $email, 'sub_hyphen_limits' );
2952 }
2953
2954 // Test for invalid characters
2955 if ( !preg_match('/^[a-z0-9-]+$/i', $sub ) ) {
2956 /** This filter is documented in wp-includes/formatting.php */
2957 return apply_filters( 'is_email', false, $email, 'sub_invalid_chars' );
2958 }
2959 }
2960
2961 // Congratulations your email made it!
2962 /** This filter is documented in wp-includes/formatting.php */
2963 return apply_filters( 'is_email', $email, $email, null );
2964}
2965
2966/**
2967 * Convert to ASCII from email subjects.
2968 *
2969 * @since 1.2.0
2970 *
2971 * @param string $string Subject line
2972 * @return string Converted string to ASCII
2973 */
2974function wp_iso_descrambler( $string ) {
2975 /* this may only work with iso-8859-1, I'm afraid */
2976 if (!preg_match('#\=\?(.+)\?Q\?(.+)\?\=#i', $string, $matches)) {
2977 return $string;
2978 } else {
2979 $subject = str_replace('_', ' ', $matches[2]);
2980 return preg_replace_callback( '#\=([0-9a-f]{2})#i', '_wp_iso_convert', $subject );
2981 }
2982}
2983
2984/**
2985 * Helper function to convert hex encoded chars to ASCII
2986 *
2987 * @since 3.1.0
2988 * @access private
2989 *
2990 * @param array $match The preg_replace_callback matches array
2991 * @return string Converted chars
2992 */
2993function _wp_iso_convert( $match ) {
2994 return chr( hexdec( strtolower( $match[1] ) ) );
2995}
2996
2997/**
2998 * Returns a date in the GMT equivalent.
2999 *
3000 * Requires and returns a date in the Y-m-d H:i:s format. If there is a
3001 * timezone_string available, the date is assumed to be in that timezone,
3002 * otherwise it simply subtracts the value of the 'gmt_offset' option. Return
3003 * format can be overridden using the $format parameter.
3004 *
3005 * @since 1.2.0
3006 *
3007 * @param string $string The date to be converted.
3008 * @param string $format The format string for the returned date (default is Y-m-d H:i:s)
3009 * @return string GMT version of the date provided.
3010 */
3011function get_gmt_from_date( $string, $format = 'Y-m-d H:i:s' ) {
3012 $tz = get_option( 'timezone_string' );
3013 if ( $tz ) {
3014 $datetime = date_create( $string, new DateTimeZone( $tz ) );
3015 if ( ! $datetime ) {
3016 return gmdate( $format, 0 );
3017 }
3018 $datetime->setTimezone( new DateTimeZone( 'UTC' ) );
3019 $string_gmt = $datetime->format( $format );
3020 } else {
3021 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 ) ) {
3022 $datetime = strtotime( $string );
3023 if ( false === $datetime ) {
3024 return gmdate( $format, 0 );
3025 }
3026 return gmdate( $format, $datetime );
3027 }
3028 $string_time = gmmktime( $matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1] );
3029 $string_gmt = gmdate( $format, $string_time - get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
3030 }
3031 return $string_gmt;
3032}
3033
3034/**
3035 * Converts a GMT date into the correct format for the blog.
3036 *
3037 * Requires and returns a date in the Y-m-d H:i:s format. If there is a
3038 * timezone_string available, the returned date is in that timezone, otherwise
3039 * it simply adds the value of gmt_offset. Return format can be overridden
3040 * using the $format parameter
3041 *
3042 * @since 1.2.0
3043 *
3044 * @param string $string The date to be converted.
3045 * @param string $format The format string for the returned date (default is Y-m-d H:i:s)
3046 * @return string Formatted date relative to the timezone / GMT offset.
3047 */
3048function get_date_from_gmt( $string, $format = 'Y-m-d H:i:s' ) {
3049 $tz = get_option( 'timezone_string' );
3050 if ( $tz ) {
3051 $datetime = date_create( $string, new DateTimeZone( 'UTC' ) );
3052 if ( ! $datetime )
3053 return date( $format, 0 );
3054 $datetime->setTimezone( new DateTimeZone( $tz ) );
3055 $string_localtime = $datetime->format( $format );
3056 } else {
3057 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) )
3058 return date( $format, 0 );
3059 $string_time = gmmktime( $matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1] );
3060 $string_localtime = gmdate( $format, $string_time + get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
3061 }
3062 return $string_localtime;
3063}
3064
3065/**
3066 * Computes an offset in seconds from an iso8601 timezone.
3067 *
3068 * @since 1.5.0
3069 *
3070 * @param string $timezone Either 'Z' for 0 offset or '±hhmm'.
3071 * @return int|float The offset in seconds.
3072 */
3073function iso8601_timezone_to_offset( $timezone ) {
3074 // $timezone is either 'Z' or '[+|-]hhmm'
3075 if ($timezone == 'Z') {
3076 $offset = 0;
3077 } else {
3078 $sign = (substr($timezone, 0, 1) == '+') ? 1 : -1;
3079 $hours = intval(substr($timezone, 1, 2));
3080 $minutes = intval(substr($timezone, 3, 4)) / 60;
3081 $offset = $sign * HOUR_IN_SECONDS * ($hours + $minutes);
3082 }
3083 return $offset;
3084}
3085
3086/**
3087 * Converts an iso8601 date to MySQL DateTime format used by post_date[_gmt].
3088 *
3089 * @since 1.5.0
3090 *
3091 * @param string $date_string Date and time in ISO 8601 format {@link https://en.wikipedia.org/wiki/ISO_8601}.
3092 * @param string $timezone Optional. If set to GMT returns the time minus gmt_offset. Default is 'user'.
3093 * @return string The date and time in MySQL DateTime format - Y-m-d H:i:s.
3094 */
3095function iso8601_to_datetime( $date_string, $timezone = 'user' ) {
3096 $timezone = strtolower($timezone);
3097
3098 if ($timezone == 'gmt') {
3099
3100 preg_match('#([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(Z|[\+|\-][0-9]{2,4}){0,1}#', $date_string, $date_bits);
3101
3102 if (!empty($date_bits[7])) { // we have a timezone, so let's compute an offset
3103 $offset = iso8601_timezone_to_offset($date_bits[7]);
3104 } else { // we don't have a timezone, so we assume user local timezone (not server's!)
3105 $offset = HOUR_IN_SECONDS * get_option('gmt_offset');
3106 }
3107
3108 $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]);
3109 $timestamp -= $offset;
3110
3111 return gmdate('Y-m-d H:i:s', $timestamp);
3112
3113 } elseif ($timezone == 'user') {
3114 return preg_replace('#([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(Z|[\+|\-][0-9]{2,4}){0,1}#', '$1-$2-$3 $4:$5:$6', $date_string);
3115 }
3116}
3117
3118/**
3119 * Strips out all characters that are not allowable in an email.
3120 *
3121 * @since 1.5.0
3122 *
3123 * @param string $email Email address to filter.
3124 * @return string Filtered email address.
3125 */
3126function sanitize_email( $email ) {
3127 // Test for the minimum length the email can be
3128 if ( strlen( $email ) < 6 ) {
3129 /**
3130 * Filters a sanitized email address.
3131 *
3132 * This filter is evaluated under several contexts, including 'email_too_short',
3133 * 'email_no_at', 'local_invalid_chars', 'domain_period_sequence', 'domain_period_limits',
3134 * 'domain_no_periods', 'domain_no_valid_subs', or no context.
3135 *
3136 * @since 2.8.0
3137 *
3138 * @param string $email The sanitized email address.
3139 * @param string $email The email address, as provided to sanitize_email().
3140 * @param string $message A message to pass to the user.
3141 */
3142 return apply_filters( 'sanitize_email', '', $email, 'email_too_short' );
3143 }
3144
3145 // Test for an @ character after the first position
3146 if ( strpos( $email, '@', 1 ) === false ) {
3147 /** This filter is documented in wp-includes/formatting.php */
3148 return apply_filters( 'sanitize_email', '', $email, 'email_no_at' );
3149 }
3150
3151 // Split out the local and domain parts
3152 list( $local, $domain ) = explode( '@', $email, 2 );
3153
3154 // LOCAL PART
3155 // Test for invalid characters
3156 $local = preg_replace( '/[^a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]/', '', $local );
3157 if ( '' === $local ) {
3158 /** This filter is documented in wp-includes/formatting.php */
3159 return apply_filters( 'sanitize_email', '', $email, 'local_invalid_chars' );
3160 }
3161
3162 // DOMAIN PART
3163 // Test for sequences of periods
3164 $domain = preg_replace( '/\.{2,}/', '', $domain );
3165 if ( '' === $domain ) {
3166 /** This filter is documented in wp-includes/formatting.php */
3167 return apply_filters( 'sanitize_email', '', $email, 'domain_period_sequence' );
3168 }
3169
3170 // Test for leading and trailing periods and whitespace
3171 $domain = trim( $domain, " \t\n\r\0\x0B." );
3172 if ( '' === $domain ) {
3173 /** This filter is documented in wp-includes/formatting.php */
3174 return apply_filters( 'sanitize_email', '', $email, 'domain_period_limits' );
3175 }
3176
3177 // Split the domain into subs
3178 $subs = explode( '.', $domain );
3179
3180 // Assume the domain will have at least two subs
3181 if ( 2 > count( $subs ) ) {
3182 /** This filter is documented in wp-includes/formatting.php */
3183 return apply_filters( 'sanitize_email', '', $email, 'domain_no_periods' );
3184 }
3185
3186 // Create an array that will contain valid subs
3187 $new_subs = array();
3188
3189 // Loop through each sub
3190 foreach ( $subs as $sub ) {
3191 // Test for leading and trailing hyphens
3192 $sub = trim( $sub, " \t\n\r\0\x0B-" );
3193
3194 // Test for invalid characters
3195 $sub = preg_replace( '/[^a-z0-9-]+/i', '', $sub );
3196
3197 // If there's anything left, add it to the valid subs
3198 if ( '' !== $sub ) {
3199 $new_subs[] = $sub;
3200 }
3201 }
3202
3203 // If there aren't 2 or more valid subs
3204 if ( 2 > count( $new_subs ) ) {
3205 /** This filter is documented in wp-includes/formatting.php */
3206 return apply_filters( 'sanitize_email', '', $email, 'domain_no_valid_subs' );
3207 }
3208
3209 // Join valid subs into the new domain
3210 $domain = join( '.', $new_subs );
3211
3212 // Put the email back together
3213 $email = $local . '@' . $domain;
3214
3215 // Congratulations your email made it!
3216 /** This filter is documented in wp-includes/formatting.php */
3217 return apply_filters( 'sanitize_email', $email, $email, null );
3218}
3219
3220/**
3221 * Determines the difference between two timestamps.
3222 *
3223 * The difference is returned in a human readable format such as "1 hour",
3224 * "5 mins", "2 days".
3225 *
3226 * @since 1.5.0
3227 *
3228 * @param int $from Unix timestamp from which the difference begins.
3229 * @param int $to Optional. Unix timestamp to end the time difference. Default becomes time() if not set.
3230 * @return string Human readable time difference.
3231 */
3232function human_time_diff( $from, $to = '' ) {
3233 if ( empty( $to ) ) {
3234 $to = time();
3235 }
3236
3237 $diff = (int) abs( $to - $from );
3238
3239 if ( $diff < HOUR_IN_SECONDS ) {
3240 $mins = round( $diff / MINUTE_IN_SECONDS );
3241 if ( $mins <= 1 )
3242 $mins = 1;
3243 /* translators: Time difference between two dates, in minutes (min=minute). 1: Number of minutes */
3244 $since = sprintf( _n( '%s min', '%s mins', $mins ), $mins );
3245 } elseif ( $diff < DAY_IN_SECONDS && $diff >= HOUR_IN_SECONDS ) {
3246 $hours = round( $diff / HOUR_IN_SECONDS );
3247 if ( $hours <= 1 )
3248 $hours = 1;
3249 /* translators: Time difference between two dates, in hours. 1: Number of hours */
3250 $since = sprintf( _n( '%s hour', '%s hours', $hours ), $hours );
3251 } elseif ( $diff < WEEK_IN_SECONDS && $diff >= DAY_IN_SECONDS ) {
3252 $days = round( $diff / DAY_IN_SECONDS );
3253 if ( $days <= 1 )
3254 $days = 1;
3255 /* translators: Time difference between two dates, in days. 1: Number of days */
3256 $since = sprintf( _n( '%s day', '%s days', $days ), $days );
3257 } elseif ( $diff < MONTH_IN_SECONDS && $diff >= WEEK_IN_SECONDS ) {
3258 $weeks = round( $diff / WEEK_IN_SECONDS );
3259 if ( $weeks <= 1 )
3260 $weeks = 1;
3261 /* translators: Time difference between two dates, in weeks. 1: Number of weeks */
3262 $since = sprintf( _n( '%s week', '%s weeks', $weeks ), $weeks );
3263 } elseif ( $diff < YEAR_IN_SECONDS && $diff >= MONTH_IN_SECONDS ) {
3264 $months = round( $diff / MONTH_IN_SECONDS );
3265 if ( $months <= 1 )
3266 $months = 1;
3267 /* translators: Time difference between two dates, in months. 1: Number of months */
3268 $since = sprintf( _n( '%s month', '%s months', $months ), $months );
3269 } elseif ( $diff >= YEAR_IN_SECONDS ) {
3270 $years = round( $diff / YEAR_IN_SECONDS );
3271 if ( $years <= 1 )
3272 $years = 1;
3273 /* translators: Time difference between two dates, in years. 1: Number of years */
3274 $since = sprintf( _n( '%s year', '%s years', $years ), $years );
3275 }
3276
3277 /**
3278 * Filters the human readable difference between two timestamps.
3279 *
3280 * @since 4.0.0
3281 *
3282 * @param string $since The difference in human readable text.
3283 * @param int $diff The difference in seconds.
3284 * @param int $from Unix timestamp from which the difference begins.
3285 * @param int $to Unix timestamp to end the time difference.
3286 */
3287 return apply_filters( 'human_time_diff', $since, $diff, $from, $to );
3288}
3289
3290/**
3291 * Generates an excerpt from the content, if needed.
3292 *
3293 * The excerpt word amount will be 55 words and if the amount is greater than
3294 * that, then the string ' […]' will be appended to the excerpt. If the string
3295 * is less than 55 words, then the content will be returned as is.
3296 *
3297 * The 55 word limit can be modified by plugins/themes using the {@see 'excerpt_length'} filter
3298 * The ' […]' string can be modified by plugins/themes using the {@see 'excerpt_more'} filter
3299 *
3300 * @since 1.5.0
3301 *
3302 * @param string $text Optional. The excerpt. If set to empty, an excerpt is generated.
3303 * @return string The excerpt.
3304 */
3305function wp_trim_excerpt( $text = '' ) {
3306 $raw_excerpt = $text;
3307 if ( '' == $text ) {
3308 $text = get_the_content('');
3309
3310 $text = strip_shortcodes( $text );
3311 $text = excerpt_remove_blocks( $text );
3312
3313 /** This filter is documented in wp-includes/post-template.php */
3314 $text = apply_filters( 'the_content', $text );
3315 $text = str_replace(']]>', ']]>', $text);
3316
3317 /**
3318 * Filters the number of words in an excerpt.
3319 *
3320 * @since 2.7.0
3321 *
3322 * @param int $number The number of words. Default 55.
3323 */
3324 $excerpt_length = apply_filters( 'excerpt_length', 55 );
3325 /**
3326 * Filters the string in the "more" link displayed after a trimmed excerpt.
3327 *
3328 * @since 2.9.0
3329 *
3330 * @param string $more_string The string shown within the more link.
3331 */
3332 $excerpt_more = apply_filters( 'excerpt_more', ' ' . '[…]' );
3333 $text = wp_trim_words( $text, $excerpt_length, $excerpt_more );
3334 }
3335 /**
3336 * Filters the trimmed excerpt string.
3337 *
3338 * @since 2.8.0
3339 *
3340 * @param string $text The trimmed text.
3341 * @param string $raw_excerpt The text prior to trimming.
3342 */
3343 return apply_filters( 'wp_trim_excerpt', $text, $raw_excerpt );
3344}
3345
3346/**
3347 * Trims text to a certain number of words.
3348 *
3349 * This function is localized. For languages that count 'words' by the individual
3350 * character (such as East Asian languages), the $num_words argument will apply
3351 * to the number of individual characters.
3352 *
3353 * @since 3.3.0
3354 *
3355 * @param string $text Text to trim.
3356 * @param int $num_words Number of words. Default 55.
3357 * @param string $more Optional. What to append if $text needs to be trimmed. Default '…'.
3358 * @return string Trimmed text.
3359 */
3360function wp_trim_words( $text, $num_words = 55, $more = null ) {
3361 if ( null === $more ) {
3362 $more = __( '…' );
3363 }
3364
3365 $original_text = $text;
3366 $text = wp_strip_all_tags( $text );
3367
3368 /*
3369 * translators: If your word count is based on single characters (e.g. East Asian characters),
3370 * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
3371 * Do not translate into your own language.
3372 */
3373 if ( strpos( _x( 'words', 'Word count type. Do not translate!' ), 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
3374 $text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
3375 preg_match_all( '/./u', $text, $words_array );
3376 $words_array = array_slice( $words_array[0], 0, $num_words + 1 );
3377 $sep = '';
3378 } else {
3379 $words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
3380 $sep = ' ';
3381 }
3382
3383 if ( count( $words_array ) > $num_words ) {
3384 array_pop( $words_array );
3385 $text = implode( $sep, $words_array );
3386 $text = $text . $more;
3387 } else {
3388 $text = implode( $sep, $words_array );
3389 }
3390
3391 /**
3392 * Filters the text content after words have been trimmed.
3393 *
3394 * @since 3.3.0
3395 *
3396 * @param string $text The trimmed text.
3397 * @param int $num_words The number of words to trim the text to. Default 55.
3398 * @param string $more An optional string to append to the end of the trimmed text, e.g. ….
3399 * @param string $original_text The text before it was trimmed.
3400 */
3401 return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text );
3402}
3403
3404/**
3405 * Converts named entities into numbered entities.
3406 *
3407 * @since 1.5.1
3408 *
3409 * @param string $text The text within which entities will be converted.
3410 * @return string Text with converted entities.
3411 */
3412function ent2ncr( $text ) {
3413
3414 /**
3415 * Filters text before named entities are converted into numbered entities.
3416 *
3417 * A non-null string must be returned for the filter to be evaluated.
3418 *
3419 * @since 3.3.0
3420 *
3421 * @param null $converted_text The text to be converted. Default null.
3422 * @param string $text The text prior to entity conversion.
3423 */
3424 $filtered = apply_filters( 'pre_ent2ncr', null, $text );
3425 if ( null !== $filtered )
3426 return $filtered;
3427
3428 $to_ncr = array(
3429 '"' => '"',
3430 '&' => '&',
3431 '<' => '<',
3432 '>' => '>',
3433 '|' => '|',
3434 ' ' => ' ',
3435 '¡' => '¡',
3436 '¢' => '¢',
3437 '£' => '£',
3438 '¤' => '¤',
3439 '¥' => '¥',
3440 '¦' => '¦',
3441 '&brkbar;' => '¦',
3442 '§' => '§',
3443 '¨' => '¨',
3444 '¨' => '¨',
3445 '©' => '©',
3446 'ª' => 'ª',
3447 '«' => '«',
3448 '¬' => '¬',
3449 '­' => '­',
3450 '®' => '®',
3451 '¯' => '¯',
3452 '&hibar;' => '¯',
3453 '°' => '°',
3454 '±' => '±',
3455 '²' => '²',
3456 '³' => '³',
3457 '´' => '´',
3458 'µ' => 'µ',
3459 '¶' => '¶',
3460 '·' => '·',
3461 '¸' => '¸',
3462 '¹' => '¹',
3463 'º' => 'º',
3464 '»' => '»',
3465 '¼' => '¼',
3466 '½' => '½',
3467 '¾' => '¾',
3468 '¿' => '¿',
3469 'À' => 'À',
3470 'Á' => 'Á',
3471 'Â' => 'Â',
3472 'Ã' => 'Ã',
3473 'Ä' => 'Ä',
3474 'Å' => 'Å',
3475 'Æ' => 'Æ',
3476 'Ç' => 'Ç',
3477 'È' => 'È',
3478 'É' => 'É',
3479 'Ê' => 'Ê',
3480 'Ë' => 'Ë',
3481 'Ì' => 'Ì',
3482 'Í' => 'Í',
3483 'Î' => 'Î',
3484 'Ï' => 'Ï',
3485 'Ð' => 'Ð',
3486 'Ñ' => 'Ñ',
3487 'Ò' => 'Ò',
3488 'Ó' => 'Ó',
3489 'Ô' => 'Ô',
3490 'Õ' => 'Õ',
3491 'Ö' => 'Ö',
3492 '×' => '×',
3493 'Ø' => 'Ø',
3494 'Ù' => 'Ù',
3495 'Ú' => 'Ú',
3496 'Û' => 'Û',
3497 'Ü' => 'Ü',
3498 'Ý' => 'Ý',
3499 'Þ' => 'Þ',
3500 'ß' => 'ß',
3501 'à' => 'à',
3502 'á' => 'á',
3503 'â' => 'â',
3504 'ã' => 'ã',
3505 'ä' => 'ä',
3506 'å' => 'å',
3507 'æ' => 'æ',
3508 'ç' => 'ç',
3509 'è' => 'è',
3510 'é' => 'é',
3511 'ê' => 'ê',
3512 'ë' => 'ë',
3513 'ì' => 'ì',
3514 'í' => 'í',
3515 'î' => 'î',
3516 'ï' => 'ï',
3517 'ð' => 'ð',
3518 'ñ' => 'ñ',
3519 'ò' => 'ò',
3520 'ó' => 'ó',
3521 'ô' => 'ô',
3522 'õ' => 'õ',
3523 'ö' => 'ö',
3524 '÷' => '÷',
3525 'ø' => 'ø',
3526 'ù' => 'ù',
3527 'ú' => 'ú',
3528 'û' => 'û',
3529 'ü' => 'ü',
3530 'ý' => 'ý',
3531 'þ' => 'þ',
3532 'ÿ' => 'ÿ',
3533 'Œ' => 'Œ',
3534 'œ' => 'œ',
3535 'Š' => 'Š',
3536 'š' => 'š',
3537 'Ÿ' => 'Ÿ',
3538 'ƒ' => 'ƒ',
3539 'ˆ' => 'ˆ',
3540 '˜' => '˜',
3541 'Α' => 'Α',
3542 'Β' => 'Β',
3543 'Γ' => 'Γ',
3544 'Δ' => 'Δ',
3545 'Ε' => 'Ε',
3546 'Ζ' => 'Ζ',
3547 'Η' => 'Η',
3548 'Θ' => 'Θ',
3549 'Ι' => 'Ι',
3550 'Κ' => 'Κ',
3551 'Λ' => 'Λ',
3552 'Μ' => 'Μ',
3553 'Ν' => 'Ν',
3554 'Ξ' => 'Ξ',
3555 'Ο' => 'Ο',
3556 'Π' => 'Π',
3557 'Ρ' => 'Ρ',
3558 'Σ' => 'Σ',
3559 'Τ' => 'Τ',
3560 'Υ' => 'Υ',
3561 'Φ' => 'Φ',
3562 'Χ' => 'Χ',
3563 'Ψ' => 'Ψ',
3564 'Ω' => 'Ω',
3565 'α' => 'α',
3566 'β' => 'β',
3567 'γ' => 'γ',
3568 'δ' => 'δ',
3569 'ε' => 'ε',
3570 'ζ' => 'ζ',
3571 'η' => 'η',
3572 'θ' => 'θ',
3573 'ι' => 'ι',
3574 'κ' => 'κ',
3575 'λ' => 'λ',
3576 'μ' => 'μ',
3577 'ν' => 'ν',
3578 'ξ' => 'ξ',
3579 'ο' => 'ο',
3580 'π' => 'π',
3581 'ρ' => 'ρ',
3582 'ς' => 'ς',
3583 'σ' => 'σ',
3584 'τ' => 'τ',
3585 'υ' => 'υ',
3586 'φ' => 'φ',
3587 'χ' => 'χ',
3588 'ψ' => 'ψ',
3589 'ω' => 'ω',
3590 'ϑ' => 'ϑ',
3591 'ϒ' => 'ϒ',
3592 'ϖ' => 'ϖ',
3593 ' ' => ' ',
3594 ' ' => ' ',
3595 ' ' => ' ',
3596 '‌' => '‌',
3597 '‍' => '‍',
3598 '‎' => '‎',
3599 '‏' => '‏',
3600 '–' => '–',
3601 '—' => '—',
3602 '‘' => '‘',
3603 '’' => '’',
3604 '‚' => '‚',
3605 '“' => '“',
3606 '”' => '”',
3607 '„' => '„',
3608 '†' => '†',
3609 '‡' => '‡',
3610 '•' => '•',
3611 '…' => '…',
3612 '‰' => '‰',
3613 '′' => '′',
3614 '″' => '″',
3615 '‹' => '‹',
3616 '›' => '›',
3617 '‾' => '‾',
3618 '⁄' => '⁄',
3619 '€' => '€',
3620 'ℑ' => 'ℑ',
3621 '℘' => '℘',
3622 'ℜ' => 'ℜ',
3623 '™' => '™',
3624 'ℵ' => 'ℵ',
3625 '↵' => '↵',
3626 '⇐' => '⇐',
3627 '⇑' => '⇑',
3628 '⇒' => '⇒',
3629 '⇓' => '⇓',
3630 '⇔' => '⇔',
3631 '∀' => '∀',
3632 '∂' => '∂',
3633 '∃' => '∃',
3634 '∅' => '∅',
3635 '∇' => '∇',
3636 '∈' => '∈',
3637 '∉' => '∉',
3638 '∋' => '∋',
3639 '∏' => '∏',
3640 '∑' => '∑',
3641 '−' => '−',
3642 '∗' => '∗',
3643 '√' => '√',
3644 '∝' => '∝',
3645 '∞' => '∞',
3646 '∠' => '∠',
3647 '∧' => '∧',
3648 '∨' => '∨',
3649 '∩' => '∩',
3650 '∪' => '∪',
3651 '∫' => '∫',
3652 '∴' => '∴',
3653 '∼' => '∼',
3654 '≅' => '≅',
3655 '≈' => '≈',
3656 '≠' => '≠',
3657 '≡' => '≡',
3658 '≤' => '≤',
3659 '≥' => '≥',
3660 '⊂' => '⊂',
3661 '⊃' => '⊃',
3662 '⊄' => '⊄',
3663 '⊆' => '⊆',
3664 '⊇' => '⊇',
3665 '⊕' => '⊕',
3666 '⊗' => '⊗',
3667 '⊥' => '⊥',
3668 '⋅' => '⋅',
3669 '⌈' => '⌈',
3670 '⌉' => '⌉',
3671 '⌊' => '⌊',
3672 '⌋' => '⌋',
3673 '⟨' => '〈',
3674 '⟩' => '〉',
3675 '←' => '←',
3676 '↑' => '↑',
3677 '→' => '→',
3678 '↓' => '↓',
3679 '↔' => '↔',
3680 '◊' => '◊',
3681 '♠' => '♠',
3682 '♣' => '♣',
3683 '♥' => '♥',
3684 '♦' => '♦'
3685 );
3686
3687 return str_replace( array_keys($to_ncr), array_values($to_ncr), $text );
3688}
3689
3690/**
3691 * Formats text for the editor.
3692 *
3693 * Generally the browsers treat everything inside a textarea as text, but
3694 * it is still a good idea to HTML entity encode `<`, `>` and `&` in the content.
3695 *
3696 * The filter {@see 'format_for_editor'} is applied here. If `$text` is empty the
3697 * filter will be applied to an empty string.
3698 *
3699 * @since 4.3.0
3700 *
3701 * @see _WP_Editors::editor()
3702 *
3703 * @param string $text The text to be formatted.
3704 * @param string $default_editor The default editor for the current user.
3705 * It is usually either 'html' or 'tinymce'.
3706 * @return string The formatted text after filter is applied.
3707 */
3708function format_for_editor( $text, $default_editor = null ) {
3709 if ( $text ) {
3710 $text = htmlspecialchars( $text, ENT_NOQUOTES, get_option( 'blog_charset' ) );
3711 }
3712
3713 /**
3714 * Filters the text after it is formatted for the editor.
3715 *
3716 * @since 4.3.0
3717 *
3718 * @param string $text The formatted text.
3719 * @param string $default_editor The default editor for the current user.
3720 * It is usually either 'html' or 'tinymce'.
3721 */
3722 return apply_filters( 'format_for_editor', $text, $default_editor );
3723}
3724
3725/**
3726 * Perform a deep string replace operation to ensure the values in $search are no longer present
3727 *
3728 * Repeats the replacement operation until it no longer replaces anything so as to remove "nested" values
3729 * e.g. $subject = '%0%0%0DDD', $search ='%0D', $result ='' rather than the '%0%0DD' that
3730 * str_replace would return
3731 *
3732 * @since 2.8.1
3733 * @access private
3734 *
3735 * @param string|array $search The value being searched for, otherwise known as the needle.
3736 * An array may be used to designate multiple needles.
3737 * @param string $subject The string being searched and replaced on, otherwise known as the haystack.
3738 * @return string The string with the replaced svalues.
3739 */
3740function _deep_replace( $search, $subject ) {
3741 $subject = (string) $subject;
3742
3743 $count = 1;
3744 while ( $count ) {
3745 $subject = str_replace( $search, '', $subject, $count );
3746 }
3747
3748 return $subject;
3749}
3750
3751/**
3752 * Escapes data for use in a MySQL query.
3753 *
3754 * Usually you should prepare queries using wpdb::prepare().
3755 * Sometimes, spot-escaping is required or useful. One example
3756 * is preparing an array for use in an IN clause.
3757 *
3758 * NOTE: Since 4.8.3, '%' characters will be replaced with a placeholder string,
3759 * this prevents certain SQLi attacks from taking place. This change in behaviour
3760 * may cause issues for code that expects the return value of esc_sql() to be useable
3761 * for other purposes.
3762 *
3763 * @since 2.8.0
3764 *
3765 * @global wpdb $wpdb WordPress database abstraction object.
3766 *
3767 * @param string|array $data Unescaped data
3768 * @return string|array Escaped data
3769 */
3770function esc_sql( $data ) {
3771 global $wpdb;
3772 return $wpdb->_escape( $data );
3773}
3774
3775/**
3776 * Checks and cleans a URL.
3777 *
3778 * A number of characters are removed from the URL. If the URL is for displaying
3779 * (the default behaviour) ampersands are also replaced. The {@see 'clean_url'} filter
3780 * is applied to the returned cleaned URL.
3781 *
3782 * @since 2.8.0
3783 *
3784 * @param string $url The URL to be cleaned.
3785 * @param array $protocols Optional. An array of acceptable protocols.
3786 * Defaults to return value of wp_allowed_protocols()
3787 * @param string $_context Private. Use esc_url_raw() for database usage.
3788 * @return string The cleaned $url after the {@see 'clean_url'} filter is applied.
3789 */
3790function esc_url( $url, $protocols = null, $_context = 'display' ) {
3791 $original_url = $url;
3792
3793 if ( '' == $url )
3794 return $url;
3795
3796 $url = str_replace( ' ', '%20', $url );
3797 $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url);
3798
3799 if ( '' === $url ) {
3800 return $url;
3801 }
3802
3803 if ( 0 !== stripos( $url, 'mailto:' ) ) {
3804 $strip = array('%0d', '%0a', '%0D', '%0A');
3805 $url = _deep_replace($strip, $url);
3806 }
3807
3808 $url = str_replace(';//', '://', $url);
3809 /* If the URL doesn't appear to contain a scheme, we
3810 * presume it needs http:// prepended (unless a relative
3811 * link starting with /, # or ? or a php file).
3812 */
3813 if ( strpos($url, ':') === false && ! in_array( $url[0], array( '/', '#', '?' ) ) &&
3814 ! preg_match('/^[a-z0-9-]+?\.php/i', $url) )
3815 $url = 'http://' . $url;
3816
3817 // Replace ampersands and single quotes only when displaying.
3818 if ( 'display' == $_context ) {
3819 $url = wp_kses_normalize_entities( $url );
3820 $url = str_replace( '&', '&', $url );
3821 $url = str_replace( "'", ''', $url );
3822 }
3823
3824 if ( ( false !== strpos( $url, '[' ) ) || ( false !== strpos( $url, ']' ) ) ) {
3825
3826 $parsed = wp_parse_url( $url );
3827 $front = '';
3828
3829 if ( isset( $parsed['scheme'] ) ) {
3830 $front .= $parsed['scheme'] . '://';
3831 } elseif ( '/' === $url[0] ) {
3832 $front .= '//';
3833 }
3834
3835 if ( isset( $parsed['user'] ) ) {
3836 $front .= $parsed['user'];
3837 }
3838
3839 if ( isset( $parsed['pass'] ) ) {
3840 $front .= ':' . $parsed['pass'];
3841 }
3842
3843 if ( isset( $parsed['user'] ) || isset( $parsed['pass'] ) ) {
3844 $front .= '@';
3845 }
3846
3847 if ( isset( $parsed['host'] ) ) {
3848 $front .= $parsed['host'];
3849 }
3850
3851 if ( isset( $parsed['port'] ) ) {
3852 $front .= ':' . $parsed['port'];
3853 }
3854
3855 $end_dirty = str_replace( $front, '', $url );
3856 $end_clean = str_replace( array( '[', ']' ), array( '%5B', '%5D' ), $end_dirty );
3857 $url = str_replace( $end_dirty, $end_clean, $url );
3858
3859 }
3860
3861 if ( '/' === $url[0] ) {
3862 $good_protocol_url = $url;
3863 } else {
3864 if ( ! is_array( $protocols ) )
3865 $protocols = wp_allowed_protocols();
3866 $good_protocol_url = wp_kses_bad_protocol( $url, $protocols );
3867 if ( strtolower( $good_protocol_url ) != strtolower( $url ) )
3868 return '';
3869 }
3870
3871 /**
3872 * Filters a string cleaned and escaped for output as a URL.
3873 *
3874 * @since 2.3.0
3875 *
3876 * @param string $good_protocol_url The cleaned URL to be returned.
3877 * @param string $original_url The URL prior to cleaning.
3878 * @param string $_context If 'display', replace ampersands and single quotes only.
3879 */
3880 return apply_filters( 'clean_url', $good_protocol_url, $original_url, $_context );
3881}
3882
3883/**
3884 * Performs esc_url() for database usage.
3885 *
3886 * @since 2.8.0
3887 *
3888 * @param string $url The URL to be cleaned.
3889 * @param array $protocols An array of acceptable protocols.
3890 * @return string The cleaned URL.
3891 */
3892function esc_url_raw( $url, $protocols = null ) {
3893 return esc_url( $url, $protocols, 'db' );
3894}
3895
3896/**
3897 * Convert entities, while preserving already-encoded entities.
3898 *
3899 * @link https://secure.php.net/htmlentities Borrowed from the PHP Manual user notes.
3900 *
3901 * @since 1.2.2
3902 *
3903 * @param string $myHTML The text to be converted.
3904 * @return string Converted text.
3905 */
3906function htmlentities2( $myHTML ) {
3907 $translation_table = get_html_translation_table( HTML_ENTITIES, ENT_QUOTES );
3908 $translation_table[chr(38)] = '&';
3909 return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/", "&", strtr($myHTML, $translation_table) );
3910}
3911
3912/**
3913 * Escape single quotes, htmlspecialchar " < > &, and fix line endings.
3914 *
3915 * Escapes text strings for echoing in JS. It is intended to be used for inline JS
3916 * (in a tag attribute, for example onclick="..."). Note that the strings have to
3917 * be in single quotes. The {@see 'js_escape'} filter is also applied here.
3918 *
3919 * @since 2.8.0
3920 *
3921 * @param string $text The text to be escaped.
3922 * @return string Escaped text.
3923 */
3924function esc_js( $text ) {
3925 $safe_text = wp_check_invalid_utf8( $text );
3926 $safe_text = _wp_specialchars( $safe_text, ENT_COMPAT );
3927 $safe_text = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", stripslashes( $safe_text ) );
3928 $safe_text = str_replace( "\r", '', $safe_text );
3929 $safe_text = str_replace( "\n", '\\n', addslashes( $safe_text ) );
3930 /**
3931 * Filters a string cleaned and escaped for output in JavaScript.
3932 *
3933 * Text passed to esc_js() is stripped of invalid or special characters,
3934 * and properly slashed for output.
3935 *
3936 * @since 2.0.6
3937 *
3938 * @param string $safe_text The text after it has been escaped.
3939 * @param string $text The text prior to being escaped.
3940 */
3941 return apply_filters( 'js_escape', $safe_text, $text );
3942}
3943
3944/**
3945 * Escaping for HTML blocks.
3946 *
3947 * @since 2.8.0
3948 *
3949 * @param string $text
3950 * @return string
3951 */
3952function esc_html( $text ) {
3953 $safe_text = wp_check_invalid_utf8( $text );
3954 $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
3955 /**
3956 * Filters a string cleaned and escaped for output in HTML.
3957 *
3958 * Text passed to esc_html() is stripped of invalid or special characters
3959 * before output.
3960 *
3961 * @since 2.8.0
3962 *
3963 * @param string $safe_text The text after it has been escaped.
3964 * @param string $text The text prior to being escaped.
3965 */
3966 return apply_filters( 'esc_html', $safe_text, $text );
3967}
3968
3969/**
3970 * Escaping for HTML attributes.
3971 *
3972 * @since 2.8.0
3973 *
3974 * @param string $text
3975 * @return string
3976 */
3977function esc_attr( $text ) {
3978 $safe_text = wp_check_invalid_utf8( $text );
3979 $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
3980 /**
3981 * Filters a string cleaned and escaped for output in an HTML attribute.
3982 *
3983 * Text passed to esc_attr() is stripped of invalid or special characters
3984 * before output.
3985 *
3986 * @since 2.0.6
3987 *
3988 * @param string $safe_text The text after it has been escaped.
3989 * @param string $text The text prior to being escaped.
3990 */
3991 return apply_filters( 'attribute_escape', $safe_text, $text );
3992}
3993
3994/**
3995 * Escaping for textarea values.
3996 *
3997 * @since 3.1.0
3998 *
3999 * @param string $text
4000 * @return string
4001 */
4002function esc_textarea( $text ) {
4003 $safe_text = htmlspecialchars( $text, ENT_QUOTES, get_option( 'blog_charset' ) );
4004 /**
4005 * Filters a string cleaned and escaped for output in a textarea element.
4006 *
4007 * @since 3.1.0
4008 *
4009 * @param string $safe_text The text after it has been escaped.
4010 * @param string $text The text prior to being escaped.
4011 */
4012 return apply_filters( 'esc_textarea', $safe_text, $text );
4013}
4014
4015/**
4016 * Escape an HTML tag name.
4017 *
4018 * @since 2.5.0
4019 *
4020 * @param string $tag_name
4021 * @return string
4022 */
4023function tag_escape( $tag_name ) {
4024 $safe_tag = strtolower( preg_replace('/[^a-zA-Z0-9_:]/', '', $tag_name) );
4025 /**
4026 * Filters a string cleaned and escaped for output as an HTML tag.
4027 *
4028 * @since 2.8.0
4029 *
4030 * @param string $safe_tag The tag name after it has been escaped.
4031 * @param string $tag_name The text before it was escaped.
4032 */
4033 return apply_filters( 'tag_escape', $safe_tag, $tag_name );
4034}
4035
4036/**
4037 * Convert full URL paths to absolute paths.
4038 *
4039 * Removes the http or https protocols and the domain. Keeps the path '/' at the
4040 * beginning, so it isn't a true relative link, but from the web root base.
4041 *
4042 * @since 2.1.0
4043 * @since 4.1.0 Support was added for relative URLs.
4044 *
4045 * @param string $link Full URL path.
4046 * @return string Absolute path.
4047 */
4048function wp_make_link_relative( $link ) {
4049 return preg_replace( '|^(https?:)?//[^/]+(/?.*)|i', '$2', $link );
4050}
4051
4052/**
4053 * Sanitises various option values based on the nature of the option.
4054 *
4055 * This is basically a switch statement which will pass $value through a number
4056 * of functions depending on the $option.
4057 *
4058 * @since 2.0.5
4059 *
4060 * @global wpdb $wpdb WordPress database abstraction object.
4061 *
4062 * @param string $option The name of the option.
4063 * @param string $value The unsanitised value.
4064 * @return string Sanitized value.
4065 */
4066function sanitize_option( $option, $value ) {
4067 global $wpdb;
4068
4069 $original_value = $value;
4070 $error = '';
4071
4072 switch ( $option ) {
4073 case 'admin_email' :
4074 case 'new_admin_email' :
4075 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4076 if ( is_wp_error( $value ) ) {
4077 $error = $value->get_error_message();
4078 } else {
4079 $value = sanitize_email( $value );
4080 if ( ! is_email( $value ) ) {
4081 $error = __( 'The email address entered did not appear to be a valid email address. Please enter a valid email address.' );
4082 }
4083 }
4084 break;
4085
4086 case 'thumbnail_size_w':
4087 case 'thumbnail_size_h':
4088 case 'medium_size_w':
4089 case 'medium_size_h':
4090 case 'medium_large_size_w':
4091 case 'medium_large_size_h':
4092 case 'large_size_w':
4093 case 'large_size_h':
4094 case 'mailserver_port':
4095 case 'comment_max_links':
4096 case 'page_on_front':
4097 case 'page_for_posts':
4098 case 'rss_excerpt_length':
4099 case 'default_category':
4100 case 'default_email_category':
4101 case 'default_link_category':
4102 case 'close_comments_days_old':
4103 case 'comments_per_page':
4104 case 'thread_comments_depth':
4105 case 'users_can_register':
4106 case 'start_of_week':
4107 case 'site_icon':
4108 $value = absint( $value );
4109 break;
4110
4111 case 'posts_per_page':
4112 case 'posts_per_rss':
4113 $value = (int) $value;
4114 if ( empty($value) )
4115 $value = 1;
4116 if ( $value < -1 )
4117 $value = abs($value);
4118 break;
4119
4120 case 'default_ping_status':
4121 case 'default_comment_status':
4122 // Options that if not there have 0 value but need to be something like "closed"
4123 if ( $value == '0' || $value == '')
4124 $value = 'closed';
4125 break;
4126
4127 case 'blogdescription':
4128 case 'blogname':
4129 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4130 if ( $value !== $original_value ) {
4131 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', wp_encode_emoji( $original_value ) );
4132 }
4133
4134 if ( is_wp_error( $value ) ) {
4135 $error = $value->get_error_message();
4136 } else {
4137 $value = esc_html( $value );
4138 }
4139 break;
4140
4141 case 'blog_charset':
4142 $value = preg_replace('/[^a-zA-Z0-9_-]/', '', $value); // strips slashes
4143 break;
4144
4145 case 'blog_public':
4146 // This is the value if the settings checkbox is not checked on POST. Don't rely on this.
4147 if ( null === $value )
4148 $value = 1;
4149 else
4150 $value = intval( $value );
4151 break;
4152
4153 case 'date_format':
4154 case 'time_format':
4155 case 'mailserver_url':
4156 case 'mailserver_login':
4157 case 'mailserver_pass':
4158 case 'upload_path':
4159 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4160 if ( is_wp_error( $value ) ) {
4161 $error = $value->get_error_message();
4162 } else {
4163 $value = strip_tags( $value );
4164 $value = wp_kses_data( $value );
4165 }
4166 break;
4167
4168 case 'ping_sites':
4169 $value = explode( "\n", $value );
4170 $value = array_filter( array_map( 'trim', $value ) );
4171 $value = array_filter( array_map( 'esc_url_raw', $value ) );
4172 $value = implode( "\n", $value );
4173 break;
4174
4175 case 'gmt_offset':
4176 $value = preg_replace('/[^0-9:.-]/', '', $value); // strips slashes
4177 break;
4178
4179 case 'siteurl':
4180 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4181 if ( is_wp_error( $value ) ) {
4182 $error = $value->get_error_message();
4183 } else {
4184 if ( preg_match( '#http(s?)://(.+)#i', $value ) ) {
4185 $value = esc_url_raw( $value );
4186 } else {
4187 $error = __( 'The WordPress address you entered did not appear to be a valid URL. Please enter a valid URL.' );
4188 }
4189 }
4190 break;
4191
4192 case 'home':
4193 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4194 if ( is_wp_error( $value ) ) {
4195 $error = $value->get_error_message();
4196 } else {
4197 if ( preg_match( '#http(s?)://(.+)#i', $value ) ) {
4198 $value = esc_url_raw( $value );
4199 } else {
4200 $error = __( 'The Site address you entered did not appear to be a valid URL. Please enter a valid URL.' );
4201 }
4202 }
4203 break;
4204
4205 case 'WPLANG':
4206 $allowed = get_available_languages();
4207 if ( ! is_multisite() && defined( 'WPLANG' ) && '' !== WPLANG && 'en_US' !== WPLANG ) {
4208 $allowed[] = WPLANG;
4209 }
4210 if ( ! in_array( $value, $allowed ) && ! empty( $value ) ) {
4211 $value = get_option( $option );
4212 }
4213 break;
4214
4215 case 'illegal_names':
4216 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4217 if ( is_wp_error( $value ) ) {
4218 $error = $value->get_error_message();
4219 } else {
4220 if ( ! is_array( $value ) )
4221 $value = explode( ' ', $value );
4222
4223 $value = array_values( array_filter( array_map( 'trim', $value ) ) );
4224
4225 if ( ! $value )
4226 $value = '';
4227 }
4228 break;
4229
4230 case 'limited_email_domains':
4231 case 'banned_email_domains':
4232 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4233 if ( is_wp_error( $value ) ) {
4234 $error = $value->get_error_message();
4235 } else {
4236 if ( ! is_array( $value ) )
4237 $value = explode( "\n", $value );
4238
4239 $domains = array_values( array_filter( array_map( 'trim', $value ) ) );
4240 $value = array();
4241
4242 foreach ( $domains as $domain ) {
4243 if ( ! preg_match( '/(--|\.\.)/', $domain ) && preg_match( '|^([a-zA-Z0-9-\.])+$|', $domain ) ) {
4244 $value[] = $domain;
4245 }
4246 }
4247 if ( ! $value )
4248 $value = '';
4249 }
4250 break;
4251
4252 case 'timezone_string':
4253 $allowed_zones = timezone_identifiers_list();
4254 if ( ! in_array( $value, $allowed_zones ) && ! empty( $value ) ) {
4255 $error = __( 'The timezone you have entered is not valid. Please select a valid timezone.' );
4256 }
4257 break;
4258
4259 case 'permalink_structure':
4260 case 'category_base':
4261 case 'tag_base':
4262 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4263 if ( is_wp_error( $value ) ) {
4264 $error = $value->get_error_message();
4265 } else {
4266 $value = esc_url_raw( $value );
4267 $value = str_replace( 'http://', '', $value );
4268 }
4269
4270 if ( 'permalink_structure' === $option && '' !== $value && ! preg_match( '/%[^\/%]+%/', $value ) ) {
4271 $error = sprintf(
4272 /* translators: %s: Codex URL */
4273 __( 'A structure tag is required when using custom permalinks. <a href="%s">Learn more</a>' ),
4274 __( 'https://codex.wordpress.org/Using_Permalinks#Choosing_your_permalink_structure' )
4275 );
4276 }
4277 break;
4278
4279 case 'default_role' :
4280 if ( ! get_role( $value ) && get_role( 'subscriber' ) )
4281 $value = 'subscriber';
4282 break;
4283
4284 case 'moderation_keys':
4285 case 'blacklist_keys':
4286 $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4287 if ( is_wp_error( $value ) ) {
4288 $error = $value->get_error_message();
4289 } else {
4290 $value = explode( "\n", $value );
4291 $value = array_filter( array_map( 'trim', $value ) );
4292 $value = array_unique( $value );
4293 $value = implode( "\n", $value );
4294 }
4295 break;
4296 }
4297
4298 if ( ! empty( $error ) ) {
4299 $value = get_option( $option );
4300 if ( function_exists( 'add_settings_error' ) ) {
4301 add_settings_error( $option, "invalid_{$option}", $error );
4302 }
4303 }
4304
4305 /**
4306 * Filters an option value following sanitization.
4307 *
4308 * @since 2.3.0
4309 * @since 4.3.0 Added the `$original_value` parameter.
4310 *
4311 * @param string $value The sanitized option value.
4312 * @param string $option The option name.
4313 * @param string $original_value The original value passed to the function.
4314 */
4315 return apply_filters( "sanitize_option_{$option}", $value, $option, $original_value );
4316}
4317
4318/**
4319 * Maps a function to all non-iterable elements of an array or an object.
4320 *
4321 * This is similar to `array_walk_recursive()` but acts upon objects too.
4322 *
4323 * @since 4.4.0
4324 *
4325 * @param mixed $value The array, object, or scalar.
4326 * @param callable $callback The function to map onto $value.
4327 * @return mixed The value with the callback applied to all non-arrays and non-objects inside it.
4328 */
4329function map_deep( $value, $callback ) {
4330 if ( is_array( $value ) ) {
4331 foreach ( $value as $index => $item ) {
4332 $value[ $index ] = map_deep( $item, $callback );
4333 }
4334 } elseif ( is_object( $value ) ) {
4335 $object_vars = get_object_vars( $value );
4336 foreach ( $object_vars as $property_name => $property_value ) {
4337 $value->$property_name = map_deep( $property_value, $callback );
4338 }
4339 } else {
4340 $value = call_user_func( $callback, $value );
4341 }
4342
4343 return $value;
4344}
4345
4346/**
4347 * Parses a string into variables to be stored in an array.
4348 *
4349 * Uses {@link https://secure.php.net/parse_str parse_str()} and stripslashes if
4350 * {@link https://secure.php.net/magic_quotes magic_quotes_gpc} is on.
4351 *
4352 * @since 2.2.1
4353 *
4354 * @param string $string The string to be parsed.
4355 * @param array $array Variables will be stored in this array.
4356 */
4357function wp_parse_str( $string, &$array ) {
4358 parse_str( $string, $array );
4359 if ( get_magic_quotes_gpc() )
4360 $array = stripslashes_deep( $array );
4361 /**
4362 * Filters the array of variables derived from a parsed string.
4363 *
4364 * @since 2.3.0
4365 *
4366 * @param array $array The array populated with variables.
4367 */
4368 $array = apply_filters( 'wp_parse_str', $array );
4369}
4370
4371/**
4372 * Convert lone less than signs.
4373 *
4374 * KSES already converts lone greater than signs.
4375 *
4376 * @since 2.3.0
4377 *
4378 * @param string $text Text to be converted.
4379 * @return string Converted text.
4380 */
4381function wp_pre_kses_less_than( $text ) {
4382 return preg_replace_callback('%<[^>]*?((?=<)|>|$)%', 'wp_pre_kses_less_than_callback', $text);
4383}
4384
4385/**
4386 * Callback function used by preg_replace.
4387 *
4388 * @since 2.3.0
4389 *
4390 * @param array $matches Populated by matches to preg_replace.
4391 * @return string The text returned after esc_html if needed.
4392 */
4393function wp_pre_kses_less_than_callback( $matches ) {
4394 if ( false === strpos($matches[0], '>') )
4395 return esc_html($matches[0]);
4396 return $matches[0];
4397}
4398
4399/**
4400 * WordPress implementation of PHP sprintf() with filters.
4401 *
4402 * @since 2.5.0
4403 * @link https://secure.php.net/sprintf
4404 *
4405 * @param string $pattern The string which formatted args are inserted.
4406 * @param mixed $args ,... Arguments to be formatted into the $pattern string.
4407 * @return string The formatted string.
4408 */
4409function wp_sprintf( $pattern ) {
4410 $args = func_get_args();
4411 $len = strlen($pattern);
4412 $start = 0;
4413 $result = '';
4414 $arg_index = 0;
4415 while ( $len > $start ) {
4416 // Last character: append and break
4417 if ( strlen($pattern) - 1 == $start ) {
4418 $result .= substr($pattern, -1);
4419 break;
4420 }
4421
4422 // Literal %: append and continue
4423 if ( substr($pattern, $start, 2) == '%%' ) {
4424 $start += 2;
4425 $result .= '%';
4426 continue;
4427 }
4428
4429 // Get fragment before next %
4430 $end = strpos($pattern, '%', $start + 1);
4431 if ( false === $end )
4432 $end = $len;
4433 $fragment = substr($pattern, $start, $end - $start);
4434
4435 // Fragment has a specifier
4436 if ( $pattern[$start] == '%' ) {
4437 // Find numbered arguments or take the next one in order
4438 if ( preg_match('/^%(\d+)\$/', $fragment, $matches) ) {
4439 $arg = isset($args[$matches[1]]) ? $args[$matches[1]] : '';
4440 $fragment = str_replace("%{$matches[1]}$", '%', $fragment);
4441 } else {
4442 ++$arg_index;
4443 $arg = isset($args[$arg_index]) ? $args[$arg_index] : '';
4444 }
4445
4446 /**
4447 * Filters a fragment from the pattern passed to wp_sprintf().
4448 *
4449 * If the fragment is unchanged, then sprintf() will be run on the fragment.
4450 *
4451 * @since 2.5.0
4452 *
4453 * @param string $fragment A fragment from the pattern.
4454 * @param string $arg The argument.
4455 */
4456 $_fragment = apply_filters( 'wp_sprintf', $fragment, $arg );
4457 if ( $_fragment != $fragment )
4458 $fragment = $_fragment;
4459 else
4460 $fragment = sprintf($fragment, strval($arg) );
4461 }
4462
4463 // Append to result and move to next fragment
4464 $result .= $fragment;
4465 $start = $end;
4466 }
4467 return $result;
4468}
4469
4470/**
4471 * Localize list items before the rest of the content.
4472 *
4473 * The '%l' must be at the first characters can then contain the rest of the
4474 * content. The list items will have ', ', ', and', and ' and ' added depending
4475 * on the amount of list items in the $args parameter.
4476 *
4477 * @since 2.5.0
4478 *
4479 * @param string $pattern Content containing '%l' at the beginning.
4480 * @param array $args List items to prepend to the content and replace '%l'.
4481 * @return string Localized list items and rest of the content.
4482 */
4483function wp_sprintf_l( $pattern, $args ) {
4484 // Not a match
4485 if ( substr($pattern, 0, 2) != '%l' )
4486 return $pattern;
4487
4488 // Nothing to work with
4489 if ( empty($args) )
4490 return '';
4491
4492 /**
4493 * Filters the translated delimiters used by wp_sprintf_l().
4494 * Placeholders (%s) are included to assist translators and then
4495 * removed before the array of strings reaches the filter.
4496 *
4497 * Please note: Ampersands and entities should be avoided here.
4498 *
4499 * @since 2.5.0
4500 *
4501 * @param array $delimiters An array of translated delimiters.
4502 */
4503 $l = apply_filters( 'wp_sprintf_l', array(
4504 /* translators: used to join items in a list with more than 2 items */
4505 'between' => sprintf( __('%s, %s'), '', '' ),
4506 /* translators: used to join last two items in a list with more than 2 times */
4507 'between_last_two' => sprintf( __('%s, and %s'), '', '' ),
4508 /* translators: used to join items in a list with only 2 items */
4509 'between_only_two' => sprintf( __('%s and %s'), '', '' ),
4510 ) );
4511
4512 $args = (array) $args;
4513 $result = array_shift($args);
4514 if ( count($args) == 1 )
4515 $result .= $l['between_only_two'] . array_shift($args);
4516 // Loop when more than two args
4517 $i = count($args);
4518 while ( $i ) {
4519 $arg = array_shift($args);
4520 $i--;
4521 if ( 0 == $i )
4522 $result .= $l['between_last_two'] . $arg;
4523 else
4524 $result .= $l['between'] . $arg;
4525 }
4526 return $result . substr($pattern, 2);
4527}
4528
4529/**
4530 * Safely extracts not more than the first $count characters from html string.
4531 *
4532 * UTF-8, tags and entities safe prefix extraction. Entities inside will *NOT*
4533 * be counted as one character. For example & will be counted as 4, < as
4534 * 3, etc.
4535 *
4536 * @since 2.5.0
4537 *
4538 * @param string $str String to get the excerpt from.
4539 * @param int $count Maximum number of characters to take.
4540 * @param string $more Optional. What to append if $str needs to be trimmed. Defaults to empty string.
4541 * @return string The excerpt.
4542 */
4543function wp_html_excerpt( $str, $count, $more = null ) {
4544 if ( null === $more )
4545 $more = '';
4546 $str = wp_strip_all_tags( $str, true );
4547 $excerpt = mb_substr( $str, 0, $count );
4548 // remove part of an entity at the end
4549 $excerpt = preg_replace( '/&[^;\s]{0,6}$/', '', $excerpt );
4550 if ( $str != $excerpt )
4551 $excerpt = trim( $excerpt ) . $more;
4552 return $excerpt;
4553}
4554
4555/**
4556 * Add a Base url to relative links in passed content.
4557 *
4558 * By default it supports the 'src' and 'href' attributes. However this can be
4559 * changed via the 3rd param.
4560 *
4561 * @since 2.7.0
4562 *
4563 * @global string $_links_add_base
4564 *
4565 * @param string $content String to search for links in.
4566 * @param string $base The base URL to prefix to links.
4567 * @param array $attrs The attributes which should be processed.
4568 * @return string The processed content.
4569 */
4570function links_add_base_url( $content, $base, $attrs = array('src', 'href') ) {
4571 global $_links_add_base;
4572 $_links_add_base = $base;
4573 $attrs = implode('|', (array)$attrs);
4574 return preg_replace_callback( "!($attrs)=(['\"])(.+?)\\2!i", '_links_add_base', $content );
4575}
4576
4577/**
4578 * Callback to add a base url to relative links in passed content.
4579 *
4580 * @since 2.7.0
4581 * @access private
4582 *
4583 * @global string $_links_add_base
4584 *
4585 * @param string $m The matched link.
4586 * @return string The processed link.
4587 */
4588function _links_add_base( $m ) {
4589 global $_links_add_base;
4590 //1 = attribute name 2 = quotation mark 3 = URL
4591 return $m[1] . '=' . $m[2] .
4592 ( preg_match( '#^(\w{1,20}):#', $m[3], $protocol ) && in_array( $protocol[1], wp_allowed_protocols() ) ?
4593 $m[3] :
4594 WP_Http::make_absolute_url( $m[3], $_links_add_base )
4595 )
4596 . $m[2];
4597}
4598
4599/**
4600 * Adds a Target attribute to all links in passed content.
4601 *
4602 * This function by default only applies to `<a>` tags, however this can be
4603 * modified by the 3rd param.
4604 *
4605 * *NOTE:* Any current target attributed will be stripped and replaced.
4606 *
4607 * @since 2.7.0
4608 *
4609 * @global string $_links_add_target
4610 *
4611 * @param string $content String to search for links in.
4612 * @param string $target The Target to add to the links.
4613 * @param array $tags An array of tags to apply to.
4614 * @return string The processed content.
4615 */
4616function links_add_target( $content, $target = '_blank', $tags = array('a') ) {
4617 global $_links_add_target;
4618 $_links_add_target = $target;
4619 $tags = implode('|', (array)$tags);
4620 return preg_replace_callback( "!<($tags)([^>]*)>!i", '_links_add_target', $content );
4621}
4622
4623/**
4624 * Callback to add a target attribute to all links in passed content.
4625 *
4626 * @since 2.7.0
4627 * @access private
4628 *
4629 * @global string $_links_add_target
4630 *
4631 * @param string $m The matched link.
4632 * @return string The processed link.
4633 */
4634function _links_add_target( $m ) {
4635 global $_links_add_target;
4636 $tag = $m[1];
4637 $link = preg_replace('|( target=([\'"])(.*?)\2)|i', '', $m[2]);
4638 return '<' . $tag . $link . ' target="' . esc_attr( $_links_add_target ) . '">';
4639}
4640
4641/**
4642 * Normalize EOL characters and strip duplicate whitespace.
4643 *
4644 * @since 2.7.0
4645 *
4646 * @param string $str The string to normalize.
4647 * @return string The normalized string.
4648 */
4649function normalize_whitespace( $str ) {
4650 $str = trim( $str );
4651 $str = str_replace( "\r", "\n", $str );
4652 $str = preg_replace( array( '/\n+/', '/[ \t]+/' ), array( "\n", ' ' ), $str );
4653 return $str;
4654}
4655
4656/**
4657 * Properly strip all HTML tags including script and style
4658 *
4659 * This differs from strip_tags() because it removes the contents of
4660 * the `<script>` and `<style>` tags. E.g. `strip_tags( '<script>something</script>' )`
4661 * will return 'something'. wp_strip_all_tags will return ''
4662 *
4663 * @since 2.9.0
4664 *
4665 * @param string $string String containing HTML tags
4666 * @param bool $remove_breaks Optional. Whether to remove left over line breaks and white space chars
4667 * @return string The processed string.
4668 */
4669function wp_strip_all_tags($string, $remove_breaks = false) {
4670 $string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string );
4671 $string = strip_tags($string);
4672
4673 if ( $remove_breaks )
4674 $string = preg_replace('/[\r\n\t ]+/', ' ', $string);
4675
4676 return trim( $string );
4677}
4678
4679/**
4680 * Sanitizes a string from user input or from the database.
4681 *
4682 * - Checks for invalid UTF-8,
4683 * - Converts single `<` characters to entities
4684 * - Strips all tags
4685 * - Removes line breaks, tabs, and extra whitespace
4686 * - Strips octets
4687 *
4688 * @since 2.9.0
4689 *
4690 * @see sanitize_textarea_field()
4691 * @see wp_check_invalid_utf8()
4692 * @see wp_strip_all_tags()
4693 *
4694 * @param string $str String to sanitize.
4695 * @return string Sanitized string.
4696 */
4697function sanitize_text_field( $str ) {
4698 $filtered = _sanitize_text_fields( $str, false );
4699
4700 /**
4701 * Filters a sanitized text field string.
4702 *
4703 * @since 2.9.0
4704 *
4705 * @param string $filtered The sanitized string.
4706 * @param string $str The string prior to being sanitized.
4707 */
4708 return apply_filters( 'sanitize_text_field', $filtered, $str );
4709}
4710
4711/**
4712 * Sanitizes a multiline string from user input or from the database.
4713 *
4714 * The function is like sanitize_text_field(), but preserves
4715 * new lines (\n) and other whitespace, which are legitimate
4716 * input in textarea elements.
4717 *
4718 * @see sanitize_text_field()
4719 *
4720 * @since 4.7.0
4721 *
4722 * @param string $str String to sanitize.
4723 * @return string Sanitized string.
4724 */
4725function sanitize_textarea_field( $str ) {
4726 $filtered = _sanitize_text_fields( $str, true );
4727
4728 /**
4729 * Filters a sanitized textarea field string.
4730 *
4731 * @since 4.7.0
4732 *
4733 * @param string $filtered The sanitized string.
4734 * @param string $str The string prior to being sanitized.
4735 */
4736 return apply_filters( 'sanitize_textarea_field', $filtered, $str );
4737}
4738
4739/**
4740 * Internal helper function to sanitize a string from user input or from the db
4741 *
4742 * @since 4.7.0
4743 * @access private
4744 *
4745 * @param string $str String to sanitize.
4746 * @param bool $keep_newlines optional Whether to keep newlines. Default: false.
4747 * @return string Sanitized string.
4748 */
4749function _sanitize_text_fields( $str, $keep_newlines = false ) {
4750 $filtered = wp_check_invalid_utf8( $str );
4751
4752 if ( strpos($filtered, '<') !== false ) {
4753 $filtered = wp_pre_kses_less_than( $filtered );
4754 // This will strip extra whitespace for us.
4755 $filtered = wp_strip_all_tags( $filtered, false );
4756
4757 // Use html entities in a special case to make sure no later
4758 // newline stripping stage could lead to a functional tag
4759 $filtered = str_replace("<\n", "<\n", $filtered);
4760 }
4761
4762 if ( ! $keep_newlines ) {
4763 $filtered = preg_replace( '/[\r\n\t ]+/', ' ', $filtered );
4764 }
4765 $filtered = trim( $filtered );
4766
4767 $found = false;
4768 while ( preg_match('/%[a-f0-9]{2}/i', $filtered, $match) ) {
4769 $filtered = str_replace($match[0], '', $filtered);
4770 $found = true;
4771 }
4772
4773 if ( $found ) {
4774 // Strip out the whitespace that may now exist after removing the octets.
4775 $filtered = trim( preg_replace('/ +/', ' ', $filtered) );
4776 }
4777
4778 return $filtered;
4779}
4780
4781/**
4782 * i18n friendly version of basename()
4783 *
4784 * @since 3.1.0
4785 *
4786 * @param string $path A path.
4787 * @param string $suffix If the filename ends in suffix this will also be cut off.
4788 * @return string
4789 */
4790function wp_basename( $path, $suffix = '' ) {
4791 return urldecode( basename( str_replace( array( '%2F', '%5C' ), '/', urlencode( $path ) ), $suffix ) );
4792}
4793
4794/**
4795 * Forever eliminate "Wordpress" from the planet (or at least the little bit we can influence).
4796 *
4797 * Violating our coding standards for a good function name.
4798 *
4799 * @since 3.0.0
4800 *
4801 * @staticvar string|false $dblq
4802 *
4803 * @param string $text The text to be modified.
4804 * @return string The modified text.
4805 */
4806function capital_P_dangit( $text ) {
4807 // Simple replacement for titles
4808 $current_filter = current_filter();
4809 if ( 'the_title' === $current_filter || 'wp_title' === $current_filter )
4810 return str_replace( 'Wordpress', 'WordPress', $text );
4811 // Still here? Use the more judicious replacement
4812 static $dblq = false;
4813 if ( false === $dblq ) {
4814 $dblq = _x( '“', 'opening curly double quote' );
4815 }
4816 return str_replace(
4817 array( ' Wordpress', '‘Wordpress', $dblq . 'Wordpress', '>Wordpress', '(Wordpress' ),
4818 array( ' WordPress', '‘WordPress', $dblq . 'WordPress', '>WordPress', '(WordPress' ),
4819 $text );
4820}
4821
4822/**
4823 * Sanitize a mime type
4824 *
4825 * @since 3.1.3
4826 *
4827 * @param string $mime_type Mime type
4828 * @return string Sanitized mime type
4829 */
4830function sanitize_mime_type( $mime_type ) {
4831 $sani_mime_type = preg_replace( '/[^-+*.a-zA-Z0-9\/]/', '', $mime_type );
4832 /**
4833 * Filters a mime type following sanitization.
4834 *
4835 * @since 3.1.3
4836 *
4837 * @param string $sani_mime_type The sanitized mime type.
4838 * @param string $mime_type The mime type prior to sanitization.
4839 */
4840 return apply_filters( 'sanitize_mime_type', $sani_mime_type, $mime_type );
4841}
4842
4843/**
4844 * Sanitize space or carriage return separated URLs that are used to send trackbacks.
4845 *
4846 * @since 3.4.0
4847 *
4848 * @param string $to_ping Space or carriage return separated URLs
4849 * @return string URLs starting with the http or https protocol, separated by a carriage return.
4850 */
4851function sanitize_trackback_urls( $to_ping ) {
4852 $urls_to_ping = preg_split( '/[\r\n\t ]/', trim( $to_ping ), -1, PREG_SPLIT_NO_EMPTY );
4853 foreach ( $urls_to_ping as $k => $url ) {
4854 if ( !preg_match( '#^https?://.#i', $url ) )
4855 unset( $urls_to_ping[$k] );
4856 }
4857 $urls_to_ping = array_map( 'esc_url_raw', $urls_to_ping );
4858 $urls_to_ping = implode( "\n", $urls_to_ping );
4859 /**
4860 * Filters a list of trackback URLs following sanitization.
4861 *
4862 * The string returned here consists of a space or carriage return-delimited list
4863 * of trackback URLs.
4864 *
4865 * @since 3.4.0
4866 *
4867 * @param string $urls_to_ping Sanitized space or carriage return separated URLs.
4868 * @param string $to_ping Space or carriage return separated URLs before sanitization.
4869 */
4870 return apply_filters( 'sanitize_trackback_urls', $urls_to_ping, $to_ping );
4871}
4872
4873/**
4874 * Add slashes to a string or array of strings.
4875 *
4876 * This should be used when preparing data for core API that expects slashed data.
4877 * This should not be used to escape data going directly into an SQL query.
4878 *
4879 * @since 3.6.0
4880 *
4881 * @param string|array $value String or array of strings to slash.
4882 * @return string|array Slashed $value
4883 */
4884function wp_slash( $value ) {
4885 if ( is_array( $value ) ) {
4886 foreach ( $value as $k => $v ) {
4887 if ( is_array( $v ) ) {
4888 $value[$k] = wp_slash( $v );
4889 } else {
4890 $value[$k] = addslashes( $v );
4891 }
4892 }
4893 } else {
4894 $value = addslashes( $value );
4895 }
4896
4897 return $value;
4898}
4899
4900/**
4901 * Remove slashes from a string or array of strings.
4902 *
4903 * This should be used to remove slashes from data passed to core API that
4904 * expects data to be unslashed.
4905 *
4906 * @since 3.6.0
4907 *
4908 * @param string|array $value String or array of strings to unslash.
4909 * @return string|array Unslashed $value
4910 */
4911function wp_unslash( $value ) {
4912 return stripslashes_deep( $value );
4913}
4914
4915/**
4916 * Extract and return the first URL from passed content.
4917 *
4918 * @since 3.6.0
4919 *
4920 * @param string $content A string which might contain a URL.
4921 * @return string|false The found URL.
4922 */
4923function get_url_in_content( $content ) {
4924 if ( empty( $content ) ) {
4925 return false;
4926 }
4927
4928 if ( preg_match( '/<a\s[^>]*?href=([\'"])(.+?)\1/is', $content, $matches ) ) {
4929 return esc_url_raw( $matches[2] );
4930 }
4931
4932 return false;
4933}
4934
4935/**
4936 * Returns the regexp for common whitespace characters.
4937 *
4938 * By default, spaces include new lines, tabs, nbsp entities, and the UTF-8 nbsp.
4939 * This is designed to replace the PCRE \s sequence. In ticket #22692, that
4940 * sequence was found to be unreliable due to random inclusion of the A0 byte.
4941 *
4942 * @since 4.0.0
4943 *
4944 * @staticvar string $spaces
4945 *
4946 * @return string The spaces regexp.
4947 */
4948function wp_spaces_regexp() {
4949 static $spaces = '';
4950
4951 if ( empty( $spaces ) ) {
4952 /**
4953 * Filters the regexp for common whitespace characters.
4954 *
4955 * This string is substituted for the \s sequence as needed in regular
4956 * expressions. For websites not written in English, different characters
4957 * may represent whitespace. For websites not encoded in UTF-8, the 0xC2 0xA0
4958 * sequence may not be in use.
4959 *
4960 * @since 4.0.0
4961 *
4962 * @param string $spaces Regexp pattern for matching common whitespace characters.
4963 */
4964 $spaces = apply_filters( 'wp_spaces_regexp', '[\r\n\t ]|\xC2\xA0| ' );
4965 }
4966
4967 return $spaces;
4968}
4969
4970/**
4971 * Print the important emoji-related styles.
4972 *
4973 * @since 4.2.0
4974 *
4975 * @staticvar bool $printed
4976 */
4977function print_emoji_styles() {
4978 static $printed = false;
4979
4980 if ( $printed ) {
4981 return;
4982 }
4983
4984 $printed = true;
4985?>
4986<style type="text/css">
4987img.wp-smiley,
4988img.emoji {
4989 display: inline !important;
4990 border: none !important;
4991 box-shadow: none !important;
4992 height: 1em !important;
4993 width: 1em !important;
4994 margin: 0 .07em !important;
4995 vertical-align: -0.1em !important;
4996 background: none !important;
4997 padding: 0 !important;
4998}
4999</style>
5000<?php
5001}
5002
5003/**
5004 * Print the inline Emoji detection script if it is not already printed.
5005 *
5006 * @since 4.2.0
5007 * @staticvar bool $printed
5008 */
5009function print_emoji_detection_script() {
5010 static $printed = false;
5011
5012 if ( $printed ) {
5013 return;
5014 }
5015
5016 $printed = true;
5017
5018 _print_emoji_detection_script();
5019}
5020
5021/**
5022 * Prints inline Emoji dection script
5023 *
5024 * @ignore
5025 * @since 4.6.0
5026 * @access private
5027 */
5028function _print_emoji_detection_script() {
5029 $settings = array(
5030 /**
5031 * Filters the URL where emoji png images are hosted.
5032 *
5033 * @since 4.2.0
5034 *
5035 * @param string The emoji base URL for png images.
5036 */
5037 'baseUrl' => apply_filters( 'emoji_url', 'https://s.w.org/images/core/emoji/11/72x72/' ),
5038
5039 /**
5040 * Filters the extension of the emoji png files.
5041 *
5042 * @since 4.2.0
5043 *
5044 * @param string The emoji extension for png files. Default .png.
5045 */
5046 'ext' => apply_filters( 'emoji_ext', '.png' ),
5047
5048 /**
5049 * Filters the URL where emoji SVG images are hosted.
5050 *
5051 * @since 4.6.0
5052 *
5053 * @param string The emoji base URL for svg images.
5054 */
5055 'svgUrl' => apply_filters( 'emoji_svg_url', 'https://s.w.org/images/core/emoji/11/svg/' ),
5056
5057 /**
5058 * Filters the extension of the emoji SVG files.
5059 *
5060 * @since 4.6.0
5061 *
5062 * @param string The emoji extension for svg files. Default .svg.
5063 */
5064 'svgExt' => apply_filters( 'emoji_svg_ext', '.svg' ),
5065 );
5066
5067 $version = 'ver=' . get_bloginfo( 'version' );
5068
5069 if ( SCRIPT_DEBUG ) {
5070 $settings['source'] = array(
5071 /** This filter is documented in wp-includes/class.wp-scripts.php */
5072 'wpemoji' => apply_filters( 'script_loader_src', includes_url( "js/wp-emoji.js?$version" ), 'wpemoji' ),
5073 /** This filter is documented in wp-includes/class.wp-scripts.php */
5074 'twemoji' => apply_filters( 'script_loader_src', includes_url( "js/twemoji.js?$version" ), 'twemoji' ),
5075 );
5076
5077 ?>
5078 <script type="text/javascript">
5079 window._wpemojiSettings = <?php echo wp_json_encode( $settings ); ?>;
5080 <?php readfile( ABSPATH . WPINC . "/js/wp-emoji-loader.js" ); ?>
5081 </script>
5082 <?php
5083 } else {
5084 $settings['source'] = array(
5085 /** This filter is documented in wp-includes/class.wp-scripts.php */
5086 'concatemoji' => apply_filters( 'script_loader_src', includes_url( "js/wp-emoji-release.min.js?$version" ), 'concatemoji' ),
5087 );
5088
5089 /*
5090 * If you're looking at a src version of this file, you'll see an "include"
5091 * statement below. This is used by the `grunt build` process to directly
5092 * include a minified version of wp-emoji-loader.js, instead of using the
5093 * readfile() method from above.
5094 *
5095 * If you're looking at a build version of this file, you'll see a string of
5096 * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
5097 * and edit wp-emoji-loader.js directly.
5098 */
5099 ?>
5100 <script type="text/javascript">
5101 window._wpemojiSettings = <?php echo wp_json_encode( $settings ); ?>;
5102 !function(a,b,c){function d(a,b){var c=String.fromCharCode;l.clearRect(0,0,k.width,k.height),l.fillText(c.apply(this,a),0,0);var d=k.toDataURL();l.clearRect(0,0,k.width,k.height),l.fillText(c.apply(this,b),0,0);var e=k.toDataURL();return d===e}function e(a){var b;if(!l||!l.fillText)return!1;switch(l.textBaseline="top",l.font="600 32px Arial",a){case"flag":return!(b=d([55356,56826,55356,56819],[55356,56826,8203,55356,56819]))&&(b=d([55356,57332,56128,56423,56128,56418,56128,56421,56128,56430,56128,56423,56128,56447],[55356,57332,8203,56128,56423,8203,56128,56418,8203,56128,56421,8203,56128,56430,8203,56128,56423,8203,56128,56447]),!b);case"emoji":return b=d([55358,56760,9792,65039],[55358,56760,8203,9792,65039]),!b}return!1}function f(a){var c=b.createElement("script");c.src=a,c.defer=c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var g,h,i,j,k=b.createElement("canvas"),l=k.getContext&&k.getContext("2d");for(j=Array("flag","emoji"),c.supports={everything:!0,everythingExceptFlag:!0},i=0;i<j.length;i++)c.supports[j[i]]=e(j[i]),c.supports.everything=c.supports.everything&&c.supports[j[i]],"flag"!==j[i]&&(c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&c.supports[j[i]]);c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&!c.supports.flag,c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.everything||(h=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",h,!1),a.addEventListener("load",h,!1)):(a.attachEvent("onload",h),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),g=c.source||{},g.concatemoji?f(g.concatemoji):g.wpemoji&&g.twemoji&&(f(g.twemoji),f(g.wpemoji)))}(window,document,window._wpemojiSettings);
5103 </script>
5104 <?php
5105 }
5106}
5107
5108/**
5109 * Convert emoji characters to their equivalent HTML entity.
5110 *
5111 * This allows us to store emoji in a DB using the utf8 character set.
5112 *
5113 * @since 4.2.0
5114 *
5115 * @param string $content The content to encode.
5116 * @return string The encoded content.
5117 */
5118function wp_encode_emoji( $content ) {
5119 $emoji = _wp_emoji_list( 'partials' );
5120
5121 foreach ( $emoji as $emojum ) {
5122 if ( version_compare( phpversion(), '5.4', '<' ) ) {
5123 $emoji_char = html_entity_decode( $emojum, ENT_COMPAT, 'UTF-8' );
5124 } else {
5125 $emoji_char = html_entity_decode( $emojum );
5126 }
5127 if ( false !== strpos( $content, $emoji_char ) ) {
5128 $content = preg_replace( "/$emoji_char/", $emojum, $content );
5129 }
5130 }
5131
5132 return $content;
5133}
5134
5135/**
5136 * Convert emoji to a static img element.
5137 *
5138 * @since 4.2.0
5139 *
5140 * @param string $text The content to encode.
5141 * @return string The encoded content.
5142 */
5143function wp_staticize_emoji( $text ) {
5144 if ( false === strpos( $text, '&#x' ) ) {
5145 if ( ( function_exists( 'mb_check_encoding' ) && mb_check_encoding( $text, 'ASCII' ) ) || ! preg_match( '/[^\x00-\x7F]/', $text ) ) {
5146 // The text doesn't contain anything that might be emoji, so we can return early.
5147 return $text;
5148 } else {
5149 $encoded_text = wp_encode_emoji( $text );
5150 if ( $encoded_text === $text ) {
5151 return $encoded_text;
5152 }
5153
5154 $text = $encoded_text;
5155 }
5156 }
5157
5158 $emoji = _wp_emoji_list( 'entities' );
5159
5160 // Quickly narrow down the list of emoji that might be in the text and need replacing.
5161 $possible_emoji = array();
5162 foreach( $emoji as $emojum ) {
5163 if ( false !== strpos( $text, $emojum ) ) {
5164 if ( version_compare( phpversion(), '5.4', '<' ) ) {
5165 $possible_emoji[ $emojum ] = html_entity_decode( $emojum, ENT_COMPAT, 'UTF-8' );
5166 } else {
5167 $possible_emoji[ $emojum ] = html_entity_decode( $emojum );
5168 }
5169 }
5170 }
5171
5172 if ( ! $possible_emoji ) {
5173 return $text;
5174 }
5175
5176 /** This filter is documented in wp-includes/formatting.php */
5177 $cdn_url = apply_filters( 'emoji_url', 'https://s.w.org/images/core/emoji/11/72x72/' );
5178
5179 /** This filter is documented in wp-includes/formatting.php */
5180 $ext = apply_filters( 'emoji_ext', '.png' );
5181
5182 $output = '';
5183 /*
5184 * HTML loop taken from smiley function, which was taken from texturize function.
5185 * It'll never be consolidated.
5186 *
5187 * First, capture the tags as well as in between.
5188 */
5189 $textarr = preg_split( '/(<.*>)/U', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
5190 $stop = count( $textarr );
5191
5192 // Ignore processing of specific tags.
5193 $tags_to_ignore = 'code|pre|style|script|textarea';
5194 $ignore_block_element = '';
5195
5196 for ( $i = 0; $i < $stop; $i++ ) {
5197 $content = $textarr[$i];
5198
5199 // If we're in an ignore block, wait until we find its closing tag.
5200 if ( '' == $ignore_block_element && preg_match( '/^<(' . $tags_to_ignore . ')>/', $content, $matches ) ) {
5201 $ignore_block_element = $matches[1];
5202 }
5203
5204 // If it's not a tag and not in ignore block.
5205 if ( '' == $ignore_block_element && strlen( $content ) > 0 && '<' != $content[0] && false !== strpos( $content, '&#x' ) ) {
5206 foreach ( $possible_emoji as $emojum => $emoji_char ) {
5207 if ( false === strpos( $content, $emojum ) ) {
5208 continue;
5209 }
5210
5211 $file = str_replace( ';&#x', '-', $emojum );
5212 $file = str_replace( array( '&#x', ';'), '', $file );
5213
5214 $entity = sprintf( '<img src="%s" alt="%s" class="wp-smiley" style="height: 1em; max-height: 1em;" />', $cdn_url . $file . $ext, $emoji_char );
5215
5216 $content = str_replace( $emojum, $entity, $content );
5217 }
5218 }
5219
5220 // Did we exit ignore block.
5221 if ( '' != $ignore_block_element && '</' . $ignore_block_element . '>' == $content ) {
5222 $ignore_block_element = '';
5223 }
5224
5225 $output .= $content;
5226 }
5227
5228 // Finally, remove any stray U+FE0F characters
5229 $output = str_replace( '️', '', $output );
5230
5231 return $output;
5232}
5233
5234/**
5235 * Convert emoji in emails into static images.
5236 *
5237 * @since 4.2.0
5238 *
5239 * @param array $mail The email data array.
5240 * @return array The email data array, with emoji in the message staticized.
5241 */
5242function wp_staticize_emoji_for_email( $mail ) {
5243 if ( ! isset( $mail['message'] ) ) {
5244 return $mail;
5245 }
5246
5247 /*
5248 * We can only transform the emoji into images if it's a text/html email.
5249 * To do that, here's a cut down version of the same process that happens
5250 * in wp_mail() - get the Content-Type from the headers, if there is one,
5251 * then pass it through the wp_mail_content_type filter, in case a plugin
5252 * is handling changing the Content-Type.
5253 */
5254 $headers = array();
5255 if ( isset( $mail['headers'] ) ) {
5256 if ( is_array( $mail['headers'] ) ) {
5257 $headers = $mail['headers'];
5258 } else {
5259 $headers = explode( "\n", str_replace( "\r\n", "\n", $mail['headers'] ) );
5260 }
5261 }
5262
5263 foreach ( $headers as $header ) {
5264 if ( strpos($header, ':') === false ) {
5265 continue;
5266 }
5267
5268 // Explode them out.
5269 list( $name, $content ) = explode( ':', trim( $header ), 2 );
5270
5271 // Cleanup crew.
5272 $name = trim( $name );
5273 $content = trim( $content );
5274
5275 if ( 'content-type' === strtolower( $name ) ) {
5276 if ( strpos( $content, ';' ) !== false ) {
5277 list( $type, $charset ) = explode( ';', $content );
5278 $content_type = trim( $type );
5279 } else {
5280 $content_type = trim( $content );
5281 }
5282 break;
5283 }
5284 }
5285
5286 // Set Content-Type if we don't have a content-type from the input headers.
5287 if ( ! isset( $content_type ) ) {
5288 $content_type = 'text/plain';
5289 }
5290
5291 /** This filter is documented in wp-includes/pluggable.php */
5292 $content_type = apply_filters( 'wp_mail_content_type', $content_type );
5293
5294 if ( 'text/html' === $content_type ) {
5295 $mail['message'] = wp_staticize_emoji( $mail['message'] );
5296 }
5297
5298 return $mail;
5299}
5300
5301/**
5302 * Returns a arrays of emoji data.
5303 *
5304 * These arrays automatically built from the regex in twemoji.js - if they need to be updated,
5305 * you should update the regex there, then run the `grunt precommit:emoji` job.
5306 *
5307 * @since 4.9.0
5308 * @access private
5309 *
5310 * @param string $type Optional. Which array type to return. Accepts 'partials' or 'entities', default 'entities'.
5311 * @return array An array to match all emoji that WordPress recognises.
5312 */
5313function _wp_emoji_list( $type = 'entities' ) {
5314 // Do not remove the START/END comments - they're used to find where to insert the arrays.
5315
5316 // START: emoji arrays
5317 $entities = array('👩‍❤️‍💋‍👩','👩‍❤️‍💋‍👨','👨‍❤️‍💋‍👨','🏴󠁧󠁢󠁳󠁣󠁴󠁿','🏴󠁧󠁢󠁷󠁬󠁳󠁿','🏴󠁧󠁢󠁥󠁮󠁧󠁿','👩‍👩‍👧‍👦','👨‍👨‍👦‍👦','👩‍👩‍👦‍👦','👨‍👨‍👧‍👦','👨‍👨‍👧‍👧','👨‍👩‍👧‍👧','👨‍👩‍👦‍👦','👩‍👩‍👧‍👧','👨‍👩‍👧‍👦','👨‍❤️‍👨','👩‍❤️‍👨','👩‍❤️‍👩','👩‍👩‍👦','👩‍👦‍👦','👩‍👧‍👦','👩‍👧‍👧','👨‍👨‍👦','👨‍👩‍👧','👨‍👧‍👧','👨‍👧‍👦','👩‍👩‍👧','👨‍👩‍👦','👨‍👨‍👧','👨‍👦‍👦','🤵🏿‍♀️','🏋🏻‍♂️','🏋🏼‍♀️','🏋🏼‍♂️','🏋🏽‍♀️','🏋🏽‍♂️','🏋🏾‍♀️','🏋🏾‍♂️','🏋🏿‍♀️','🏋🏿‍♂️','🏌🏻‍♀️','🏌🏻‍♂️','🏌🏼‍♀️','🏌🏼‍♂️','🏌🏽‍♀️','🏌🏽‍♂️','🏌🏾‍♀️','🏌🏾‍♂️','🏌🏿‍♀️','🏌🏿‍♂️','💂🏻‍♀️','🏃🏼‍♀️','🏃🏼‍♂️','🧝🏿‍♂️','🧝🏿‍♀️','🧝🏾‍♂️','🧝🏾‍♀️','🧝🏽‍♂️','🧝🏽‍♀️','🧝🏼‍♂️','🧝🏼‍♀️','🧝🏻‍♂️','🧝🏻‍♀️','🧜🏿‍♂️','🧜🏿‍♀️','🧜🏾‍♂️','🧜🏾‍♀️','🧜🏽‍♂️','🧜🏽‍♀️','🧜🏼‍♂️','🧜🏼‍♀️','👨🏻‍⚕️','👨🏻‍⚖️','👨🏻‍✈️','🧜🏻‍♂️','🧜🏻‍♀️','🧛🏿‍♂️','🧛🏿‍♀️','🧛🏾‍♂️','🧛🏾‍♀️','🧛🏽‍♂️','🧛🏽‍♀️','🧛🏼‍♂️','🧛🏼‍♀️','🧛🏻‍♂️','🧛🏻‍♀️','🧚🏿‍♂️','🧚🏿‍♀️','🧚🏾‍♂️','🧚🏾‍♀️','🧚🏽‍♂️','👨🏼‍⚕️','👨🏼‍⚖️','👨🏼‍✈️','🧚🏽‍♀️','🧚🏼‍♂️','🧚🏼‍♀️','🧚🏻‍♂️','🧚🏻‍♀️','🧙🏿‍♂️','🧙🏿‍♀️','🧙🏾‍♂️','🧙🏾‍♀️','🧙🏽‍♂️','🧙🏽‍♀️','🧙🏼‍♂️','🧙🏼‍♀️','🧙🏻‍♂️','🧙🏻‍♀️','🧘🏿‍♂️','🧘🏿‍♀️','👨🏽‍⚕️','👨🏽‍⚖️','👨🏽‍✈️','🧘🏾‍♂️','🧘🏾‍♀️','🧘🏽‍♂️','🧘🏽‍♀️','🧘🏼‍♂️','🧘🏼‍♀️','🧘🏻‍♂️','🧘🏻‍♀️','🧗🏿‍♂️','🧗🏿‍♀️','🧗🏾‍♂️','🧗🏾‍♀️','🧗🏽‍♂️','🧗🏽‍♀️','🧗🏼‍♂️','🧗🏼‍♀️','🧗🏻‍♂️','👨🏾‍⚕️','👨🏾‍⚖️','👨🏾‍✈️','🧗🏻‍♀️','🧖🏿‍♂️','🧖🏿‍♀️','🧖🏾‍♂️','🧖🏾‍♀️','🧖🏽‍♂️','🧖🏽‍♀️','🧖🏼‍♂️','🧖🏼‍♀️','🧖🏻‍♂️','🧖🏻‍♀️','🦹🏿‍♂️','🦹🏿‍♀️','🦹🏾‍♂️','🦹🏾‍♀️','🦹🏽‍♂️','🦹🏽‍♀️','👨🏿‍⚕️','👨🏿‍⚖️','👨🏿‍✈️','🦹🏼‍♂️','🦹🏼‍♀️','🦹🏻‍♂️','🦹🏻‍♀️','🦸🏿‍♂️','🦸🏿‍♀️','🦸🏾‍♂️','🏃🏽‍&\D0y\B7\00\00\00\00\D0y\B7\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00p+\B6\00\00\00\00\00\00\00\00\00\00\00\00@)\B6\00\00\00\008z\B7\00\00\00\00\00\00\00\00\00\00\00\00\F0y\B7\00\00\00\00\00@\00\00\00\00\00\00\F0y\B7\00\00\00\00\00\00\00\00\00\00\00\00;','🏃🏾‍♀️','🦸🏽‍♂️','🏃🏾‍♂️','🏃🏿‍♀️','🏃🏿‍♂️','🏄🏻‍♀️','🏄🏻‍♂️','🏄🏼‍♀️','🏄🏼‍♂️','🏄🏽‍♀️','🏄🏽‍♂️','🏄🏾‍♀️','🦸🏽‍♀️','🦸🏼‍♂️','🦸🏼‍♀️','🦸🏻‍♂️','🦸🏻‍♀️','🤾🏿‍♂️','🤾🏿‍♀️','🤾🏾‍♂️','🤾🏾‍♀️','🤾🏽‍♂️','🤾🏽‍♀️','🤾🏼‍♂️','🤾🏼‍♀️','🏄🏾‍♂️','🏄🏿‍♀️','🤾🏻‍♂️','🤾🏻‍♀️','🤽🏿‍♂️','🤽🏿‍♀️','🤽🏾‍♂️','🤽🏾‍♀️','🤽🏽‍♂️','🤽🏽‍♀️','🤽🏼‍♂️','🤽🏼‍♀️','🤽🏻‍♂️','🤽🏻‍♀️','🤹🏿‍♂️','🤹🏿‍♀️','🤹🏾‍♂️','🤹🏾‍♀️','🤹🏽‍♂️','👩🏻‍⚕️','👩🏻‍⚖️','👩🏻‍✈️','🤹🏽‍♀️','🤹🏼‍♂️','🤹🏼‍♀️','🤹🏻‍♂️','🤹🏻‍♀️','🤸🏿‍♂️','🤸🏿‍♀️','🤸🏾‍♂️','🤸🏾‍♀️','🤸🏽‍♂️','🤸🏽‍♀️','🤸🏼‍♂️','🤸🏼‍♀️','🤸🏻‍♂️','🤸🏻‍♀️','🤷🏿‍♂️','🤷🏿‍♀️','👩🏼‍⚕️','👩🏼‍⚖️','👩🏼‍✈️','🤷🏾‍♂️','🤷🏾‍♀️','🤷🏽‍♂️','🤷🏽‍♀️','🤷🏼‍♂️','🤷🏼‍♀️','🤷🏻‍♂️','🤷🏻‍♀️','🤵🏿‍♂️','🏃🏻‍♂️','🤵🏾‍♂️','🤵🏾‍♀️','🤵🏽‍♂️','🤵🏽‍♀️','🤵🏼‍♂️','🤵🏼‍♀️','🤵🏻‍♂️','👩🏽‍⚕️','👩🏽‍⚖️','👩🏽‍✈️','🤵🏻‍♀️','🤦🏿‍♂️','🤦🏿‍♀️','🤦🏾‍♂️','🤦🏾‍♀️','🤦🏽‍♂️','🤦🏽‍♀️','🤦🏼‍♂️','🤦🏼‍♀️','🤦🏻‍♂️','🤦🏻‍♀️','🚶🏿‍♂️','🚶🏿‍♀️','🚶🏾‍♂️','🚶🏾‍♀️','🚶🏽‍♂️','🚶🏽‍♀️','👩🏾‍⚕️','👩🏾‍⚖️','👩🏾‍✈️','🚶🏼‍♂️','🚶🏼‍♀️','🚶🏻‍♂️','🚶🏻‍♀️','🚵🏿‍♂️','🚵🏿‍♀️','🚵🏾‍♂️','🚵🏾‍♀️','🚵🏽‍♂️','🚵🏽‍♀️','🚵🏼‍♂️','🚵🏼‍♀️','🚵🏻‍♂️','🚵🏻‍♀️','🚴🏿‍♂️','🚴🏿‍♀️','🚴🏾‍♂️','👩🏿‍⚕️','👩🏿‍⚖️','👩🏿‍✈️','🚴🏾‍♀️','🚴🏽‍♂️','🚴🏽‍♀️','🚴🏼‍♂️','🚴🏼‍♀️','🚴🏻‍♂️','🚴🏻‍♀️','🏄🏿‍♂️','🚣🏿‍♂️','🏊🏻‍♀️','🏊🏻‍♂️','🚣🏿‍♀️','🏊🏼‍♀️','🏊🏼‍♂️','🏊🏽‍♀️','🏊🏽‍♂️','🏊🏾‍♀️','🚣🏾‍♂️','🚣🏾‍♀️','🚣🏽‍♂️','🚣🏽‍♀️','🚣🏼‍♂️','🚣🏼‍♀️','🚣🏻‍♂️','🚣🏻‍♀️','🙎🏿‍♂️','🙎🏿‍♀️','🙎🏾‍♂️','🙎🏾‍♀️','🙎🏽‍♂️','🏊🏾‍♂️','🏊🏿‍♀️','🏊🏿‍♂️','🏋🏻‍♀️','👮🏻‍♀️','👮🏻‍♂️','👮🏼‍♀️','👮🏼‍♂️','👮🏽‍♀️','👮🏽‍♂️','👮🏾‍♀️','👮🏾‍♂️','👮🏿‍♀️','👮🏿‍♂️','🙎🏽‍♀️','🙎🏼‍♂️','🙎🏼‍♀️','🙎🏻‍♂️','👱🏻‍♀️','👱🏻‍♂️','👱🏼‍♀️','👱🏼‍♂️','👱🏽‍♀️','👱🏽‍♂️','👱🏾‍♀️','👱🏾‍♂️','👱🏿‍♀️','👱🏿‍♂️','🙎🏻‍♀️','🙍🏿‍♂️','👳🏻‍♀️','👳🏻‍♂️','👳🏼‍♀️','👳🏼‍♂️','👳🏽‍♀️','👳🏽‍♂️','👳🏾‍♀️','👳🏾‍♂️','👳🏿‍♀️','👳🏿‍♂️','🙍🏿‍♀️','🙍🏾‍♂️','👷🏻‍♀️','👷🏻‍♂️','👷🏼‍♀️','👷🏼‍♂️','👷🏽‍♀️','👷🏽‍♂️','👷🏾‍♀️','👷🏾‍♂️','👷🏿‍♀️','👷🏿‍♂️','🙍🏾‍♀️','🙍🏽‍♂️','💁🏻‍♀️','💁🏻‍♂️','💁🏼‍♀️','💁🏼‍♂️','💁🏽‍♀️','💁🏽‍♂️','💁🏾‍♀️','💁🏾‍♂️','💁🏿‍♀️','💁🏿‍♂️','🙍🏽‍♀️','🙍🏼‍♂️','🏃🏻‍♀️','💂🏻‍♂️','💂🏼‍♀️','💂🏼‍♂️','💂🏽‍♀️','💂🏽‍♂️','💂🏾‍♀️','💂🏾‍♂️','💂🏿‍♀️','💂🏿‍♂️','🙍🏼‍♀️','🙍🏻‍♂️','💆🏻‍♀️','💆🏻‍♂️','💆🏼‍♀️','💆🏼‍♂️','💆🏽‍♀️','💆🏽‍♂️','💆🏾‍♀️','💆🏾‍♂️','💆🏿‍♀️','💆🏿‍♂️','🙍🏻‍♀️','🙋🏿‍♂️','💇🏻‍♀️','💇🏻‍♂️','💇🏼‍♀️','💇🏼‍♂️','💇🏽‍♀️','💇🏽‍♂️','💇🏾‍♀️','💇🏾‍♂️','💇🏿‍♀️','💇🏿‍♂️','🙋🏿‍♀️','🙋🏾‍♂️','🕴🏻‍♀️','🕴🏻‍♂️','🕴🏼‍♀️','🕴🏼‍♂️','🕴🏽‍♀️','🕴🏽‍♂️','🕴🏾‍♀️','🕴🏾‍♂️','🕴🏿‍♀️','🕴🏿‍♂️','🕵🏻‍♀️','🕵🏻‍♂️','🕵🏼‍♀️','🕵🏼‍♂️','🕵🏽‍♀️','🕵🏽‍♂️','🕵🏾‍♀️','🕵🏾‍♂️','🕵🏿‍♀️','🕵🏿‍♂️','🙅🏻‍♀️','🙅🏻‍♂️','🙅🏼‍♀️','🙅🏼‍♂️','🙅🏽‍♀️','🙅🏽‍♂️','🙅🏾‍♀️','🙅🏾‍♂️','🙅🏿‍♀️','🙅🏿‍♂️','🙋🏾‍♀️','🙋🏽‍♂️','🙆🏻‍♀️','🙆🏻‍♂️','🙆🏼‍♀️','🙆🏼‍♂️','🙆🏽‍♀️','🙆🏽‍♂️','🙆🏾‍♀️','🙆🏾‍♂️','🙆🏿‍♀️','🙆🏿‍♂️','🙋🏽‍♀️','🙋🏼‍♂️','🙇🏻‍♀️','🙇🏻‍♂️','🙇🏼‍♀️','🙇🏼‍♂️','🙇🏽‍♀️','🙇🏽‍♂️','🙇🏾‍♀️','🙇🏾‍♂️','🙇🏿‍♀️','🙇🏿‍♂️','🙋🏼‍♀️','🙋🏻‍♂️','🙋🏻‍♀️','🕴️‍♀️','🕴️‍♂️','🏋️‍♀️','🏋️‍♂️','⛹🏾‍♀️','🏌️‍♀️','🏌️‍♂️','⛹🏻‍♂️','⛹🏻‍♀️','⛹🏾‍♂️','⛹🏿‍♀️','⛹🏿‍♂️','🕵️‍♀️','🕵️‍♂️','⛹🏽‍♀️','⛹🏽‍♂️','⛹🏼‍♀️','⛹🏼‍♂️','⛹️‍♀️','⛹️‍♂️','👩🏻‍🎨','👨🏻‍🌾','👨🏻‍🍳','👨🏻‍🎓','👨🏻‍🎤','👨🏻‍🎨','👨🏻‍🏫','👨🏻‍🏭','👨🏻‍💻','👨🏻‍💼','👨🏻‍🔧','👨🏻‍🔬','👨🏻‍🚀','👨🏻‍🚒','👨🏻‍🦰','👨🏻‍🦱','👨🏻‍🦲','👨🏻‍🦳','👨🏼‍🌾','👨🏼‍🍳','👨🏼‍🎓','👨🏼‍🎤','👨🏼‍🎨','👨🏼‍🏫','👨🏼‍🏭','👨🏼‍💻','👨🏼‍💼','👨🏼‍🔧','👨🏼‍🔬','👨🏼‍🚀','👨🏼‍🚒','\D0y\B7\00\00\00\00\D0y\B7\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00p+\B6\00\00\00\00\00\00\00\00\00\00\00\00@)\B6\00\00\00\008z\B7\00\00\00\00\00\00\00\00\00\00\00\00\F0y\B7\00\00\00\00\00@\00\00\00\00\00\00\F0y\B7\00\00\00\00\00\00\00\00\00\00\00\00x1f9b2;','👨🏼‍🦳','👨🏽‍🌾','👨🏽‍🍳','👨🏽‍🎓','👨🏽‍🎤','👨🏽‍🎨','👨🏽‍🏫','👨🏽‍🏭','👨🏽‍💻','👨🏽‍💼','👨🏽‍🔧','👨🏽‍🔬','👨🏽‍🚀','👨🏽‍🚒','👨🏽‍🦰','👨🏽‍🦱','👨🏽‍🦲','👩🏿‍🦳','👩🏿‍🦲','👩🏿‍🦱','👨🏽‍🦳','👨🏾‍🌾','👩🏿‍🦰','👩🏿‍🚒','👩🏿‍🚀','👩🏿‍🔬','👩🏿‍🔧','👩🏿‍💼','👩🏿‍💻','👩🏿‍🏭','👩🏿‍🏫','👩🏿‍🎨','👨🏾‍🍳','👨🏾‍🎓','👩🏿‍🎤','👩🏿‍🎓','👩🏿‍🍳','👩🏿‍🌾','👩🏾‍🦳','👩🏾‍🦲','👩🏾‍🦱','👩🏾‍🦰','👩🏾‍🚒','👩🏾‍🚀','👨🏾‍🎤','👨🏾‍🎨','👩🏾‍🔬','👩🏾‍🔧','👩🏾‍💼','👩🏾‍💻','👩🏾‍🏭','👩🏾‍🏫','👩🏾‍🎨','👩🏾‍🎤','👩🏾‍🎓','👩🏾‍🍳','👨🏾‍🏫','👨🏾‍🏭','👩🏾‍🌾','👩🏽‍🦳','👩🏽‍🦲','👩🏽‍🦱','👩🏽‍🦰','👩🏽‍🚒','👩🏽‍🚀','👩🏽‍🔬','👩🏽‍🔧','👩🏽‍💼','👨🏾‍💻','👨🏾‍💼','👩🏽‍💻','👩🏽‍🏭','👩🏽‍🏫','👩🏽‍🎨','👩🏽‍🎤','👩🏽‍🎓','👩🏽‍🍳','👩🏽‍🌾','👩🏼‍🦳','👩🏼‍🦲','👨🏾‍🔧','👨🏾‍🔬','👩🏼‍🦱','👩🏼‍🦰','👩🏼‍🚒','👩🏼‍🚀','👩🏼‍🔬','👩🏼‍🔧','👩🏼‍💼','👩🏼‍💻','👩🏼‍🏭','👩🏼‍🏫','👨🏾‍🚀','👨🏾‍🚒','👩🏼‍🎨','👩🏼‍🎤','👩🏼‍🎓','👩🏼‍🍳','👩🏼‍🌾','👩🏻‍🦳','👩🏻‍🦲','👩🏻‍🦱','👩🏻‍🦰','👩🏻‍🚒','👨🏾‍🦰','👨🏾‍🦱','👨🏾‍🦲','👨🏾‍🦳','👩🏻‍🚀','👩🏻‍🔬','👩🏻‍🔧','👩🏻‍💼','👩🏻‍💻','👩🏻‍🏭','👩🏻‍🏫','👨🏿‍🌾','👩🏻‍🎤','👩🏻‍🎓','👨🏿‍🍳','👨🏿‍🎓','👩🏻‍🍳','👩🏻‍🌾','👨🏿‍🎤','👨🏿‍🎨','👨🏿‍🏫','👨🏿‍🏭','👨🏿‍💻','👨🏿‍💼','👨🏿‍🔧','👨🏿‍🔬','👨🏿‍🚀','👨🏿‍🚒','👨🏿‍🦰','👨🏿‍🦱','👨🏿‍🦲','👨🏿‍🦳','🏳️‍🌈','👩‍⚖️','🦸‍♂️','🤾‍♂️','🤾‍♀️','🦹‍♀️','🦹‍♂️','👨‍⚕️','👨‍⚖️','👨‍✈️','🤽‍♂️','🤽‍♀️','🧖‍♀️','🧖‍♂️','🙇‍♀️','🤼‍♂️','🤼‍♀️','🤹‍♂️','🤹‍♀️','🤸‍♂️','🤸‍♀️','🤷‍♂️','🤷‍♀️','🤵‍♂️','🧗‍♀️','🧗‍♂️','🤵‍♀️','🤦‍♂️','🤦‍♀️','🚶‍♂️','🚶‍♀️','🚵‍♂️','🚵‍♀️','🚴‍♂️','🚴‍♀️','🧘‍♀️','🧘‍♂️','🚣‍♂️','🚣‍♀️','🧙‍♀️','🧙‍♂️','🙎‍♂️','🙎‍♀️','🧚‍♀️','🧚‍♂️','👩‍⚕️','🦸‍♀️','👩‍✈️','👮‍♀️','👮‍♂️','👯‍♀️','👯‍♂️','👱‍♀️','🧛‍♀️','🧛‍♂️','🙍‍♂️','🙍‍♀️','👱‍♂️','👳‍♀️','👳‍♂️','👷‍♀️','👷‍♂️','💁‍♀️','💁‍♂️','💂‍♀️','🧜‍♀️','🧜‍♂️','💂‍♂️','💆‍♀️','🙋‍♂️','🙋‍♀️','💆‍♂️','💇‍♀️','💇‍♂️','🙅‍♀️','🙅‍♂️','🧝‍♀️','🧝‍♂️','🧞‍♀️','🧞‍♂️','🧟‍♀️','🧟‍♂️','🏴‍☠️','🙇‍♂️','🙆‍♀️','🙆‍♂️','🏊‍♂️','🏊‍♀️','🏄‍♂️','🏄‍♀️','🏃‍♂️','🏃‍♀️','👨‍🎨','👩‍🍳','👩‍🎓','👩‍🎤','👩‍🎨','👩‍🏫','👩‍🏭','👨‍💻','👨‍🚒','👩‍👦','👨‍👧','👨‍🦰','👨‍🦱','👩‍💻','👩‍💼','👩‍🔧','👩‍🔬','👩‍🚀','👩‍🚒','👩‍🦰','👩‍🦱','👁‍🗨','👨‍🦲','👨‍🦳','👨‍👦','👨‍🎤','👩‍🦲','👩‍🦳','👨‍🎓','👨‍🍳','👨‍🌾','👨‍💼','👨‍🔧','👨‍🔬','👨‍🚀','👨‍🏭','👩‍🌾','👨‍🏫','👩‍👧','👆🏿','👇🏻','👷🏼','👇🏼','👇🏽','👷🏽','👇🏾','👇🏿','👷🏾','👈🏻','👈🏼','👷🏿','👈🏽','👈🏾','👸🏻','👸🏼','👸🏽','👸🏾','👸🏿','👼🏻','👼🏼','👼🏽','👼🏾','👼🏿','👈🏿','👉🏻','💁🏻','👉🏼','👉🏽','💁🏼','👉🏾','👉🏿','💁🏽','👊🏻','👊🏼','💁🏾','👊🏽','👊🏾','💁🏿','👊🏿','👋🏻','👋🏼','👋🏽','💂🏻','👋🏾','👋🏿','💂🏼','👌🏻','👌🏼','💂🏽','👌🏽','👌🏾','💂🏾','👌🏿','👍🏻','💂🏿','👍🏼','👍🏽','💃🏻','💃🏼','💃🏽','💃🏾','💃🏿','💅🏻','💅🏼','💅🏽','💅🏾','💅🏿','👍🏾','👍🏿','💆🏻','👎🏻','👎🏼','💆🏼','👎🏽','👎🏾','💆🏽','👎🏿','👏🏻','💆🏾','👏🏼','👏🏽','💆🏿','👏🏾','👏🏿','👐🏻','👐🏼','💇🏻','👐🏽','👐🏾','💇🏼','👐🏿','👦🏻','💇🏽','👦🏼','👦🏽','💇🏾','👦🏾','👦🏿','💇🏿','👧🏻','👧🏼','💪🏻','💪🏼','💪🏽','💪🏾','💪🏿','👧🏽','👧🏾','🕴🏻','👧🏿','🇪🇪','🕴🏼','🇪🇬','🇪🇭','🕴🏽','🇪🇷','🇪🇸','🕴🏾','🇪🇹','🇪🇺','🕴🏿','🇫🇮','🇫🇯','🇫🇰','🇫🇲','🕵🏻','🇫🇴','🇫🇷','🕵🏼','🇬🇦','🇬🇧','🕵🏽','🇬🇩','🇬🇪','🕵🏾','🇬🇫','🇬🇬','🕵🏿','🇬🇭','👨🏻','🕺🏻','🕺🏼','🕺🏽','🕺🏾','🕺🏿','🖐🏻','🖐🏼','🖐🏽','🖐🏾','🖐🏿','🖕🏻','🖕🏼','🖕🏽','🖕🏾','🖕🏿','🖖🏻','🖖🏼','🖖🏽','🖖🏾','🖖🏿','🇬🇮','🇬🇱','🙅🏻','🇬🇲','🇬🇳','🙅🏼','🇬🇵','🇬🇶','🙅🏽','🇬🇷','🇬🇸','🙅🏾','🇬🇹','🇬🇺','🙅🏿','🇬🇼','🇬🇾','🇭🇰','🇭🇲','🙆🏻','🇭🇳','🇭🇷','🙆🏼','🇭🇹','🇭🇺','🙆🏽','🇮🇨','🇮🇩','🙆🏾','👨🏼','🇮🇪','🙆🏿','🇮🇱','🇮🇲','🇮🇳','🇮🇴','🙇🏻','🇮🇶','🇮🇷','🙇🏼','🇮🇸','🇮🇹','🙇🏽','🇯🇪','🇯🇲','🙇🏾','🇯🇴','🇯🇵','🙇🏿','🇰🇪','🇰🇬','🇰🇭','🇰🇮','🙋🏻','🇰🇲','🇰🇳','🙋🏼','🇰🇵','👨🏽','🙋🏽','🇰🇷','🇰🇼','🙋🏾','🇰🇾','🇰🇿','🙋🏿','🇱🇦','🇱🇧','🙌🏻','🙌🏼','🙌🏽','🙌🏾','🙌🏿','🇱🇨','🇱🇮','🙍🏻','🇱🇰','🇱🇷','🙍🏼','🇱🇸','🇱🇹','🙍🏽','🇱🇺','🇱🇻','🙍🏾','🇱🇾','🇲🇦','🙍🏿','🇲🇨','🇲🇩','🇲🇪','🇲🇫','🙎🏻','👨🏾','🇲🇬','🙎🏼','🇲🇭','🇲🇰','🙎🏽','🇲🇱','🇲🇲','🙎🏾','🇲🇳','🇲🇴','🙎🏿','🇲🇵','🇲🇶','🙏🏻','🙏🏼','🙏🏽','🙏🏾','🙏🏿','🇲🇷','🇲🇸','🚣🏻','🇲🇹','🇲🇺','🚣🏼','🇲🇻','🇲🇼','🚣🏽','🇲🇽','🇲🇾','🚣🏾','🇲🇿','🇳🇦','🚣🏿','🇳🇨','👨🏿','🇳🇪','🇳🇫','🚴🏻','🇳🇬','🇳🇮','🚴🏼','🇳🇱','🇳🇴','🚴🏽','🇳🇵','🇳🇷','🚴🏾','🇳🇺','🇳🇿','🚴🏿','🇴🇲','🇵🇦','🇵🇪','🇵🇫','🚵🏻','🇵🇬','🇵🇭','🚵🏼','🇵🇰','🇵🇱','🚵🏽','🇵🇲','&\D0y\B7\00\00\00\00\D0y\B7\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00p+\B6\00\00\00\00\00\00\00\00\00\00\00\00@)\B6\00\00\00\008z\B7\00\00\00\00\00\00\00\00\00\00\00\00\F0y\B7\00\00\00\00\00@\00\00\00\00\00\00\F0y\B7\00\00\00\00\00\00\00\00\00\00\00\00🇵🇹','🇵🇼','🇵🇾','🇶🇦','🚶🏻','🇷🇪','🇷🇴','🚶🏼','🇷🇸','🇷🇺','🚶🏽','🇷🇼','🇸🇦','🚶🏾','🇸🇧','🇸🇨','🚶🏿','🇸🇩','🇸🇪','🛀🏻','🛀🏼','🛀🏽','🛀🏾','🛀🏿','🛌🏻','🛌🏼','🛌🏽','🛌🏾','🛌🏿','🤘🏻','🤘🏼','🤘🏽','🤘🏾','🤘🏿','🤙🏻','🤙🏼','🤙🏽','🤙🏾','🤙🏿','🤚🏻','🤚🏼','🤚🏽','🤚🏾','🤚🏿','🤛🏻','🤛🏼','🤛🏽','🤛🏾','🤛🏿','🤜🏻','🤜🏼','🤜🏽','🤜🏾','🤜🏿','🤞🏻','🤞🏼','🤞🏽','🤞🏾','🤞🏿','🤟🏻','🤟🏼','🤟🏽','🤟🏾','🤟🏿','🇸🇬','🇸🇭','🤦🏻','🇸🇮','🇸🇯','🤦🏼','🇸🇰','🇦🇩','🤦🏽','🇸🇲','🇸🇳','🤦🏾','🇸🇴','🇸🇷','🤦🏿','🇸🇸','🇸🇹','🤰🏻','🤰🏼','🤰🏽','🤰🏾','🤰🏿','🤱🏻','🤱🏼','🤱🏽','🤱🏾','🤱🏿','🤲🏻','🤲🏼','🤲🏽','🤲🏾','🤲🏿','🤳🏻','🤳🏼','🤳🏽','🤳🏾','🤳🏿','🤴🏻','🤴🏼','🤴🏽','🤴🏾','🤴🏿','🇸🇻','🇸🇽','🤵🏻','🇸🇾','🇸🇿','🤵🏼','🇹🇦','🇹🇨','🤵🏽','🇹🇩','🇹🇫','🤵🏾','🇹🇬','👩🏻','🤵🏿','🇹🇭','🇹🇯','🤶🏻','🤶🏼','🤶🏽','🤶🏾','🤶🏿','🇹🇰','🇹🇱','🤷🏻','🇹🇲','🇹🇳','🤷🏼','🇹🇴','🇹🇷','🤷🏽','🇹🇹','🇹🇻','🤷🏾','🇹🇼','🇹🇿','🤷🏿','🇺🇦','🇺🇬','🇺🇲','🇺🇳','🤸🏻','🇺🇸','🇺🇾','🤸🏼','🇺🇿','🇻🇦','🤸🏽','👩🏼','🇻🇨','🤸🏾','🇻🇪','🇻🇬','🤸🏿','🇻🇮','🇻🇳','🇻🇺','🇼🇫','🤹🏻','🇼🇸','🇽🇰','🤹🏼','🇾🇪','🇾🇹','🤹🏽','🇿🇦','🇿🇲','🤹🏾','🇿🇼','🎅🏻','🤹🏿','🎅🏼','🎅🏽','🎅🏾','🎅🏿','🏂🏻','👩🏽','🤽🏻','🏂🏼','🏂🏽','🤽🏼','🏂🏾','🏂🏿','🤽🏽','🇦🇨','🇦🇪','🤽🏾','🏃🏻','🇦🇫','🤽🏿','🇦🇬','🏃🏼','🇦🇮','🇦🇱','🤾🏻','🏃🏽','🇦🇲','🤾🏼','🇦🇴','🏃🏾','🤾🏽','🇦🇶','🇦🇷','🤾🏾','🏃🏿','🇦🇸','🤾🏿','👩🏾','🇦🇹','🦵🏻','🦵🏼','🦵🏽','🦵🏾','🦵🏿','🦶🏻','🦶🏼','🦶🏽','🦶🏾','🦶🏿','🇦🇺','🇦🇼','🦸🏻','🏄🏻','🇦🇽','🦸🏼','🇦🇿','🏄🏼','🦸🏽','🇧🇦','🇧🇧','🦸🏾','🏄🏽','🇧🇩','🦸🏿','🇧🇪','🏄🏾','🇧🇫','🇧🇬','🦹🏻','🏄🏿','🇧🇭','🦹🏼','🇧🇮','🏇🏻','🦹🏽','🏇🏼','👩🏿','🦹🏾','🏇🏽','🏇🏾','🦹🏿','🏇🏿','🇧🇯','🧑🏻','🧑🏼','🧑🏽','🧑🏾','🧑🏿','🧒🏻','🧒🏼','🧒🏽','🧒🏾','🧒🏿','🧓🏻','🧓🏼','🧓🏽','🧓🏾','🧓🏿','🧔🏻','🧔🏼','🧔🏽','🧔🏾','🧔🏿','🧕🏻','🧕🏼','🧕🏽','🧕🏾','🧕🏿','🇧🇱','🏊🏻','🧖🏻','🇧🇲','🇧🇳','🧖🏼','🏊🏼','🇧🇴','🧖🏽','🇧🇶','🏊🏽','🧖🏾','🇧🇷','🇧🇸','🧖🏿','🏊🏾','🇧🇹','🇧🇻','🏊🏿','🧗🏻','🇧🇼','🇧🇾','🧗🏼','🇧🇿','🇨🇦','🧗🏽','🏋🏻','🇨🇨','🧗🏾','🇨🇩','🏋🏼','🧗🏿','🇨🇫','🇨🇬','🏋🏽','🇨🇭','🧘🏻','🇨🇮','🏋🏾','🧘🏼','🇨🇰','🇨🇱','🧘🏽','🏋🏿','🇨🇲','🧘🏾','👮🏻','🇨🇳','🧘🏿','🇨🇴','👮🏼','🇨🇵','🏌🏻','🧙🏻','👮🏽','🇨🇷','🧙🏼','🇨🇺','👮🏾','🧙🏽','🏌🏼','🇨🇻','🧙🏾','👮🏿','🇨🇼','🧙🏿','🏌🏽','🇨🇽','🇨🇾','👰🏻','🧚🏻','👰🏼','👰🏽','🧚🏼','👰🏾','👰🏿','🧚🏽','🏌🏾','🇨🇿','🧚🏾','👱🏻','🇩🇪','🧚🏿','🏌🏿','👱🏼','🇩🇬','🇩🇯','🧛🏻','👱🏽','🇩🇰','🧛🏼','🇩🇲','👱🏾','🧛🏽','🇩🇴','🇩🇿','🧛🏾','👱🏿','🇪🇦','🧛🏿','🇪🇨','👲🏻','👲🏼','👲🏽','🧜🏻','👲🏾','👲🏿','🧜🏼','👂🏻','👂🏼','🧜🏽','👳🏻','👂🏽','🧜🏾','👂🏾','👳🏼','🧜🏿','👂🏿','👃🏻','👳🏽','👃🏼','🧝🏻','👃🏽','👳🏾','🧝🏼','👃🏾','👃🏿','🧝🏽','👳🏿','👆🏻','🧝🏾','👆🏼','👴🏻','🧝🏿','👴🏼','👴🏽','👴🏾','👴🏿','👵🏻','👵🏼','👵🏽','👵🏾','👵🏿','👶🏻','👶🏼','👶🏽','👶🏾','👶🏿','👆🏽','👆🏾','👷🏻','🇸🇱','✍🏿','⛹🏻','✍🏾','✍🏽','✍🏼','✍🏻','✌🏿','✌🏾','✌🏽','✌🏼','✌🏻','✋🏿','✋🏾','✋🏽','✋🏼','✋🏻','✊🏿','✊🏾','✊🏽','✊🏼','✊🏻','⛷🏽','⛷🏾','⛹🏿','☝🏿','☝🏾','⛹🏾','☝🏽','☝🏼','⛹🏽','☝🏻','⛷🏿','⛹🏼','⛷🏻','⛷🏼','4⃣','#⃣','0⃣','1⃣','2⃣','3⃣','*⃣','5⃣','6⃣','7⃣','8⃣','9⃣','🃏','🕺','🖇','🖊','🖋','🖌','🖍','🀄','🇾','🇦','🅰','🅱','🖐','🇿','🈁','🈂','🏄','🏅','🖕','🏆','🈚','🈯','🈲','🈳','🖖','🖤','🖥','🖨','🖱','🖲','🖼','🗂','🗃','🗄','🗑','🗒','🗓','🗜','🗝','🗞','🗡','🗣','🗨','🗯','🗳','🗺','🗻','🗼','🗽','🗾','🗿','😀','😁','😂','😃','😄','😅','😆','😇','😈','😉','😊','😋','😌','😍','😎','😏','😐','😑','😒','😓','😔','😕','😖','😗','😘','😙','😚','😛','😜','😝','😞','😟','😠','😡','😢','😣','😤','😥','😦','😧','😨','😩','😪','😫','😬','😭','😮','😯','😰','😱','😲','😳','😴','😵','😶','😷','😸','😹','😺','😻','😼','😽','😾','😿','🙀','🙁','🙂','🙃','🙄','🈴','🏇','🏈','🏉','🈵','🈶','🈷','🈸','🈹','🈺','🉐','🉑','🌀','🌁','🌂','🌃','🌄','🙅','🌅','🌆','🌇','🌈','🏊','🌉','🌊','🌋','🌌','👨','🌍','🌎','🌏','🌐','🌑','🌒','🌓','🙆','🌔','🌕','🌖','🌗','🌘','🌙','🏋','🌚','🌛','🌜','🌝','🌞','🌟','🌠','🌡','🌤','🌥','🙇','🙈','🙉','🙊','🌦','🌧','🌨','🌩','🌪','🌫','🌬','🏌','🏍','🏎','🏏','🏐','🏑','🏒','🏓','🏔','🏕','🙋','🏖','🏗','🏘','🏙','🏚','🙌','🏛','🏜','🏝','🏞','🏟','🏠','🏡','🏢','🏣','🏤','🏥','🏦','🏧','🏨','🏩','🏪','🏫','🙍','🏬','🏭','🏮','🏯','🏰','🌭','🏳','🌮','🌯','🌰','🌱','🏴','🏵','🏷','🏸','🏹','🏺','🙎','🏻','🏼','🏽','🏾','🏿','🙏','🚀','🚁','🚂','🚃','🚄','🚅','🚆','🚇','🚈','🚉','🚊','🚋','🚌','🚍','🚎','🚏','🚐','🚑','🚒','🚓','🚔','🚕','🚖','🚗','🚘','🚙','🚚','🚛','🚜','🚝','🚞','🚟','🚠','🚡','🚢','🐀','🐁','🐂','🐃','🐄','🐅','🐆','🐇','🐈','🐉','🐊','🐋','🐌','🐍','🐎','🐏','🐐','🚣','🚤','🚥','🚦','🚧','🚨','🚩','🚪','🚫','🚬','🚭','🚮','🚯','🚰','🚱','🚲','🚳','🐑','🐒','🐓','🐔','🐕','🐖','🐗','🐘','🐙','🐚','🐛','🐜','🐝','🐞','🐟','🐠','🐡','🚴','🐢','🐣','🐤','🐥','🐦','🐧','🐨','🐩','🐪','🐫','🐬','🐭','🐮','🐯','🐰','🐱','🐲','🚵','🐳','🐴','🐵','👩','👪','👫','👬','👭','🐶','🐷','🐸','🐹','🐺','🐻','🐼','🐽','🐾','🚶','🚷','🚸','🚹','🚺','🚻','🚼','🚽','🚾','🚿','🐿','👀','🌲','👁','🌳','🛀','🛁','🛂','🛃','🛄','🛅','🛋','🌴','🌵','🌶','👮','🌷','🛌','🛍','🛎','🛏','🛐','🛑','🛒','🛠','🛡','🛢','🛣','🛤','🛥','🛩','🛫','🛬','🛰','🛳','🛴','🛵','🛶','🛷','🛸','🛹','🤐','🤑','🤒','🤓','🤔','🤕','🤖','🤗','👂','👯','🌸','🌹','🌺','🤘','🌻','🌼','👰','👃','👄','🤙','👅','🌽','🌾','🌿','🍀','🤚','🍁','👆','🍂','🍃','🍄','🤛','🍅','🍆','👇','🍇','🍈','🤜','🤝','👱','🍉','🍊','🍋','👈','🤞','🍌','👲','🍍','🍎','🍏','🤟','🤠','🤡','🤢','🤣','🤤','🤥','🍐','👉','🍑','🍒','🍓','🍔','🍕','👊','🍖','🍗','🍘','🍙','🍚','👋','👳','🍛','🍜','🤦','🤧','🤨','🤩','🤪','🤫','🤬','🤭','🤮','🤯','🍝','🍞','🍟','👴','👌','🤰','🍠','🍡','🍢','🍣','👵','🤱','🍤','👍','🍥','🍦','🍧','🤲','👶','🍨','🍩','👎','🍪','🤳','🍫','🍬','🍭','🍮','👏','🤴','🍯','🍰','🍱','🍲','🍳','👐','👑','👒','👷','👓','👔','👕','👖','👗','👸','👹','👺','🤵','👻','👘','👙','👚','👛','🤶','👜','👼','👽','👾','👿','💀','👝','👞','👟','👠','👡','👢','👣','👤','👥','🍴','🍵','🤷','🍶','🍷','🍸','👦','🍹','🍺','💁','🍻','🍼','🍽','👧','🍾','🍿','🎀','🎁','🎂','🎃','🤸','🎄','🇵','🅾','🇶','🇲','🅿','🎅','💂','🎆','🎇','🎈','🎉','🎊','💃','💄','🎋','🎌','🤹','🤺','🎍','🎎','🤼','🎏','💅','🎐','🎑','🎒','🎓','🎖','🎗','🎙','🎚','🎛','🎞','🎟','🎠','🎡','🎢','🎣','🤽','🎤','🎥','💆','🎦','🎧','🎨','🎩','🎪','🎫','🎬','🎭','🎮','🎯','🎰','🎱','🎲','🎳','🤾','🥀','🥁','🥂','🥃','🥄','🥅','🥇','🥈','🥉','🥊','🥋','🥌','🥍','🥎','🥏','🥐','🥑','🥒','🥓','🥔','🥕','🥖','🥗','🥘','🥙','🥚','🥛','🥜','🥝','🥞','🥟','🥠','🥡','🥢','🥣','🥤','🥥','🥦','🥧','🥨','🥩','🥪','🥫','🥬','🥭','🥮','🥯','🥰','🥳','🥴','🥵','🥶','🥺','🥼','🥽','🥾','🥿','🦀','🦁','🦂','🦃','🦄','🦅','🦆','🦇','🦈','🦉','🦊','🦋','🦌','🦍','🦎','🦏','🦐','🦑','🦒','🦓','🦔','🦕','🦖','🦗','🦘','🦙','🦚','🦛','🦜','🦝','🦞','🦟','🦠','🦡','🦢','🦴','🎴','🎵','🎶','💇','💈','🦵','💉','💊','💋','💌','💍','🦶','🦷','💎','💏','💐','💑','💒','💓','💔','💕','💖','💗','💘','💙','💚','💛','💜','💝','💞','🦸','💟','💠','💡','💢','💣','💤','💥','💦','💧','💨','💩','🎷','🎸','🎹','🎺','🎻','💪','🦹','🧀','🧁','🧂','🧐','💫','💬','💭','💮','💯','🧑','💰','💱','💲','💳','💴','🧒','💵','💶','💷','💸','💹','🧓','💺','💻','💼','💽','💾','🧔','💿','📀','📁','📂','📃','🧕','📄','📅','📆','📇','📈','📉','📊','📋','📌','📍','📎','📏','📐','📑','📒','📓','📔','🧖','📕','📖','📗','📘','📙','📚','📛','📜','📝','📞','📟','📠','📡','📢','📣','📤','📥','🧗','📦','📧','📨','📩','📪','📫','📬','📭','📮','📯','📰','📱','📲','📳','📴','📵','📶','🧘','📷','📸','📹','📺','📻','📼','📽','📿','🔀','🔁','🔂','🔃','🔄','🔅','🔆','🔇','🔈','🧙','🔉','🔊','🔋','🔌','🔍','🔎','🔏','🔐','🔑','🔒','🔓','🔔','🔕','🔖','🔗','🔘','🔙','🧚','🔚','🔛','🔜','🔝','🔞','🔟','🔠','🔡','🔢','🔣','🔤','🔥','🔦','🔧','🔨','🔩','🔪','🧛','🔫','🔬','🔭','🔮','🔯','🔰','🔱','🔲','🔳','🔴','🔵','🔶','🔷','🔸','🔹','🔺','🔻','🧜','🔼','🔽','🕉','🕊','🕋','🕌','🕍','🕎','🕐','🕑','🕒','🕓','🕔','🕕','🕖','🕗','🕘','🧝','🕙','🕚','🧞','🕛','🕜','🧟','🧠','🧡','🧢','🧣','🧤','🧥','🧦','🧧','🧨','🧩','🧪','🧫','🧬','🧭','🧮','🧯','🧰','🧱','🧲','🧳','🧴','🧵','🧶','🧷','🧸','🧹','🧺','🧻','🧼','🧽','🧾','🧿','🕝','🕞','🕟','🕠','🕡','🕢','🕣','🕤','🕥','🕦','🕧','🕯','🕰','🕳','🎼','🎽','🎾','🎿','🏀','🏁','🇧','🇮','🇪','🇷','🇱','🏂','🆎','🆑','🇨','🇹','🇯','🕴','🆒','🇬','🆓','🇳','🆔','🇴','🇺','🇫','🆕','🆖','🆗','🇭','🏃','🆘','🇩','🇻','🇰','🕵','🕶','🕷','🕸','🕹','🆙','🇼','🆚','🇽','🇸','▫','☦','☮','☯','☸','☹','☺','♀','♂','♈','♉','♊','♋','♌','♍','♎','♏','♐','♑','♒','♓','♟','♠','♣','♥','♦','♨','♻','♾','♿','⚒','⚓','⚔','⚕','⚖','⚗','⚙','⚛','⚜','⚠','⚡','⚪','⚫','⚰','⚱','⚽','⚾','⛄','⛅','⛈','⛎','⛏','⛑','⛓','⛔','⛩','⛪','⛰','⛱','⛲','⛳','⛴','⛵','☣','☢','☠','☝','☘','⛷','⛸','☕','☔','☑','☎','☄','☃','☂','☁','☀','◾','◽','◼','◻','◀','▶','☪','▪','⛹','⛺','⛽','✂','✅','✈','✉','Ⓜ','⏺','⏹','⏸','⏳','✊','⏲','⏱','⏰','⏯','⏮','✋','⏭','⏬','⏫','⏪','⏩','✌','⏏','⌨','⌛','⌚','↪','✍','✏','✒','✔','✖','✝','✡','✨','✳','✴','❄','❇','❌','❎','❓','❔','❕','❗','❣','❤','➕','➖','➗','➡','➰','➿','⤴','⤵','↩','⬅','⬆','⬇','⬛','⬜','⭐','⭕','↙','〰','〽','↘','↗','㊗','㊙','↖','↕','↔','ℹ','™','⁉','‼','');
5318 $partials = array('🀄','🃏','🅰','🅱','🅾','🅿','🆎','🆑','🆒','🆓','🆔','🆕','🆖','🆗','🆘','🆙','🆚','🇦','🇨','🇩','🇪','🇫','🇬','🇮','🇱','🇲','🇴','🇶','🇷','🇸','🇹','🇺','🇼','🇽','🇿','🇧','🇭','🇯','🇳','🇻','🇾','🇰','🇵','🈁','🈂','🈚','🈯','🈲','🈳','🈴','🈵','🈶','🈷','🈸','🈹','🈺','🉐','🉑','🌀','🌁','🌂','🌃','🌄','🌅','🌆','🌇','🌈','🌉','🌊','🌋','🌌','🌍','🌎','🌏','🌐','🌑','🌒','🌓','🌔','🌕','🌖','🌗','🌘','🌙','🌚','🌛','🌜','🌝','🌞','🌟','🌠','🌡','🌤','🌥','🌦','🌧','🌨','🌩','🌪','🌫','🌬','🌭','🌮','🌯','🌰','🌱','🌲','🌳','🌴','🌵','🌶','🌷','🌸','🌹','🌺','🌻','🌼','🌽','🌾','🌿','🍀','🍁','🍂','🍃','🍄','🍅','🍆','🍇','🍈','🍉','🍊','🍋','🍌','🍍','🍎','🍏','🍐','🍑','🍒','🍓','🍔','🍕','🍖','🍗','🍘','🍙','🍚','🍛','🍜','🍝','🍞','🍟','🍠','🍡','🍢','🍣','🍤','🍥','🍦','🍧','🍨','🍩','🍪','🍫','🍬','🍭','🍮','🍯','🍰','🍱','🍲','🍳','🍴','🍵','🍶','🍷','🍸','🍹','🍺','🍻','🍼','🍽','🍾','🍿','🎀','🎁','🎂','🎃','🎄','🎅','🏻','🏼','🏽','🏾','🏿','🎆','🎇','🎈','🎉','🎊','🎋','🎌','🎍','🎎','🎏','🎐','🎑','🎒','🎓','🎖','🎗','🎙','🎚','🎛','🎞','🎟','🎠','🎡','🎢','🎣','🎤','🎥','🎦','🎧','🎨','🎩','🎪','🎫','🎬','🎭','🎮','🎯','🎰','🎱','🎲','🎳','🎴','🎵','🎶','🎷','🎸','🎹','🎺','🎻','🎼','🎽','🎾','🎿','🏀','🏁','🏂','🏃','‍','♀','️','♂','🏄','🏅','🏆','🏇','🏈','🏉','🏊','🏋','🏌','🏍','🏎','🏏','🏐','🏑','🏒','🏓','🏔','🏕','🏖','🏗','🏘','🏙','🏚','🏛','🏜','🏝','🏞','🏟','🏠','🏡','🏢','🏣','🏤','🏥','🏦','🏧','🏨','🏩','🏪','🏫','🏬','🏭','🏮','🏯','🏰','🏳','🏴','☠','󠁧','󠁢','󠁥','󠁮','󠁿','󠁳','󠁣','󠁴','󠁷','󠁬','🏵','🏷','🏸','🏹','🏺','🐀','🐁','🐂','🐃','🐄','🐅','🐆','🐇','🐈','🐉','🐊','🐋','🐌','🐍','🐎','🐏','🐐','🐑','🐒','🐓','🐔','🐕','🐖','🐗','🐘','🐙','🐚','🐛','🐜','🐝','🐞','🐟','🐠','🐡','🐢','🐣','🐤','🐥','🐦','🐧','🐨','🐩','🐪','🐫','🐬','🐭','🐮','🐯','🐰','🐱','🐲','🐳','🐴','🐵','🐶','🐷','🐸','🐹','🐺','🐻','🐼','🐽','🐾','🐿','👀','👁','🗨','👂','👃','👄','👅','👆','👇','👈','👉','👊','👋','👌','👍','👎','👏','👐','👑','👒','👓','👔','👕','👖','👗','👘','👙','👚','👛','👜','👝','👞','👟','👠','👡','👢','👣','👤','👥','👦','👧','👨','💻','💼','🔧','🔬','🚀','🚒','🦰','🦱','🦲','🦳','⚕','⚖','✈','👩','❤','💋','👪','👫','👬','👭','👮','👯','👰','👱','👲','👳','👴','👵','👶','👷','👸','👹','👺','👻','👼','👽','👾','👿','💀','💁','💂','💃','💄','💅','💆','💇','💈','💉','💊','💌','💍','💎','💏','💐','💑','💒','💓','💔','💕','💖','💗','💘','💙','💚','💛','💜','💝','💞','💟','💠','💡','💢','💣','💤','💥','💦','💧','💨','💩','💪','💫','💬','💭','💮','💯','💰','💱','💲','💳','💴','💵','💶','💷','💸','💹','💺','💽','💾','💿','📀','📁','📂','📃','📄','📅','📆','📇','📈','📉','📊','📋','📌','📍','📎','📏','📐','📑','📒','📓','📔','📕','📖','📗','📘','📙','📚','📛','📜','📝','📞','📟','📠','📡','📢','📣','📤','📥','📦','📧','📨','📩','📪','📫','📬','📭','📮','📯','📰','📱','📲','📳','📴','📵','📶','📷','📸','📹','📺','📻','📼','📽','📿','🔀','🔁','🔂','🔃','🔄','🔅','🔆','🔇','🔈','🔉','🔊','🔋','🔌','🔍','🔎','🔏','🔐','🔑','🔒','🔓','🔔','🔕','🔖','🔗','🔘','🔙','🔚','🔛','🔜','🔝','🔞','🔟','🔠','🔡','🔢','🔣','🔤','🔥','🔦','🔨','🔩','🔪','🔫','🔭','🔮','🔯','🔰','🔱','🔲','🔳','🔴','🔵','🔶','🔷','🔸','🔹','🔺','🔻','🔼','🔽','🕉','🕊','🕋','🕌','🕍','🕎','🕐','🕑','🕒','🕓','🕔','🕕','🕖','🕗','🕘','🕙','🕚','🕛','🕜','🕝','🕞','🕟','🕠','🕡','🕢','🕣','🕤','🕥','🕦','🕧','🕯','🕰','🕳','🕴','🕵','🕶','🕷','🕸','🕹','🕺','🖇','🖊','🖋','🖌','🖍','🖐','🖕','🖖','🖤','🖥','🖨','🖱','🖲','🖼','🗂','🗃','🗄','🗑','🗒','🗓','🗜','🗝','🗞','🗡','🗣','🗯','🗳','🗺','🗻','🗼','🗽','🗾','🗿','😀','😁','😂','😃','😄','😅','😆','😇','😈','😉','😊','😋','😌','😍','😎','😏','😐','😑','😒','😓','😔','😕','😖','😗','😘','😙','😚','😛','😜','😝','😞','😟','😠','😡','😢','😣','😤','😥','😦','😧','😨','😩','😪','😫','😬','😭','😮','😯','😰','😱','😲','😳','😴','😵','😶','😷','😸','😹','😺','😻','😼','😽','😾','😿','🙀','🙁','🙂','🙃','🙄','🙅','🙆','🙇','🙈','🙉','🙊','🙋','🙌','🙍','🙎','🙏','🚁','🚂','🚃','🚄','🚅','🚆','🚇','🚈','🚉','🚊','🚋','🚌','🚍','🚎','🚏','🚐','🚑','🚓','🚔','🚕','🚖','🚗','🚘','🚙','🚚','🚛','🚜','🚝','🚞','🚟','🚠','🚡','🚢','🚣','🚤','🚥','🚦','🚧','🚨','🚩','🚪','🚫','🚬','🚭','🚮','🚯','🚰','🚱','🚲','🚳','🚴','🚵','🚶','🚷','🚸','🚹','🚺','🚻','🚼','🚽','🚾','🚿','🛀','🛁','🛂','🛃','🛄','🛅','🛋','🛌','🛍','🛎','🛏','🛐','🛑','🛒','🛠','🛡','🛢','🛣','🛤','🛥','🛩','🛫','🛬','🛰','🛳','🛴','🛵','🛶','🛷','🛸','🛹','🤐','🤑','🤒','🤓','🤔','🤕','🤖','🤗','🤘','🤙','🤚','🤛','🤜','🤝','🤞','🤟','🤠','🤡','🤢','🤣','🤤','🤥','🤦','🤧','🤨','🤩','🤪','🤫','🤬','🤭','🤮','🤯','🤰','🤱','🤲','🤳','🤴','🤵','🤶','🤷','🤸','🤹','🤺','🤼','🤽','🤾','🥀','🥁','🥂','🥃','🥄','🥅','🥇','🥈','🥉','🥊','🥋','🥌','🥍','🥎','🥏','🥐','🥑','🥒','🥓','🥔','🥕','🥖','🥗','🥘','🥙','🥚','🥛','🥜','🥝','🥞','🥟','🥠','🥡','🥢','🥣','🥤','🥥','🥦','🥧','🥨','🥩','🥪','🥫','🥬','🥭','🥮','🥯','🥰','🥳','🥴','🥵','🥶','🥺','🥼','🥽','🥾','🥿','🦀','🦁','🦂','🦃','🦄','🦅','🦆','🦇','🦈','🦉','🦊','🦋','🦌','🦍','🦎','🦏','🦐','🦑','🦒','🦓','🦔','🦕','🦖','🦗','🦘','🦙','🦚','🦛','🦜','🦝','🦞','🦟','🦠','🦡','🦢','🦴','🦵','🦶','🦷','🦸','🦹','🧀','🧁','🧂','🧐','🧑','🧒','🧓','🧔','🧕','🧖','🧗','🧘','🧙','🧚','🧛','🧜','🧝','🧞','🧟','🧠','🧡','🧢','🧣','🧤','🧥','🧦','🧧','🧨','🧩','🧪','🧫','🧬','🧭','🧮','🧯','🧰','🧱','🧲','🧳','🧴','🧵','🧶','🧷','🧸','🧹','🧺','🧻','🧼','🧽','🧾','🧿','‼','⁉','™','ℹ','↔','↕','↖','↗','↘','↙','↩','↪','⃣','⌚','⌛','⌨','⏏','⏩','⏪','⏫','⏬','⏭','⏮','⏯','⏰','⏱','⏲','⏳','⏸','⏹','⏺','Ⓜ','▪','▫','▶','◀','◻','◼','◽','◾','☀','☁','☂','☃','☄','☎','☑','☔','☕','☘','☝','☢','☣','☦','☪','☮','☯','☸','☹','☺','♈','♉','♊','♋','♌','♍','♎','♏','♐','♑','♒','♓','♟','♠','♣','♥','♦','♨','♻','♾','♿','⚒','⚓','⚔','⚗','⚙','⚛','⚜','⚠','⚡','⚪','⚫','⚰','⚱','⚽','⚾','⛄','⛅','⛈','⛎','⛏','⛑','⛓','⛔','⛩','⛪','⛰','⛱','⛲','⛳','⛴','⛵','⛷','⛸','⛹','⛺','⛽','✂','✅','✉','✊','✋','✌','✍','✏','✒','✔','✖','✝','✡','✨','✳','✴','❄','❇','❌','❎','❓','❔','❕','❗','❣','➕','➖','➗','➡','➰','➿','⤴','⤵','⬅','⬆','⬇','⬛','⬜','⭐','⭕','〰','〽','㊗','㊙','');
5319 // END: emoji arrays
5320
5321 if ( 'entities' === $type ) {
5322 return $entities;
5323 }
5324
5325 return $partials;
5326}
5327
5328/**
5329 * Shorten a URL, to be used as link text.
5330 *
5331 * @since 1.2.0
5332 * @since 4.4.0 Moved to wp-includes/formatting.php from wp-admin/includes/misc.php and added $length param.
5333 *
5334 * @param string $url URL to shorten.
5335 * @param int $length Optional. Maximum length of the shortened URL. Default 35 characters.
5336 * @return string Shortened URL.
5337 */
5338function url_shorten( $url, $length = 35 ) {
5339 $stripped = str_replace( array( 'https://', 'http://', 'www.' ), '', $url );
5340 $short_url = untrailingslashit( $stripped );
5341
5342 if ( strlen( $short_url ) > $length ) {
5343 $short_url = substr( $short_url, 0, $length - 3 ) . '…';
5344 }
5345 return $short_url;
5346}
5347
5348/**
5349 * Sanitizes a hex color.
5350 *
5351 * Returns either '', a 3 or 6 digit hex color (with #), or nothing.
5352 * For sanitizing values without a #, see sanitize_hex_color_no_hash().
5353 *
5354 * @since 3.4.0
5355 *
5356 * @param string $color
5357 * @return string|void
5358 */
5359function sanitize_hex_color( $color ) {
5360 if ( '' === $color ) {
5361 return '';
5362 }
5363
5364 // 3 or 6 hex digits, or the empty string.
5365 if ( preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
5366 return $color;
5367 }
5368}
5369
5370/**
5371 * Sanitizes a hex color without a hash. Use sanitize_hex_color() when possible.
5372 *
5373 * Saving hex colors without a hash puts the burden of adding the hash on the
5374 * UI, which makes it difficult to use or upgrade to other color types such as
5375 * rgba, hsl, rgb, and html color names.
5376 *
5377 * Returns either '', a 3 or 6 digit hex color (without a #), or null.
5378 *
5379 * @since 3.4.0
5380 *
5381 * @param string $color
5382 * @return string|null
5383 */
5384function sanitize_hex_color_no_hash( $color ) {
5385 $color = ltrim( $color, '#' );
5386
5387 if ( '' === $color ) {
5388 return '';
5389 }
5390
5391 return sanitize_hex_color( '#' . $color ) ? $color : null;
5392}
5393
5394/**
5395 * Ensures that any hex color is properly hashed.
5396 * Otherwise, returns value untouched.
5397 *
5398 * This method should only be necessary if using sanitize_hex_color_no_hash().
5399 *
5400 * @since 3.4.0
5401 *
5402 * @param string $color
5403 * @return string
5404 */
5405function maybe_hash_hex_color( $color ) {
5406 if ( $unhashed = sanitize_hex_color_no_hash( $color ) ) {
5407 return '#' . $unhashed;
5408 }
5409
5410 return $color;
5411}