WordPress.org

Make WordPress Core

Ticket #47763: functions.php

File functions.php, 214.5 KB (added by dxd5001, 2 years ago)

Added Unicode Normalization code to wp_unique_filename function.

Line 
1<?php
2/**
3 * Main WordPress API
4 *
5 * @package WordPress
6 */
7
8require( ABSPATH . WPINC . '/option.php' );
9
10/**
11 * Convert given date string into a different format.
12 *
13 * $format should be either a PHP date format string, e.g. 'U' for a Unix
14 * timestamp, or 'G' for a Unix timestamp assuming that $date is GMT.
15 *
16 * If $translate is true then the given date and format string will
17 * be passed to date_i18n() for translation.
18 *
19 * @since 0.71
20 *
21 * @param string $format    Format of the date to return.
22 * @param string $date      Date string to convert.
23 * @param bool   $translate Whether the return date should be translated. Default true.
24 * @return string|int|bool Formatted date string or Unix timestamp. False if $date is empty.
25 */
26function mysql2date( $format, $date, $translate = true ) {
27        if ( empty( $date ) ) {
28                return false;
29        }
30
31        if ( 'G' == $format ) {
32                return strtotime( $date . ' +0000' );
33        }
34
35        $i = strtotime( $date );
36
37        if ( 'U' == $format ) {
38                return $i;
39        }
40
41        if ( $translate ) {
42                return date_i18n( $format, $i );
43        } else {
44                return date( $format, $i );
45        }
46}
47
48/**
49 * Retrieve the current time based on specified type.
50 *
51 * The 'mysql' type will return the time in the format for MySQL DATETIME field.
52 * The 'timestamp' type will return the current timestamp.
53 * Other strings will be interpreted as PHP date formats (e.g. 'Y-m-d').
54 *
55 * If $gmt is set to either '1' or 'true', then both types will use GMT time.
56 * if $gmt is false, the output is adjusted with the GMT offset in the WordPress option.
57 *
58 * @since 1.0.0
59 *
60 * @param string   $type Type of time to retrieve. Accepts 'mysql', 'timestamp', or PHP date
61 *                       format string (e.g. 'Y-m-d').
62 * @param int|bool $gmt  Optional. Whether to use GMT timezone. Default false.
63 * @return int|string Integer if $type is 'timestamp', string otherwise.
64 */
65function current_time( $type, $gmt = 0 ) {
66        switch ( $type ) {
67                case 'mysql':
68                        return ( $gmt ) ? gmdate( 'Y-m-d H:i:s' ) : gmdate( 'Y-m-d H:i:s', ( time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) ) );
69                case 'timestamp':
70                        return ( $gmt ) ? time() : time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
71                default:
72                        return ( $gmt ) ? gmdate( $type ) : gmdate( $type, time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) );
73        }
74}
75
76/**
77 * Retrieve the date in localized format, based on a sum of Unix timestamp and
78 * timezone offset in seconds.
79 *
80 * If the locale specifies the locale month and weekday, then the locale will
81 * take over the format for the date. If it isn't, then the date format string
82 * will be used instead.
83 *
84 * @since 0.71
85 *
86 * @global WP_Locale $wp_locale
87 *
88 * @param string   $dateformatstring      Format to display the date.
89 * @param int|bool $timestamp_with_offset Optional. A sum of Unix timestamp and timezone offset in seconds.
90 *                                        Default false.
91 * @param bool     $gmt                   Optional. Whether to use GMT timezone. Only applies if timestamp is
92 *                                        not provided. Default false.
93 *
94 * @return string The date, translated if locale specifies it.
95 */
96function date_i18n( $dateformatstring, $timestamp_with_offset = false, $gmt = false ) {
97        global $wp_locale;
98        $i = $timestamp_with_offset;
99
100        if ( false === $i ) {
101                $i = current_time( 'timestamp', $gmt );
102        }
103
104        /*
105         * Store original value for language with untypical grammars.
106         * See https://core.trac.wordpress.org/ticket/9396
107         */
108        $req_format = $dateformatstring;
109
110        $dateformatstring = preg_replace( '/(?<!\\\\)c/', DATE_W3C, $dateformatstring );
111        $dateformatstring = preg_replace( '/(?<!\\\\)r/', DATE_RFC2822, $dateformatstring );
112
113        if ( ( ! empty( $wp_locale->month ) ) && ( ! empty( $wp_locale->weekday ) ) ) {
114                $datemonth            = $wp_locale->get_month( date( 'm', $i ) );
115                $datemonth_abbrev     = $wp_locale->get_month_abbrev( $datemonth );
116                $dateweekday          = $wp_locale->get_weekday( date( 'w', $i ) );
117                $dateweekday_abbrev   = $wp_locale->get_weekday_abbrev( $dateweekday );
118                $datemeridiem         = $wp_locale->get_meridiem( date( 'a', $i ) );
119                $datemeridiem_capital = $wp_locale->get_meridiem( date( 'A', $i ) );
120                $dateformatstring     = ' ' . $dateformatstring;
121                $dateformatstring     = preg_replace( '/([^\\\])D/', "\\1" . backslashit( $dateweekday_abbrev ), $dateformatstring );
122                $dateformatstring     = preg_replace( '/([^\\\])F/', "\\1" . backslashit( $datemonth ), $dateformatstring );
123                $dateformatstring     = preg_replace( '/([^\\\])l/', "\\1" . backslashit( $dateweekday ), $dateformatstring );
124                $dateformatstring     = preg_replace( '/([^\\\])M/', "\\1" . backslashit( $datemonth_abbrev ), $dateformatstring );
125                $dateformatstring     = preg_replace( '/([^\\\])a/', "\\1" . backslashit( $datemeridiem ), $dateformatstring );
126                $dateformatstring     = preg_replace( '/([^\\\])A/', "\\1" . backslashit( $datemeridiem_capital ), $dateformatstring );
127
128                $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) - 1 );
129        }
130        $timezone_formats    = array( 'P', 'I', 'O', 'T', 'Z', 'e' );
131        $timezone_formats_re = implode( '|', $timezone_formats );
132        if ( preg_match( "/$timezone_formats_re/", $dateformatstring ) ) {
133                $timezone_string = get_option( 'timezone_string' );
134                if ( false === $timestamp_with_offset && $gmt ) {
135                        $timezone_string = 'UTC';
136                }
137                if ( $timezone_string ) {
138                        $timezone_object = timezone_open( $timezone_string );
139                        $date_object     = date_create( null, $timezone_object );
140                        foreach ( $timezone_formats as $timezone_format ) {
141                                if ( false !== strpos( $dateformatstring, $timezone_format ) ) {
142                                        $formatted        = date_format( $date_object, $timezone_format );
143                                        $dateformatstring = ' ' . $dateformatstring;
144                                        $dateformatstring = preg_replace( "/([^\\\])$timezone_format/", "\\1" . backslashit( $formatted ), $dateformatstring );
145                                        $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) - 1 );
146                                }
147                        }
148                } else {
149                        $offset = get_option( 'gmt_offset' );
150                        foreach ( $timezone_formats as $timezone_format ) {
151                                if ( 'I' === $timezone_format ) {
152                                        continue;
153                                }
154
155                                if ( false !== strpos( $dateformatstring, $timezone_format ) ) {
156                                        if ( 'Z' === $timezone_format ) {
157                                                $formatted = (string) ( $offset * HOUR_IN_SECONDS );
158                                        } else {
159                                                $prefix    = '';
160                                                $hours     = (int) $offset;
161                                                $separator = '';
162                                                $minutes   = abs( ( $offset - $hours ) * 60 );
163
164                                                if ( 'T' === $timezone_format ) {
165                                                        $prefix = 'GMT';
166                                                } elseif ( 'e' === $timezone_format || 'P' === $timezone_format ) {
167                                                        $separator = ':';
168                                                }
169
170                                                $formatted = sprintf( '%s%+03d%s%02d', $prefix, $hours, $separator, $minutes );
171                                        }
172
173                                        $dateformatstring = ' ' . $dateformatstring;
174                                        $dateformatstring = preg_replace( "/([^\\\])$timezone_format/", "\\1" . backslashit( $formatted ), $dateformatstring );
175                                        $dateformatstring = substr( $dateformatstring, 1 );
176                                }
177                        }
178                }
179        }
180        $j = @date( $dateformatstring, $i );
181
182        /**
183         * Filters the date formatted based on the locale.
184         *
185         * @since 2.8.0
186         *
187         * @param string $j          Formatted date string.
188         * @param string $req_format Format to display the date.
189         * @param int    $i          A sum of Unix timestamp and timezone offset in seconds.
190         * @param bool   $gmt        Whether to use GMT timezone. Only applies if timestamp was
191         *                           not provided. Default false.
192         */
193        $j = apply_filters( 'date_i18n', $j, $req_format, $i, $gmt );
194        return $j;
195}
196
197/**
198 * Determines if the date should be declined.
199 *
200 * If the locale specifies that month names require a genitive case in certain
201 * formats (like 'j F Y'), the month name will be replaced with a correct form.
202 *
203 * @since 4.4.0
204 *
205 * @global WP_Locale $wp_locale
206 *
207 * @param string $date Formatted date string.
208 * @return string The date, declined if locale specifies it.
209 */
210function wp_maybe_decline_date( $date ) {
211        global $wp_locale;
212
213        // i18n functions are not available in SHORTINIT mode
214        if ( ! function_exists( '_x' ) ) {
215                return $date;
216        }
217
218        /* translators: If months in your language require a genitive case,
219         * translate this to 'on'. Do not translate into your own language.
220         */
221        if ( 'on' === _x( 'off', 'decline months names: on or off' ) ) {
222                // Match a format like 'j F Y' or 'j. F'
223                if ( @preg_match( '#^\d{1,2}\.? [^\d ]+#u', $date ) ) {
224                        $months          = $wp_locale->month;
225                        $months_genitive = $wp_locale->month_genitive;
226
227                        foreach ( $months as $key => $month ) {
228                                $months[ $key ] = '# ' . $month . '( |$)#u';
229                        }
230
231                        foreach ( $months_genitive as $key => $month ) {
232                                $months_genitive[ $key ] = ' ' . $month . '$1';
233                        }
234
235                        $date = preg_replace( $months, $months_genitive, $date );
236                }
237        }
238
239        // Used for locale-specific rules
240        $locale = get_locale();
241
242        if ( 'ca' === $locale ) {
243                // " de abril| de agost| de octubre..." -> " d'abril| d'agost| d'octubre..."
244                $date = preg_replace( '# de ([ao])#i', " d'\\1", $date );
245        }
246
247        return $date;
248}
249
250/**
251 * Convert float number to format based on the locale.
252 *
253 * @since 2.3.0
254 *
255 * @global WP_Locale $wp_locale
256 *
257 * @param float $number   The number to convert based on locale.
258 * @param int   $decimals Optional. Precision of the number of decimal places. Default 0.
259 * @return string Converted number in string format.
260 */
261function number_format_i18n( $number, $decimals = 0 ) {
262        global $wp_locale;
263
264        if ( isset( $wp_locale ) ) {
265                $formatted = number_format( $number, absint( $decimals ), $wp_locale->number_format['decimal_point'], $wp_locale->number_format['thousands_sep'] );
266        } else {
267                $formatted = number_format( $number, absint( $decimals ) );
268        }
269
270        /**
271         * Filters the number formatted based on the locale.
272         *
273         * @since 2.8.0
274         * @since 4.9.0 The `$number` and `$decimals` parameters were added.
275         *
276         * @param string $formatted Converted number in string format.
277         * @param float  $number    The number to convert based on locale.
278         * @param int    $decimals  Precision of the number of decimal places.
279         */
280        return apply_filters( 'number_format_i18n', $formatted, $number, $decimals );
281}
282
283/**
284 * Convert number of bytes largest unit bytes will fit into.
285 *
286 * It is easier to read 1 KB than 1024 bytes and 1 MB than 1048576 bytes. Converts
287 * number of bytes to human readable number by taking the number of that unit
288 * that the bytes will go into it. Supports TB value.
289 *
290 * Please note that integers in PHP are limited to 32 bits, unless they are on
291 * 64 bit architecture, then they have 64 bit size. If you need to place the
292 * larger size then what PHP integer type will hold, then use a string. It will
293 * be converted to a double, which should always have 64 bit length.
294 *
295 * Technically the correct unit names for powers of 1024 are KiB, MiB etc.
296 *
297 * @since 2.3.0
298 *
299 * @param int|string $bytes    Number of bytes. Note max integer size for integers.
300 * @param int        $decimals Optional. Precision of number of decimal places. Default 0.
301 * @return string|false False on failure. Number string on success.
302 */
303function size_format( $bytes, $decimals = 0 ) {
304        $quant = array(
305                'TB' => TB_IN_BYTES,
306                'GB' => GB_IN_BYTES,
307                'MB' => MB_IN_BYTES,
308                'KB' => KB_IN_BYTES,
309                'B'  => 1,
310        );
311
312        if ( 0 === $bytes ) {
313                return number_format_i18n( 0, $decimals ) . ' B';
314        }
315
316        foreach ( $quant as $unit => $mag ) {
317                if ( doubleval( $bytes ) >= $mag ) {
318                        return number_format_i18n( $bytes / $mag, $decimals ) . ' ' . $unit;
319                }
320        }
321
322        return false;
323}
324
325/**
326 * Convert a duration to human readable format.
327 *
328 * @since 5.1.0
329 *
330 * @param string $duration Duration will be in string format (HH:ii:ss) OR (ii:ss),
331 *                         with a possible prepended negative sign (-).
332 * @return string|false A human readable duration string, false on failure.
333 */
334function human_readable_duration( $duration = '' ) {
335        if ( ( empty( $duration ) || ! is_string( $duration ) ) ) {
336                return false;
337        }
338
339        $duration = trim( $duration );
340
341        // Remove prepended negative sign.
342        if ( '-' === substr( $duration, 0, 1 ) ) {
343                $duration = substr( $duration, 1 );
344        }
345
346        // Extract duration parts.
347        $duration_parts = array_reverse( explode( ':', $duration ) );
348        $duration_count = count( $duration_parts );
349
350        $hour   = null;
351        $minute = null;
352        $second = null;
353
354        if ( 3 === $duration_count ) {
355                // Validate HH:ii:ss duration format.
356                if ( ! ( (bool) preg_match( '/^([0-9]+):([0-5]?[0-9]):([0-5]?[0-9])$/', $duration ) ) ) {
357                        return false;
358                }
359                // Three parts: hours, minutes & seconds.
360                list( $second, $minute, $hour ) = $duration_parts;
361        } elseif ( 2 === $duration_count ) {
362                // Validate ii:ss duration format.
363                if ( ! ( (bool) preg_match( '/^([0-5]?[0-9]):([0-5]?[0-9])$/', $duration ) ) ) {
364                        return false;
365                }
366                // Two parts: minutes & seconds.
367                list( $second, $minute ) = $duration_parts;
368        } else {
369                return false;
370        }
371
372        $human_readable_duration = array();
373
374        // Add the hour part to the string.
375        if ( is_numeric( $hour ) ) {
376                /* translators: Time duration in hour or hours. */
377                $human_readable_duration[] = sprintf( _n( '%s hour', '%s hours', $hour ), (int) $hour );
378        }
379
380        // Add the minute part to the string.
381        if ( is_numeric( $minute ) ) {
382                /* translators: Time duration in minute or minutes. */
383                $human_readable_duration[] = sprintf( _n( '%s minute', '%s minutes', $minute ), (int) $minute );
384        }
385
386        // Add the second part to the string.
387        if ( is_numeric( $second ) ) {
388                /* translators: Time duration in second or seconds. */
389                $human_readable_duration[] = sprintf( _n( '%s second', '%s seconds', $second ), (int) $second );
390        }
391
392        return implode( ', ', $human_readable_duration );
393}
394
395/**
396 * Get the week start and end from the datetime or date string from MySQL.
397 *
398 * @since 0.71
399 *
400 * @param string     $mysqlstring   Date or datetime field type from MySQL.
401 * @param int|string $start_of_week Optional. Start of the week as an integer. Default empty string.
402 * @return array Keys are 'start' and 'end'.
403 */
404function get_weekstartend( $mysqlstring, $start_of_week = '' ) {
405        // MySQL string year.
406        $my = substr( $mysqlstring, 0, 4 );
407
408        // MySQL string month.
409        $mm = substr( $mysqlstring, 8, 2 );
410
411        // MySQL string day.
412        $md = substr( $mysqlstring, 5, 2 );
413
414        // The timestamp for MySQL string day.
415        $day = mktime( 0, 0, 0, $md, $mm, $my );
416
417        // The day of the week from the timestamp.
418        $weekday = date( 'w', $day );
419
420        if ( ! is_numeric( $start_of_week ) ) {
421                $start_of_week = get_option( 'start_of_week' );
422        }
423
424        if ( $weekday < $start_of_week ) {
425                $weekday += 7;
426        }
427
428        // The most recent week start day on or before $day.
429        $start = $day - DAY_IN_SECONDS * ( $weekday - $start_of_week );
430
431        // $start + 1 week - 1 second.
432        $end = $start + WEEK_IN_SECONDS - 1;
433        return compact( 'start', 'end' );
434}
435
436/**
437 * Unserialize value only if it was serialized.
438 *
439 * @since 2.0.0
440 *
441 * @param string $original Maybe unserialized original, if is needed.
442 * @return mixed Unserialized data can be any type.
443 */
444function maybe_unserialize( $original ) {
445        if ( is_serialized( $original ) ) { // don't attempt to unserialize data that wasn't serialized going in
446                return @unserialize( $original );
447        }
448        return $original;
449}
450
451/**
452 * Check value to find if it was serialized.
453 *
454 * If $data is not an string, then returned value will always be false.
455 * Serialized data is always a string.
456 *
457 * @since 2.0.5
458 *
459 * @param string $data   Value to check to see if was serialized.
460 * @param bool   $strict Optional. Whether to be strict about the end of the string. Default true.
461 * @return bool False if not serialized and true if it was.
462 */
463function is_serialized( $data, $strict = true ) {
464        // if it isn't a string, it isn't serialized.
465        if ( ! is_string( $data ) ) {
466                return false;
467        }
468        $data = trim( $data );
469        if ( 'N;' == $data ) {
470                return true;
471        }
472        if ( strlen( $data ) < 4 ) {
473                return false;
474        }
475        if ( ':' !== $data[1] ) {
476                return false;
477        }
478        if ( $strict ) {
479                $lastc = substr( $data, -1 );
480                if ( ';' !== $lastc && '}' !== $lastc ) {
481                        return false;
482                }
483        } else {
484                $semicolon = strpos( $data, ';' );
485                $brace     = strpos( $data, '}' );
486                // Either ; or } must exist.
487                if ( false === $semicolon && false === $brace ) {
488                        return false;
489                }
490                // But neither must be in the first X characters.
491                if ( false !== $semicolon && $semicolon < 3 ) {
492                        return false;
493                }
494                if ( false !== $brace && $brace < 4 ) {
495                        return false;
496                }
497        }
498        $token = $data[0];
499        switch ( $token ) {
500                case 's':
501                        if ( $strict ) {
502                                if ( '"' !== substr( $data, -2, 1 ) ) {
503                                        return false;
504                                }
505                        } elseif ( false === strpos( $data, '"' ) ) {
506                                return false;
507                        }
508                        // or else fall through
509                case 'a':
510                case 'O':
511                        return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
512                case 'b':
513                case 'i':
514                case 'd':
515                        $end = $strict ? '$' : '';
516                        return (bool) preg_match( "/^{$token}:[0-9.E-]+;$end/", $data );
517        }
518        return false;
519}
520
521/**
522 * Check whether serialized data is of string type.
523 *
524 * @since 2.0.5
525 *
526 * @param string $data Serialized data.
527 * @return bool False if not a serialized string, true if it is.
528 */
529function is_serialized_string( $data ) {
530        // if it isn't a string, it isn't a serialized string.
531        if ( ! is_string( $data ) ) {
532                return false;
533        }
534        $data = trim( $data );
535        if ( strlen( $data ) < 4 ) {
536                return false;
537        } elseif ( ':' !== $data[1] ) {
538                return false;
539        } elseif ( ';' !== substr( $data, -1 ) ) {
540                return false;
541        } elseif ( $data[0] !== 's' ) {
542                return false;
543        } elseif ( '"' !== substr( $data, -2, 1 ) ) {
544                return false;
545        } else {
546                return true;
547        }
548}
549
550/**
551 * Serialize data, if needed.
552 *
553 * @since 2.0.5
554 *
555 * @param string|array|object $data Data that might be serialized.
556 * @return mixed A scalar data
557 */
558function maybe_serialize( $data ) {
559        if ( is_array( $data ) || is_object( $data ) ) {
560                return serialize( $data );
561        }
562
563        // Double serialization is required for backward compatibility.
564        // See https://core.trac.wordpress.org/ticket/12930
565        // Also the world will end. See WP 3.6.1.
566        if ( is_serialized( $data, false ) ) {
567                return serialize( $data );
568        }
569
570        return $data;
571}
572
573/**
574 * Retrieve post title from XMLRPC XML.
575 *
576 * If the title element is not part of the XML, then the default post title from
577 * the $post_default_title will be used instead.
578 *
579 * @since 0.71
580 *
581 * @global string $post_default_title Default XML-RPC post title.
582 *
583 * @param string $content XMLRPC XML Request content
584 * @return string Post title
585 */
586function xmlrpc_getposttitle( $content ) {
587        global $post_default_title;
588        if ( preg_match( '/<title>(.+?)<\/title>/is', $content, $matchtitle ) ) {
589                $post_title = $matchtitle[1];
590        } else {
591                $post_title = $post_default_title;
592        }
593        return $post_title;
594}
595
596/**
597 * Retrieve the post category or categories from XMLRPC XML.
598 *
599 * If the category element is not found, then the default post category will be
600 * used. The return type then would be what $post_default_category. If the
601 * category is found, then it will always be an array.
602 *
603 * @since 0.71
604 *
605 * @global string $post_default_category Default XML-RPC post category.
606 *
607 * @param string $content XMLRPC XML Request content
608 * @return string|array List of categories or category name.
609 */
610function xmlrpc_getpostcategory( $content ) {
611        global $post_default_category;
612        if ( preg_match( '/<category>(.+?)<\/category>/is', $content, $matchcat ) ) {
613                $post_category = trim( $matchcat[1], ',' );
614                $post_category = explode( ',', $post_category );
615        } else {
616                $post_category = $post_default_category;
617        }
618        return $post_category;
619}
620
621/**
622 * XMLRPC XML content without title and category elements.
623 *
624 * @since 0.71
625 *
626 * @param string $content XML-RPC XML Request content.
627 * @return string XMLRPC XML Request content without title and category elements.
628 */
629function xmlrpc_removepostdata( $content ) {
630        $content = preg_replace( '/<title>(.+?)<\/title>/si', '', $content );
631        $content = preg_replace( '/<category>(.+?)<\/category>/si', '', $content );
632        $content = trim( $content );
633        return $content;
634}
635
636/**
637 * Use RegEx to extract URLs from arbitrary content.
638 *
639 * @since 3.7.0
640 *
641 * @param string $content Content to extract URLs from.
642 * @return array URLs found in passed string.
643 */
644function wp_extract_urls( $content ) {
645        preg_match_all(
646                "#([\"']?)("
647                        . '(?:([\w-]+:)?//?)'
648                        . '[^\s()<>]+'
649                        . '[.]'
650                        . '(?:'
651                                . '\([\w\d]+\)|'
652                                . '(?:'
653                                        . "[^`!()\[\]{};:'\".,<>«»“”‘’\s]|"
654                                        . '(?:[:]\d+)?/?'
655                                . ')+'
656                        . ')'
657                . ")\\1#",
658                $content,
659                $post_links
660        );
661
662        $post_links = array_unique( array_map( 'html_entity_decode', $post_links[2] ) );
663
664        return array_values( $post_links );
665}
666
667/**
668 * Check content for video and audio links to add as enclosures.
669 *
670 * Will not add enclosures that have already been added and will
671 * remove enclosures that are no longer in the post. This is called as
672 * pingbacks and trackbacks.
673 *
674 * @since 1.5.0
675 *
676 * @global wpdb $wpdb WordPress database abstraction object.
677 *
678 * @param string $content Post Content.
679 * @param int    $post_ID Post ID.
680 */
681function do_enclose( $content, $post_ID ) {
682        global $wpdb;
683
684        //TODO: Tidy this ghetto code up and make the debug code optional
685        include_once( ABSPATH . WPINC . '/class-IXR.php' );
686
687        $post_links = array();
688
689        $pung = get_enclosed( $post_ID );
690
691        $post_links_temp = wp_extract_urls( $content );
692
693        foreach ( $pung as $link_test ) {
694                if ( ! in_array( $link_test, $post_links_temp ) ) { // link no longer in post
695                        $mids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = 'enclosure' AND meta_value LIKE %s", $post_ID, $wpdb->esc_like( $link_test ) . '%' ) );
696                        foreach ( $mids as $mid ) {
697                                delete_metadata_by_mid( 'post', $mid );
698                        }
699                }
700        }
701
702        foreach ( (array) $post_links_temp as $link_test ) {
703                if ( ! in_array( $link_test, $pung ) ) { // If we haven't pung it already
704                        $test = @parse_url( $link_test );
705                        if ( false === $test ) {
706                                continue;
707                        }
708                        if ( isset( $test['query'] ) ) {
709                                $post_links[] = $link_test;
710                        } elseif ( isset( $test['path'] ) && ( $test['path'] != '/' ) && ( $test['path'] != '' ) ) {
711                                $post_links[] = $link_test;
712                        }
713                }
714        }
715
716        /**
717         * Filters the list of enclosure links before querying the database.
718         *
719         * Allows for the addition and/or removal of potential enclosures to save
720         * to postmeta before checking the database for existing enclosures.
721         *
722         * @since 4.4.0
723         *
724         * @param array $post_links An array of enclosure links.
725         * @param int   $post_ID    Post ID.
726         */
727        $post_links = apply_filters( 'enclosure_links', $post_links, $post_ID );
728
729        foreach ( (array) $post_links as $url ) {
730                if ( $url != '' && ! $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = 'enclosure' AND meta_value LIKE %s", $post_ID, $wpdb->esc_like( $url ) . '%' ) ) ) {
731
732                        if ( $headers = wp_get_http_headers( $url ) ) {
733                                $len           = isset( $headers['content-length'] ) ? (int) $headers['content-length'] : 0;
734                                $type          = isset( $headers['content-type'] ) ? $headers['content-type'] : '';
735                                $allowed_types = array( 'video', 'audio' );
736
737                                // Check to see if we can figure out the mime type from
738                                // the extension
739                                $url_parts = @parse_url( $url );
740                                if ( false !== $url_parts ) {
741                                        $extension = pathinfo( $url_parts['path'], PATHINFO_EXTENSION );
742                                        if ( ! empty( $extension ) ) {
743                                                foreach ( wp_get_mime_types() as $exts => $mime ) {
744                                                        if ( preg_match( '!^(' . $exts . ')$!i', $extension ) ) {
745                                                                $type = $mime;
746                                                                break;
747                                                        }
748                                                }
749                                        }
750                                }
751
752                                if ( in_array( substr( $type, 0, strpos( $type, '/' ) ), $allowed_types ) ) {
753                                        add_post_meta( $post_ID, 'enclosure', "$url\n$len\n$mime\n" );
754                                }
755                        }
756                }
757        }
758}
759
760/**
761 * Retrieve HTTP Headers from URL.
762 *
763 * @since 1.5.1
764 *
765 * @param string $url        URL to retrieve HTTP headers from.
766 * @param bool   $deprecated Not Used.
767 * @return bool|string False on failure, headers on success.
768 */
769function wp_get_http_headers( $url, $deprecated = false ) {
770        if ( ! empty( $deprecated ) ) {
771                _deprecated_argument( __FUNCTION__, '2.7.0' );
772        }
773
774        $response = wp_safe_remote_head( $url );
775
776        if ( is_wp_error( $response ) ) {
777                return false;
778        }
779
780        return wp_remote_retrieve_headers( $response );
781}
782
783/**
784 * Determines whether the publish date of the current post in the loop is different
785 * from the publish date of the previous post in the loop.
786 *
787 * For more information on this and similar theme functions, check out
788 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
789 * Conditional Tags} article in the Theme Developer Handbook.
790 *
791 * @since 0.71
792 *
793 * @global string $currentday  The day of the current post in the loop.
794 * @global string $previousday The day of the previous post in the loop.
795 *
796 * @return int 1 when new day, 0 if not a new day.
797 */
798function is_new_day() {
799        global $currentday, $previousday;
800        if ( $currentday != $previousday ) {
801                return 1;
802        } else {
803                return 0;
804        }
805}
806
807/**
808 * Build URL query based on an associative and, or indexed array.
809 *
810 * This is a convenient function for easily building url queries. It sets the
811 * separator to '&' and uses _http_build_query() function.
812 *
813 * @since 2.3.0
814 *
815 * @see _http_build_query() Used to build the query
816 * @link https://secure.php.net/manual/en/function.http-build-query.php for more on what
817 *       http_build_query() does.
818 *
819 * @param array $data URL-encode key/value pairs.
820 * @return string URL-encoded string.
821 */
822function build_query( $data ) {
823        return _http_build_query( $data, null, '&', '', false );
824}
825
826/**
827 * From php.net (modified by Mark Jaquith to behave like the native PHP5 function).
828 *
829 * @since 3.2.0
830 * @access private
831 *
832 * @see https://secure.php.net/manual/en/function.http-build-query.php
833 *
834 * @param array|object  $data       An array or object of data. Converted to array.
835 * @param string        $prefix     Optional. Numeric index. If set, start parameter numbering with it.
836 *                                  Default null.
837 * @param string        $sep        Optional. Argument separator; defaults to 'arg_separator.output'.
838 *                                  Default null.
839 * @param string        $key        Optional. Used to prefix key name. Default empty.
840 * @param bool          $urlencode  Optional. Whether to use urlencode() in the result. Default true.
841 *
842 * @return string The query string.
843 */
844function _http_build_query( $data, $prefix = null, $sep = null, $key = '', $urlencode = true ) {
845        $ret = array();
846
847        foreach ( (array) $data as $k => $v ) {
848                if ( $urlencode ) {
849                        $k = urlencode( $k );
850                }
851                if ( is_int( $k ) && $prefix != null ) {
852                        $k = $prefix . $k;
853                }
854                if ( ! empty( $key ) ) {
855                        $k = $key . '%5B' . $k . '%5D';
856                }
857                if ( $v === null ) {
858                        continue;
859                } elseif ( $v === false ) {
860                        $v = '0';
861                }
862
863                if ( is_array( $v ) || is_object( $v ) ) {
864                        array_push( $ret, _http_build_query( $v, '', $sep, $k, $urlencode ) );
865                } elseif ( $urlencode ) {
866                        array_push( $ret, $k . '=' . urlencode( $v ) );
867                } else {
868                        array_push( $ret, $k . '=' . $v );
869                }
870        }
871
872        if ( null === $sep ) {
873                $sep = ini_get( 'arg_separator.output' );
874        }
875
876        return implode( $sep, $ret );
877}
878
879/**
880 * Retrieves a modified URL query string.
881 *
882 * You can rebuild the URL and append query variables to the URL query by using this function.
883 * There are two ways to use this function; either a single key and value, or an associative array.
884 *
885 * Using a single key and value:
886 *
887 *     add_query_arg( 'key', 'value', 'http://example.com' );
888 *
889 * Using an associative array:
890 *
891 *     add_query_arg( array(
892 *         'key1' => 'value1',
893 *         'key2' => 'value2',
894 *     ), 'http://example.com' );
895 *
896 * Omitting the URL from either use results in the current URL being used
897 * (the value of `$_SERVER['REQUEST_URI']`).
898 *
899 * Values are expected to be encoded appropriately with urlencode() or rawurlencode().
900 *
901 * Setting any query variable's value to boolean false removes the key (see remove_query_arg()).
902 *
903 * Important: The return value of add_query_arg() is not escaped by default. Output should be
904 * late-escaped with esc_url() or similar to help prevent vulnerability to cross-site scripting
905 * (XSS) attacks.
906 *
907 * @since 1.5.0
908 *
909 * @param string|array $key   Either a query variable key, or an associative array of query variables.
910 * @param string       $value Optional. Either a query variable value, or a URL to act upon.
911 * @param string       $url   Optional. A URL to act upon.
912 * @return string New URL query string (unescaped).
913 */
914function add_query_arg() {
915        $args = func_get_args();
916        if ( is_array( $args[0] ) ) {
917                if ( count( $args ) < 2 || false === $args[1] ) {
918                        $uri = $_SERVER['REQUEST_URI'];
919                } else {
920                        $uri = $args[1];
921                }
922        } else {
923                if ( count( $args ) < 3 || false === $args[2] ) {
924                        $uri = $_SERVER['REQUEST_URI'];
925                } else {
926                        $uri = $args[2];
927                }
928        }
929
930        if ( $frag = strstr( $uri, '#' ) ) {
931                $uri = substr( $uri, 0, -strlen( $frag ) );
932        } else {
933                $frag = '';
934        }
935
936        if ( 0 === stripos( $uri, 'http://' ) ) {
937                $protocol = 'http://';
938                $uri      = substr( $uri, 7 );
939        } elseif ( 0 === stripos( $uri, 'https://' ) ) {
940                $protocol = 'https://';
941                $uri      = substr( $uri, 8 );
942        } else {
943                $protocol = '';
944        }
945
946        if ( strpos( $uri, '?' ) !== false ) {
947                list( $base, $query ) = explode( '?', $uri, 2 );
948                $base                .= '?';
949        } elseif ( $protocol || strpos( $uri, '=' ) === false ) {
950                $base  = $uri . '?';
951                $query = '';
952        } else {
953                $base  = '';
954                $query = $uri;
955        }
956
957        wp_parse_str( $query, $qs );
958        $qs = urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string
959        if ( is_array( $args[0] ) ) {
960                foreach ( $args[0] as $k => $v ) {
961                        $qs[ $k ] = $v;
962                }
963        } else {
964                $qs[ $args[0] ] = $args[1];
965        }
966
967        foreach ( $qs as $k => $v ) {
968                if ( $v === false ) {
969                        unset( $qs[ $k ] );
970                }
971        }
972
973        $ret = build_query( $qs );
974        $ret = trim( $ret, '?' );
975        $ret = preg_replace( '#=(&|$)#', '$1', $ret );
976        $ret = $protocol . $base . $ret . $frag;
977        $ret = rtrim( $ret, '?' );
978        return $ret;
979}
980
981/**
982 * Removes an item or items from a query string.
983 *
984 * @since 1.5.0
985 *
986 * @param string|array $key   Query key or keys to remove.
987 * @param bool|string  $query Optional. When false uses the current URL. Default false.
988 * @return string New URL query string.
989 */
990function remove_query_arg( $key, $query = false ) {
991        if ( is_array( $key ) ) { // removing multiple keys
992                foreach ( $key as $k ) {
993                        $query = add_query_arg( $k, false, $query );
994                }
995                return $query;
996        }
997        return add_query_arg( $key, false, $query );
998}
999
1000/**
1001 * Returns an array of single-use query variable names that can be removed from a URL.
1002 *
1003 * @since 4.4.0
1004 *
1005 * @return array An array of parameters to remove from the URL.
1006 */
1007function wp_removable_query_args() {
1008        $removable_query_args = array(
1009                'activate',
1010                'activated',
1011                'approved',
1012                'deactivate',
1013                'deleted',
1014                'disabled',
1015                'enabled',
1016                'error',
1017                'hotkeys_highlight_first',
1018                'hotkeys_highlight_last',
1019                'locked',
1020                'message',
1021                'same',
1022                'saved',
1023                'settings-updated',
1024                'skipped',
1025                'spammed',
1026                'trashed',
1027                'unspammed',
1028                'untrashed',
1029                'update',
1030                'updated',
1031                'wp-post-new-reload',
1032        );
1033
1034        /**
1035         * Filters the list of query variables to remove.
1036         *
1037         * @since 4.2.0
1038         *
1039         * @param array $removable_query_args An array of query variables to remove from a URL.
1040         */
1041        return apply_filters( 'removable_query_args', $removable_query_args );
1042}
1043
1044/**
1045 * Walks the array while sanitizing the contents.
1046 *
1047 * @since 0.71
1048 *
1049 * @param array $array Array to walk while sanitizing contents.
1050 * @return array Sanitized $array.
1051 */
1052function add_magic_quotes( $array ) {
1053        foreach ( (array) $array as $k => $v ) {
1054                if ( is_array( $v ) ) {
1055                        $array[ $k ] = add_magic_quotes( $v );
1056                } else {
1057                        $array[ $k ] = addslashes( $v );
1058                }
1059        }
1060        return $array;
1061}
1062
1063/**
1064 * HTTP request for URI to retrieve content.
1065 *
1066 * @since 1.5.1
1067 *
1068 * @see wp_safe_remote_get()
1069 *
1070 * @param string $uri URI/URL of web page to retrieve.
1071 * @return false|string HTTP content. False on failure.
1072 */
1073function wp_remote_fopen( $uri ) {
1074        $parsed_url = @parse_url( $uri );
1075
1076        if ( ! $parsed_url || ! is_array( $parsed_url ) ) {
1077                return false;
1078        }
1079
1080        $options            = array();
1081        $options['timeout'] = 10;
1082
1083        $response = wp_safe_remote_get( $uri, $options );
1084
1085        if ( is_wp_error( $response ) ) {
1086                return false;
1087        }
1088
1089        return wp_remote_retrieve_body( $response );
1090}
1091
1092/**
1093 * Set up the WordPress query.
1094 *
1095 * @since 2.0.0
1096 *
1097 * @global WP       $wp_locale
1098 * @global WP_Query $wp_query
1099 * @global WP_Query $wp_the_query
1100 *
1101 * @param string|array $query_vars Default WP_Query arguments.
1102 */
1103function wp( $query_vars = '' ) {
1104        global $wp, $wp_query, $wp_the_query;
1105        $wp->main( $query_vars );
1106
1107        if ( ! isset( $wp_the_query ) ) {
1108                $wp_the_query = $wp_query;
1109        }
1110}
1111
1112/**
1113 * Retrieve the description for the HTTP status.
1114 *
1115 * @since 2.3.0
1116 * @since 3.9.0 Added status codes 418, 428, 429, 431, and 511.
1117 * @since 4.5.0 Added status codes 308, 421, and 451.
1118 * @since 5.1.0 Added status code 103.
1119 *
1120 * @global array $wp_header_to_desc
1121 *
1122 * @param int $code HTTP status code.
1123 * @return string Empty string if not found, or description if found.
1124 */
1125function get_status_header_desc( $code ) {
1126        global $wp_header_to_desc;
1127
1128        $code = absint( $code );
1129
1130        if ( ! isset( $wp_header_to_desc ) ) {
1131                $wp_header_to_desc = array(
1132                        100 => 'Continue',
1133                        101 => 'Switching Protocols',
1134                        102 => 'Processing',
1135                        103 => 'Early Hints',
1136
1137                        200 => 'OK',
1138                        201 => 'Created',
1139                        202 => 'Accepted',
1140                        203 => 'Non-Authoritative Information',
1141                        204 => 'No Content',
1142                        205 => 'Reset Content',
1143                        206 => 'Partial Content',
1144                        207 => 'Multi-Status',
1145                        226 => 'IM Used',
1146
1147                        300 => 'Multiple Choices',
1148                        301 => 'Moved Permanently',
1149                        302 => 'Found',
1150                        303 => 'See Other',
1151                        304 => 'Not Modified',
1152                        305 => 'Use Proxy',
1153                        306 => 'Reserved',
1154                        307 => 'Temporary Redirect',
1155                        308 => 'Permanent Redirect',
1156
1157                        400 => 'Bad Request',
1158                        401 => 'Unauthorized',
1159                        402 => 'Payment Required',
1160                        403 => 'Forbidden',
1161                        404 => 'Not Found',
1162                        405 => 'Method Not Allowed',
1163                        406 => 'Not Acceptable',
1164                        407 => 'Proxy Authentication Required',
1165                        408 => 'Request Timeout',
1166                        409 => 'Conflict',
1167                        410 => 'Gone',
1168                        411 => 'Length Required',
1169                        412 => 'Precondition Failed',
1170                        413 => 'Request Entity Too Large',
1171                        414 => 'Request-URI Too Long',
1172                        415 => 'Unsupported Media Type',
1173                        416 => 'Requested Range Not Satisfiable',
1174                        417 => 'Expectation Failed',
1175                        418 => 'I\'m a teapot',
1176                        421 => 'Misdirected Request',
1177                        422 => 'Unprocessable Entity',
1178                        423 => 'Locked',
1179                        424 => 'Failed Dependency',
1180                        426 => 'Upgrade Required',
1181                        428 => 'Precondition Required',
1182                        429 => 'Too Many Requests',
1183                        431 => 'Request Header Fields Too Large',
1184                        451 => 'Unavailable For Legal Reasons',
1185
1186                        500 => 'Internal Server Error',
1187                        501 => 'Not Implemented',
1188                        502 => 'Bad Gateway',
1189                        503 => 'Service Unavailable',
1190                        504 => 'Gateway Timeout',
1191                        505 => 'HTTP Version Not Supported',
1192                        506 => 'Variant Also Negotiates',
1193                        507 => 'Insufficient Storage',
1194                        510 => 'Not Extended',
1195                        511 => 'Network Authentication Required',
1196                );
1197        }
1198
1199        if ( isset( $wp_header_to_desc[ $code ] ) ) {
1200                return $wp_header_to_desc[ $code ];
1201        } else {
1202                return '';
1203        }
1204}
1205
1206/**
1207 * Set HTTP status header.
1208 *
1209 * @since 2.0.0
1210 * @since 4.4.0 Added the `$description` parameter.
1211 *
1212 * @see get_status_header_desc()
1213 *
1214 * @param int    $code        HTTP status code.
1215 * @param string $description Optional. A custom description for the HTTP status.
1216 */
1217function status_header( $code, $description = '' ) {
1218        if ( ! $description ) {
1219                $description = get_status_header_desc( $code );
1220        }
1221
1222        if ( empty( $description ) ) {
1223                return;
1224        }
1225
1226        $protocol      = wp_get_server_protocol();
1227        $status_header = "$protocol $code $description";
1228        if ( function_exists( 'apply_filters' ) ) {
1229
1230                /**
1231                 * Filters an HTTP status header.
1232                 *
1233                 * @since 2.2.0
1234                 *
1235                 * @param string $status_header HTTP status header.
1236                 * @param int    $code          HTTP status code.
1237                 * @param string $description   Description for the status code.
1238                 * @param string $protocol      Server protocol.
1239                 */
1240                $status_header = apply_filters( 'status_header', $status_header, $code, $description, $protocol );
1241        }
1242
1243        @header( $status_header, true, $code );
1244}
1245
1246/**
1247 * Get the header information to prevent caching.
1248 *
1249 * The several different headers cover the different ways cache prevention
1250 * is handled by different browsers
1251 *
1252 * @since 2.8.0
1253 *
1254 * @return array The associative array of header names and field values.
1255 */
1256function wp_get_nocache_headers() {
1257        $headers = array(
1258                'Expires'       => 'Wed, 11 Jan 1984 05:00:00 GMT',
1259                'Cache-Control' => 'no-cache, must-revalidate, max-age=0',
1260        );
1261
1262        if ( function_exists( 'apply_filters' ) ) {
1263                /**
1264                 * Filters the cache-controlling headers.
1265                 *
1266                 * @since 2.8.0
1267                 *
1268                 * @see wp_get_nocache_headers()
1269                 *
1270                 * @param array $headers {
1271                 *     Header names and field values.
1272                 *
1273                 *     @type string $Expires       Expires header.
1274                 *     @type string $Cache-Control Cache-Control header.
1275                 * }
1276                 */
1277                $headers = (array) apply_filters( 'nocache_headers', $headers );
1278        }
1279        $headers['Last-Modified'] = false;
1280        return $headers;
1281}
1282
1283/**
1284 * Set the headers to prevent caching for the different browsers.
1285 *
1286 * Different browsers support different nocache headers, so several
1287 * headers must be sent so that all of them get the point that no
1288 * caching should occur.
1289 *
1290 * @since 2.0.0
1291 *
1292 * @see wp_get_nocache_headers()
1293 */
1294function nocache_headers() {
1295        $headers = wp_get_nocache_headers();
1296
1297        unset( $headers['Last-Modified'] );
1298
1299        // In PHP 5.3+, make sure we are not sending a Last-Modified header.
1300        if ( function_exists( 'header_remove' ) ) {
1301                @header_remove( 'Last-Modified' );
1302        } else {
1303                // In PHP 5.2, send an empty Last-Modified header, but only as a
1304                // last resort to override a header already sent. #WP23021
1305                foreach ( headers_list() as $header ) {
1306                        if ( 0 === stripos( $header, 'Last-Modified' ) ) {
1307                                $headers['Last-Modified'] = '';
1308                                break;
1309                        }
1310                }
1311        }
1312
1313        foreach ( $headers as $name => $field_value ) {
1314                @header( "{$name}: {$field_value}" );
1315        }
1316}
1317
1318/**
1319 * Set the headers for caching for 10 days with JavaScript content type.
1320 *
1321 * @since 2.1.0
1322 */
1323function cache_javascript_headers() {
1324        $expiresOffset = 10 * DAY_IN_SECONDS;
1325
1326        header( 'Content-Type: text/javascript; charset=' . get_bloginfo( 'charset' ) );
1327        header( 'Vary: Accept-Encoding' ); // Handle proxies
1328        header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + $expiresOffset ) . ' GMT' );
1329}
1330
1331/**
1332 * Retrieve the number of database queries during the WordPress execution.
1333 *
1334 * @since 2.0.0
1335 *
1336 * @global wpdb $wpdb WordPress database abstraction object.
1337 *
1338 * @return int Number of database queries.
1339 */
1340function get_num_queries() {
1341        global $wpdb;
1342        return $wpdb->num_queries;
1343}
1344
1345/**
1346 * Whether input is yes or no.
1347 *
1348 * Must be 'y' to be true.
1349 *
1350 * @since 1.0.0
1351 *
1352 * @param string $yn Character string containing either 'y' (yes) or 'n' (no).
1353 * @return bool True if yes, false on anything else.
1354 */
1355function bool_from_yn( $yn ) {
1356        return ( strtolower( $yn ) == 'y' );
1357}
1358
1359/**
1360 * Load the feed template from the use of an action hook.
1361 *
1362 * If the feed action does not have a hook, then the function will die with a
1363 * message telling the visitor that the feed is not valid.
1364 *
1365 * It is better to only have one hook for each feed.
1366 *
1367 * @since 2.1.0
1368 *
1369 * @global WP_Query $wp_query Used to tell if the use a comment feed.
1370 */
1371function do_feed() {
1372        global $wp_query;
1373
1374        $feed = get_query_var( 'feed' );
1375
1376        // Remove the pad, if present.
1377        $feed = preg_replace( '/^_+/', '', $feed );
1378
1379        if ( $feed == '' || $feed == 'feed' ) {
1380                $feed = get_default_feed();
1381        }
1382
1383        if ( ! has_action( "do_feed_{$feed}" ) ) {
1384                wp_die( __( 'ERROR: This is not a valid feed template.' ), '', array( 'response' => 404 ) );
1385        }
1386
1387        /**
1388         * Fires once the given feed is loaded.
1389         *
1390         * The dynamic portion of the hook name, `$feed`, refers to the feed template name.
1391         * Possible values include: 'rdf', 'rss', 'rss2', and 'atom'.
1392         *
1393         * @since 2.1.0
1394         * @since 4.4.0 The `$feed` parameter was added.
1395         *
1396         * @param bool   $is_comment_feed Whether the feed is a comment feed.
1397         * @param string $feed            The feed name.
1398         */
1399        do_action( "do_feed_{$feed}", $wp_query->is_comment_feed, $feed );
1400}
1401
1402/**
1403 * Load the RDF RSS 0.91 Feed template.
1404 *
1405 * @since 2.1.0
1406 *
1407 * @see load_template()
1408 */
1409function do_feed_rdf() {
1410        load_template( ABSPATH . WPINC . '/feed-rdf.php' );
1411}
1412
1413/**
1414 * Load the RSS 1.0 Feed Template.
1415 *
1416 * @since 2.1.0
1417 *
1418 * @see load_template()
1419 */
1420function do_feed_rss() {
1421        load_template( ABSPATH . WPINC . '/feed-rss.php' );
1422}
1423
1424/**
1425 * Load either the RSS2 comment feed or the RSS2 posts feed.
1426 *
1427 * @since 2.1.0
1428 *
1429 * @see load_template()
1430 *
1431 * @param bool $for_comments True for the comment feed, false for normal feed.
1432 */
1433function do_feed_rss2( $for_comments ) {
1434        if ( $for_comments ) {
1435                load_template( ABSPATH . WPINC . '/feed-rss2-comments.php' );
1436        } else {
1437                load_template( ABSPATH . WPINC . '/feed-rss2.php' );
1438        }
1439}
1440
1441/**
1442 * Load either Atom comment feed or Atom posts feed.
1443 *
1444 * @since 2.1.0
1445 *
1446 * @see load_template()
1447 *
1448 * @param bool $for_comments True for the comment feed, false for normal feed.
1449 */
1450function do_feed_atom( $for_comments ) {
1451        if ( $for_comments ) {
1452                load_template( ABSPATH . WPINC . '/feed-atom-comments.php' );
1453        } else {
1454                load_template( ABSPATH . WPINC . '/feed-atom.php' );
1455        }
1456}
1457
1458/**
1459 * Display the robots.txt file content.
1460 *
1461 * The echo content should be with usage of the permalinks or for creating the
1462 * robots.txt file.
1463 *
1464 * @since 2.1.0
1465 */
1466function do_robots() {
1467        header( 'Content-Type: text/plain; charset=utf-8' );
1468
1469        /**
1470         * Fires when displaying the robots.txt file.
1471         *
1472         * @since 2.1.0
1473         */
1474        do_action( 'do_robotstxt' );
1475
1476        $output = "User-agent: *\n";
1477        $public = get_option( 'blog_public' );
1478        if ( '0' == $public ) {
1479                $output .= "Disallow: /\n";
1480        } else {
1481                $site_url = parse_url( site_url() );
1482                $path     = ( ! empty( $site_url['path'] ) ) ? $site_url['path'] : '';
1483                $output  .= "Disallow: $path/wp-admin/\n";
1484                $output  .= "Allow: $path/wp-admin/admin-ajax.php\n";
1485        }
1486
1487        /**
1488         * Filters the robots.txt output.
1489         *
1490         * @since 3.0.0
1491         *
1492         * @param string $output Robots.txt output.
1493         * @param bool   $public Whether the site is considered "public".
1494         */
1495        echo apply_filters( 'robots_txt', $output, $public );
1496}
1497
1498/**
1499 * Determines whether WordPress is already installed.
1500 *
1501 * The cache will be checked first. If you have a cache plugin, which saves
1502 * the cache values, then this will work. If you use the default WordPress
1503 * cache, and the database goes away, then you might have problems.
1504 *
1505 * Checks for the 'siteurl' option for whether WordPress is installed.
1506 *
1507 * For more information on this and similar theme functions, check out
1508 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
1509 * Conditional Tags} article in the Theme Developer Handbook.
1510 *
1511 * @since 2.1.0
1512 *
1513 * @global wpdb $wpdb WordPress database abstraction object.
1514 *
1515 * @return bool Whether the site is already installed.
1516 */
1517function is_blog_installed() {
1518        global $wpdb;
1519
1520        /*
1521         * Check cache first. If options table goes away and we have true
1522         * cached, oh well.
1523         */
1524        if ( wp_cache_get( 'is_blog_installed' ) ) {
1525                return true;
1526        }
1527
1528        $suppress = $wpdb->suppress_errors();
1529        if ( ! wp_installing() ) {
1530                $alloptions = wp_load_alloptions();
1531        }
1532        // If siteurl is not set to autoload, check it specifically
1533        if ( ! isset( $alloptions['siteurl'] ) ) {
1534                $installed = $wpdb->get_var( "SELECT option_value FROM $wpdb->options WHERE option_name = 'siteurl'" );
1535        } else {
1536                $installed = $alloptions['siteurl'];
1537        }
1538        $wpdb->suppress_errors( $suppress );
1539
1540        $installed = ! empty( $installed );
1541        wp_cache_set( 'is_blog_installed', $installed );
1542
1543        if ( $installed ) {
1544                return true;
1545        }
1546
1547        // If visiting repair.php, return true and let it take over.
1548        if ( defined( 'WP_REPAIRING' ) ) {
1549                return true;
1550        }
1551
1552        $suppress = $wpdb->suppress_errors();
1553
1554        /*
1555         * Loop over the WP tables. If none exist, then scratch installation is allowed.
1556         * If one or more exist, suggest table repair since we got here because the
1557         * options table could not be accessed.
1558         */
1559        $wp_tables = $wpdb->tables();
1560        foreach ( $wp_tables as $table ) {
1561                // The existence of custom user tables shouldn't suggest an insane state or prevent a clean installation.
1562                if ( defined( 'CUSTOM_USER_TABLE' ) && CUSTOM_USER_TABLE == $table ) {
1563                        continue;
1564                }
1565                if ( defined( 'CUSTOM_USER_META_TABLE' ) && CUSTOM_USER_META_TABLE == $table ) {
1566                        continue;
1567                }
1568
1569                if ( ! $wpdb->get_results( "DESCRIBE $table;" ) ) {
1570                        continue;
1571                }
1572
1573                // One or more tables exist. We are insane.
1574
1575                wp_load_translations_early();
1576
1577                // Die with a DB error.
1578                $wpdb->error = sprintf(
1579                        /* translators: %s: database repair URL */
1580                        __( 'One or more database tables are unavailable. The database may need to be <a href="%s">repaired</a>.' ),
1581                        'maint/repair.php?referrer=is_blog_installed'
1582                );
1583
1584                dead_db();
1585        }
1586
1587        $wpdb->suppress_errors( $suppress );
1588
1589        wp_cache_set( 'is_blog_installed', false );
1590
1591        return false;
1592}
1593
1594/**
1595 * Retrieve URL with nonce added to URL query.
1596 *
1597 * @since 2.0.4
1598 *
1599 * @param string     $actionurl URL to add nonce action.
1600 * @param int|string $action    Optional. Nonce action name. Default -1.
1601 * @param string     $name      Optional. Nonce name. Default '_wpnonce'.
1602 * @return string Escaped URL with nonce action added.
1603 */
1604function wp_nonce_url( $actionurl, $action = -1, $name = '_wpnonce' ) {
1605        $actionurl = str_replace( '&amp;', '&', $actionurl );
1606        return esc_html( add_query_arg( $name, wp_create_nonce( $action ), $actionurl ) );
1607}
1608
1609/**
1610 * Retrieve or display nonce hidden field for forms.
1611 *
1612 * The nonce field is used to validate that the contents of the form came from
1613 * the location on the current site and not somewhere else. The nonce does not
1614 * offer absolute protection, but should protect against most cases. It is very
1615 * important to use nonce field in forms.
1616 *
1617 * The $action and $name are optional, but if you want to have better security,
1618 * it is strongly suggested to set those two parameters. It is easier to just
1619 * call the function without any parameters, because validation of the nonce
1620 * doesn't require any parameters, but since crackers know what the default is
1621 * it won't be difficult for them to find a way around your nonce and cause
1622 * damage.
1623 *
1624 * The input name will be whatever $name value you gave. The input value will be
1625 * the nonce creation value.
1626 *
1627 * @since 2.0.4
1628 *
1629 * @param int|string $action  Optional. Action name. Default -1.
1630 * @param string     $name    Optional. Nonce name. Default '_wpnonce'.
1631 * @param bool       $referer Optional. Whether to set the referer field for validation. Default true.
1632 * @param bool       $echo    Optional. Whether to display or return hidden form field. Default true.
1633 * @return string Nonce field HTML markup.
1634 */
1635function wp_nonce_field( $action = -1, $name = '_wpnonce', $referer = true, $echo = true ) {
1636        $name        = esc_attr( $name );
1637        $nonce_field = '<input type="hidden" id="' . $name . '" name="' . $name . '" value="' . wp_create_nonce( $action ) . '" />';
1638
1639        if ( $referer ) {
1640                $nonce_field .= wp_referer_field( false );
1641        }
1642
1643        if ( $echo ) {
1644                echo $nonce_field;
1645        }
1646
1647        return $nonce_field;
1648}
1649
1650/**
1651 * Retrieve or display referer hidden field for forms.
1652 *
1653 * The referer link is the current Request URI from the server super global. The
1654 * input name is '_wp_http_referer', in case you wanted to check manually.
1655 *
1656 * @since 2.0.4
1657 *
1658 * @param bool $echo Optional. Whether to echo or return the referer field. Default true.
1659 * @return string Referer field HTML markup.
1660 */
1661function wp_referer_field( $echo = true ) {
1662        $referer_field = '<input type="hidden" name="_wp_http_referer" value="' . esc_attr( wp_unslash( $_SERVER['REQUEST_URI'] ) ) . '" />';
1663
1664        if ( $echo ) {
1665                echo $referer_field;
1666        }
1667        return $referer_field;
1668}
1669
1670/**
1671 * Retrieve or display original referer hidden field for forms.
1672 *
1673 * The input name is '_wp_original_http_referer' and will be either the same
1674 * value of wp_referer_field(), if that was posted already or it will be the
1675 * current page, if it doesn't exist.
1676 *
1677 * @since 2.0.4
1678 *
1679 * @param bool   $echo         Optional. Whether to echo the original http referer. Default true.
1680 * @param string $jump_back_to Optional. Can be 'previous' or page you want to jump back to.
1681 *                             Default 'current'.
1682 * @return string Original referer field.
1683 */
1684function wp_original_referer_field( $echo = true, $jump_back_to = 'current' ) {
1685        if ( ! $ref = wp_get_original_referer() ) {
1686                $ref = 'previous' == $jump_back_to ? wp_get_referer() : wp_unslash( $_SERVER['REQUEST_URI'] );
1687        }
1688        $orig_referer_field = '<input type="hidden" name="_wp_original_http_referer" value="' . esc_attr( $ref ) . '" />';
1689        if ( $echo ) {
1690                echo $orig_referer_field;
1691        }
1692        return $orig_referer_field;
1693}
1694
1695/**
1696 * Retrieve referer from '_wp_http_referer' or HTTP referer.
1697 *
1698 * If it's the same as the current request URL, will return false.
1699 *
1700 * @since 2.0.4
1701 *
1702 * @return false|string False on failure. Referer URL on success.
1703 */
1704function wp_get_referer() {
1705        if ( ! function_exists( 'wp_validate_redirect' ) ) {
1706                return false;
1707        }
1708
1709        $ref = wp_get_raw_referer();
1710
1711        if ( $ref && $ref !== wp_unslash( $_SERVER['REQUEST_URI'] ) && $ref !== home_url() . wp_unslash( $_SERVER['REQUEST_URI'] ) ) {
1712                return wp_validate_redirect( $ref, false );
1713        }
1714
1715        return false;
1716}
1717
1718/**
1719 * Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer.
1720 *
1721 * Do not use for redirects, use wp_get_referer() instead.
1722 *
1723 * @since 4.5.0
1724 *
1725 * @return string|false Referer URL on success, false on failure.
1726 */
1727function wp_get_raw_referer() {
1728        if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
1729                return wp_unslash( $_REQUEST['_wp_http_referer'] );
1730        } elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
1731                return wp_unslash( $_SERVER['HTTP_REFERER'] );
1732        }
1733
1734        return false;
1735}
1736
1737/**
1738 * Retrieve original referer that was posted, if it exists.
1739 *
1740 * @since 2.0.4
1741 *
1742 * @return string|false False if no original referer or original referer if set.
1743 */
1744function wp_get_original_referer() {
1745        if ( ! empty( $_REQUEST['_wp_original_http_referer'] ) && function_exists( 'wp_validate_redirect' ) ) {
1746                return wp_validate_redirect( wp_unslash( $_REQUEST['_wp_original_http_referer'] ), false );
1747        }
1748        return false;
1749}
1750
1751/**
1752 * Recursive directory creation based on full path.
1753 *
1754 * Will attempt to set permissions on folders.
1755 *
1756 * @since 2.0.1
1757 *
1758 * @param string $target Full path to attempt to create.
1759 * @return bool Whether the path was created. True if path already exists.
1760 */
1761function wp_mkdir_p( $target ) {
1762        $wrapper = null;
1763
1764        // Strip the protocol.
1765        if ( wp_is_stream( $target ) ) {
1766                list( $wrapper, $target ) = explode( '://', $target, 2 );
1767        }
1768
1769        // From php.net/mkdir user contributed notes.
1770        $target = str_replace( '//', '/', $target );
1771
1772        // Put the wrapper back on the target.
1773        if ( $wrapper !== null ) {
1774                $target = $wrapper . '://' . $target;
1775        }
1776
1777        /*
1778         * Safe mode fails with a trailing slash under certain PHP versions.
1779         * Use rtrim() instead of untrailingslashit to avoid formatting.php dependency.
1780         */
1781        $target = rtrim( $target, '/' );
1782        if ( empty( $target ) ) {
1783                $target = '/';
1784        }
1785
1786        if ( file_exists( $target ) ) {
1787                return @is_dir( $target );
1788        }
1789
1790        // We need to find the permissions of the parent folder that exists and inherit that.
1791        $target_parent = dirname( $target );
1792        while ( '.' != $target_parent && ! is_dir( $target_parent ) && dirname( $target_parent ) !== $target_parent ) {
1793                $target_parent = dirname( $target_parent );
1794        }
1795
1796        // Get the permission bits.
1797        if ( $stat = @stat( $target_parent ) ) {
1798                $dir_perms = $stat['mode'] & 0007777;
1799        } else {
1800                $dir_perms = 0777;
1801        }
1802
1803        if ( @mkdir( $target, $dir_perms, true ) ) {
1804
1805                /*
1806                 * If a umask is set that modifies $dir_perms, we'll have to re-set
1807                 * the $dir_perms correctly with chmod()
1808                 */
1809                if ( $dir_perms != ( $dir_perms & ~umask() ) ) {
1810                        $folder_parts = explode( '/', substr( $target, strlen( $target_parent ) + 1 ) );
1811                        for ( $i = 1, $c = count( $folder_parts ); $i <= $c; $i++ ) {
1812                                @chmod( $target_parent . '/' . implode( '/', array_slice( $folder_parts, 0, $i ) ), $dir_perms );
1813                        }
1814                }
1815
1816                return true;
1817        }
1818
1819        return false;
1820}
1821
1822/**
1823 * Test if a given filesystem path is absolute.
1824 *
1825 * For example, '/foo/bar', or 'c:\windows'.
1826 *
1827 * @since 2.5.0
1828 *
1829 * @param string $path File path.
1830 * @return bool True if path is absolute, false is not absolute.
1831 */
1832function path_is_absolute( $path ) {
1833        /*
1834         * Check to see if the path is a stream and check to see if its an actual
1835         * path or file as realpath() does not support stream wrappers.
1836         */
1837        if ( wp_is_stream( $path ) && ( is_dir( $path ) || is_file( $path ) ) ) {
1838                return true;
1839        }
1840
1841        /*
1842         * This is definitive if true but fails if $path does not exist or contains
1843         * a symbolic link.
1844         */
1845        if ( realpath( $path ) == $path ) {
1846                return true;
1847        }
1848
1849        if ( strlen( $path ) == 0 || $path[0] == '.' ) {
1850                return false;
1851        }
1852
1853        // Windows allows absolute paths like this.
1854        if ( preg_match( '#^[a-zA-Z]:\\\\#', $path ) ) {
1855                return true;
1856        }
1857
1858        // A path starting with / or \ is absolute; anything else is relative.
1859        return ( $path[0] == '/' || $path[0] == '\\' );
1860}
1861
1862/**
1863 * Join two filesystem paths together.
1864 *
1865 * For example, 'give me $path relative to $base'. If the $path is absolute,
1866 * then it the full path is returned.
1867 *
1868 * @since 2.5.0
1869 *
1870 * @param string $base Base path.
1871 * @param string $path Path relative to $base.
1872 * @return string The path with the base or absolute path.
1873 */
1874function path_join( $base, $path ) {
1875        if ( path_is_absolute( $path ) ) {
1876                return $path;
1877        }
1878
1879        return rtrim( $base, '/' ) . '/' . ltrim( $path, '/' );
1880}
1881
1882/**
1883 * Normalize a filesystem path.
1884 *
1885 * On windows systems, replaces backslashes with forward slashes
1886 * and forces upper-case drive letters.
1887 * Allows for two leading slashes for Windows network shares, but
1888 * ensures that all other duplicate slashes are reduced to a single.
1889 *
1890 * @since 3.9.0
1891 * @since 4.4.0 Ensures upper-case drive letters on Windows systems.
1892 * @since 4.5.0 Allows for Windows network shares.
1893 * @since 4.9.7 Allows for PHP file wrappers.
1894 *
1895 * @param string $path Path to normalize.
1896 * @return string Normalized path.
1897 */
1898function wp_normalize_path( $path ) {
1899        $wrapper = '';
1900        if ( wp_is_stream( $path ) ) {
1901                list( $wrapper, $path ) = explode( '://', $path, 2 );
1902                $wrapper               .= '://';
1903        }
1904
1905        // Standardise all paths to use /
1906        $path = str_replace( '\\', '/', $path );
1907
1908        // Replace multiple slashes down to a singular, allowing for network shares having two slashes.
1909        $path = preg_replace( '|(?<=.)/+|', '/', $path );
1910
1911        // Windows paths should uppercase the drive letter
1912        if ( ':' === substr( $path, 1, 1 ) ) {
1913                $path = ucfirst( $path );
1914        }
1915
1916        return $wrapper . $path;
1917}
1918
1919/**
1920 * Determine a writable directory for temporary files.
1921 *
1922 * Function's preference is the return value of sys_get_temp_dir(),
1923 * followed by your PHP temporary upload directory, followed by WP_CONTENT_DIR,
1924 * before finally defaulting to /tmp/
1925 *
1926 * In the event that this function does not find a writable location,
1927 * It may be overridden by the WP_TEMP_DIR constant in your wp-config.php file.
1928 *
1929 * @since 2.5.0
1930 *
1931 * @staticvar string $temp
1932 *
1933 * @return string Writable temporary directory.
1934 */
1935function get_temp_dir() {
1936        static $temp = '';
1937        if ( defined( 'WP_TEMP_DIR' ) ) {
1938                return trailingslashit( WP_TEMP_DIR );
1939        }
1940
1941        if ( $temp ) {
1942                return trailingslashit( $temp );
1943        }
1944
1945        if ( function_exists( 'sys_get_temp_dir' ) ) {
1946                $temp = sys_get_temp_dir();
1947                if ( @is_dir( $temp ) && wp_is_writable( $temp ) ) {
1948                        return trailingslashit( $temp );
1949                }
1950        }
1951
1952        $temp = ini_get( 'upload_tmp_dir' );
1953        if ( @is_dir( $temp ) && wp_is_writable( $temp ) ) {
1954                return trailingslashit( $temp );
1955        }
1956
1957        $temp = WP_CONTENT_DIR . '/';
1958        if ( is_dir( $temp ) && wp_is_writable( $temp ) ) {
1959                return $temp;
1960        }
1961
1962        return '/tmp/';
1963}
1964
1965/**
1966 * Determine if a directory is writable.
1967 *
1968 * This function is used to work around certain ACL issues in PHP primarily
1969 * affecting Windows Servers.
1970 *
1971 * @since 3.6.0
1972 *
1973 * @see win_is_writable()
1974 *
1975 * @param string $path Path to check for write-ability.
1976 * @return bool Whether the path is writable.
1977 */
1978function wp_is_writable( $path ) {
1979        if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) ) {
1980                return win_is_writable( $path );
1981        } else {
1982                return @is_writable( $path );
1983        }
1984}
1985
1986/**
1987 * Workaround for Windows bug in is_writable() function
1988 *
1989 * PHP has issues with Windows ACL's for determine if a
1990 * directory is writable or not, this works around them by
1991 * checking the ability to open files rather than relying
1992 * upon PHP to interprate the OS ACL.
1993 *
1994 * @since 2.8.0
1995 *
1996 * @see https://bugs.php.net/bug.php?id=27609
1997 * @see https://bugs.php.net/bug.php?id=30931
1998 *
1999 * @param string $path Windows path to check for write-ability.
2000 * @return bool Whether the path is writable.
2001 */
2002function win_is_writable( $path ) {
2003
2004        if ( $path[ strlen( $path ) - 1 ] == '/' ) { // if it looks like a directory, check a random file within the directory
2005                return win_is_writable( $path . uniqid( mt_rand() ) . '.tmp' );
2006        } elseif ( is_dir( $path ) ) { // If it's a directory (and not a file) check a random file within the directory
2007                return win_is_writable( $path . '/' . uniqid( mt_rand() ) . '.tmp' );
2008        }
2009        // check tmp file for read/write capabilities
2010        $should_delete_tmp_file = ! file_exists( $path );
2011        $f                      = @fopen( $path, 'a' );
2012        if ( $f === false ) {
2013                return false;
2014        }
2015        fclose( $f );
2016        if ( $should_delete_tmp_file ) {
2017                unlink( $path );
2018        }
2019        return true;
2020}
2021
2022/**
2023 * Retrieves uploads directory information.
2024 *
2025 * Same as wp_upload_dir() but "light weight" as it doesn't attempt to create the uploads directory.
2026 * Intended for use in themes, when only 'basedir' and 'baseurl' are needed, generally in all cases
2027 * when not uploading files.
2028 *
2029 * @since 4.5.0
2030 *
2031 * @see wp_upload_dir()
2032 *
2033 * @return array See wp_upload_dir() for description.
2034 */
2035function wp_get_upload_dir() {
2036        return wp_upload_dir( null, false );
2037}
2038
2039/**
2040 * Get an array containing the current upload directory's path and url.
2041 *
2042 * Checks the 'upload_path' option, which should be from the web root folder,
2043 * and if it isn't empty it will be used. If it is empty, then the path will be
2044 * 'WP_CONTENT_DIR/uploads'. If the 'UPLOADS' constant is defined, then it will
2045 * override the 'upload_path' option and 'WP_CONTENT_DIR/uploads' path.
2046 *
2047 * The upload URL path is set either by the 'upload_url_path' option or by using
2048 * the 'WP_CONTENT_URL' constant and appending '/uploads' to the path.
2049 *
2050 * If the 'uploads_use_yearmonth_folders' is set to true (checkbox if checked in
2051 * the administration settings panel), then the time will be used. The format
2052 * will be year first and then month.
2053 *
2054 * If the path couldn't be created, then an error will be returned with the key
2055 * 'error' containing the error message. The error suggests that the parent
2056 * directory is not writable by the server.
2057 *
2058 * On success, the returned array will have many indices:
2059 * 'path' - base directory and sub directory or full path to upload directory.
2060 * 'url' - base url and sub directory or absolute URL to upload directory.
2061 * 'subdir' - sub directory if uploads use year/month folders option is on.
2062 * 'basedir' - path without subdir.
2063 * 'baseurl' - URL path without subdir.
2064 * 'error' - false or error message.
2065 *
2066 * @since 2.0.0
2067 * @uses _wp_upload_dir()
2068 *
2069 * @staticvar array $cache
2070 * @staticvar array $tested_paths
2071 *
2072 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null.
2073 * @param bool   $create_dir Optional. Whether to check and create the uploads directory.
2074 *                           Default true for backward compatibility.
2075 * @param bool   $refresh_cache Optional. Whether to refresh the cache. Default false.
2076 * @return array See above for description.
2077 */
2078function wp_upload_dir( $time = null, $create_dir = true, $refresh_cache = false ) {
2079        static $cache = array(), $tested_paths = array();
2080
2081        $key = sprintf( '%d-%s', get_current_blog_id(), (string) $time );
2082
2083        if ( $refresh_cache || empty( $cache[ $key ] ) ) {
2084                $cache[ $key ] = _wp_upload_dir( $time );
2085        }
2086
2087        /**
2088         * Filters the uploads directory data.
2089         *
2090         * @since 2.0.0
2091         *
2092         * @param array $uploads Array of upload directory data with keys of 'path',
2093         *                       'url', 'subdir, 'basedir', and 'error'.
2094         */
2095        $uploads = apply_filters( 'upload_dir', $cache[ $key ] );
2096
2097        if ( $create_dir ) {
2098                $path = $uploads['path'];
2099
2100                if ( array_key_exists( $path, $tested_paths ) ) {
2101                        $uploads['error'] = $tested_paths[ $path ];
2102                } else {
2103                        if ( ! wp_mkdir_p( $path ) ) {
2104                                if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) {
2105                                        $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
2106                                } else {
2107                                        $error_path = wp_basename( $uploads['basedir'] ) . $uploads['subdir'];
2108                                }
2109
2110                                $uploads['error'] = sprintf(
2111                                        /* translators: %s: directory path */
2112                                        __( 'Unable to create directory %s. Is its parent directory writable by the server?' ),
2113                                        esc_html( $error_path )
2114                                );
2115                        }
2116
2117                        $tested_paths[ $path ] = $uploads['error'];
2118                }
2119        }
2120
2121        return $uploads;
2122}
2123
2124/**
2125 * A non-filtered, non-cached version of wp_upload_dir() that doesn't check the path.
2126 *
2127 * @since 4.5.0
2128 * @access private
2129 *
2130 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null.
2131 * @return array See wp_upload_dir()
2132 */
2133function _wp_upload_dir( $time = null ) {
2134        $siteurl     = get_option( 'siteurl' );
2135        $upload_path = trim( get_option( 'upload_path' ) );
2136
2137        if ( empty( $upload_path ) || 'wp-content/uploads' == $upload_path ) {
2138                $dir = WP_CONTENT_DIR . '/uploads';
2139        } elseif ( 0 !== strpos( $upload_path, ABSPATH ) ) {
2140                // $dir is absolute, $upload_path is (maybe) relative to ABSPATH
2141                $dir = path_join( ABSPATH, $upload_path );
2142        } else {
2143                $dir = $upload_path;
2144        }
2145
2146        if ( ! $url = get_option( 'upload_url_path' ) ) {
2147                if ( empty( $upload_path ) || ( 'wp-content/uploads' == $upload_path ) || ( $upload_path == $dir ) ) {
2148                        $url = WP_CONTENT_URL . '/uploads';
2149                } else {
2150                        $url = trailingslashit( $siteurl ) . $upload_path;
2151                }
2152        }
2153
2154        /*
2155         * Honor the value of UPLOADS. This happens as long as ms-files rewriting is disabled.
2156         * We also sometimes obey UPLOADS when rewriting is enabled -- see the next block.
2157         */
2158        if ( defined( 'UPLOADS' ) && ! ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) ) {
2159                $dir = ABSPATH . UPLOADS;
2160                $url = trailingslashit( $siteurl ) . UPLOADS;
2161        }
2162
2163        // If multisite (and if not the main site in a post-MU network)
2164        if ( is_multisite() && ! ( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
2165
2166                if ( ! get_site_option( 'ms_files_rewriting' ) ) {
2167                        /*
2168                         * If ms-files rewriting is disabled (networks created post-3.5), it is fairly
2169                         * straightforward: Append sites/%d if we're not on the main site (for post-MU
2170                         * networks). (The extra directory prevents a four-digit ID from conflicting with
2171                         * a year-based directory for the main site. But if a MU-era network has disabled
2172                         * ms-files rewriting manually, they don't need the extra directory, as they never
2173                         * had wp-content/uploads for the main site.)
2174                         */
2175
2176                        if ( defined( 'MULTISITE' ) ) {
2177                                $ms_dir = '/sites/' . get_current_blog_id();
2178                        } else {
2179                                $ms_dir = '/' . get_current_blog_id();
2180                        }
2181
2182                        $dir .= $ms_dir;
2183                        $url .= $ms_dir;
2184
2185                } elseif ( defined( 'UPLOADS' ) && ! ms_is_switched() ) {
2186                        /*
2187                         * Handle the old-form ms-files.php rewriting if the network still has that enabled.
2188                         * When ms-files rewriting is enabled, then we only listen to UPLOADS when:
2189                         * 1) We are not on the main site in a post-MU network, as wp-content/uploads is used
2190                         *    there, and
2191                         * 2) We are not switched, as ms_upload_constants() hardcodes these constants to reflect
2192                         *    the original blog ID.
2193                         *
2194                         * Rather than UPLOADS, we actually use BLOGUPLOADDIR if it is set, as it is absolute.
2195                         * (And it will be set, see ms_upload_constants().) Otherwise, UPLOADS can be used, as
2196                         * as it is relative to ABSPATH. For the final piece: when UPLOADS is used with ms-files
2197                         * rewriting in multisite, the resulting URL is /files. (#WP22702 for background.)
2198                         */
2199
2200                        if ( defined( 'BLOGUPLOADDIR' ) ) {
2201                                $dir = untrailingslashit( BLOGUPLOADDIR );
2202                        } else {
2203                                $dir = ABSPATH . UPLOADS;
2204                        }
2205                        $url = trailingslashit( $siteurl ) . 'files';
2206                }
2207        }
2208
2209        $basedir = $dir;
2210        $baseurl = $url;
2211
2212        $subdir = '';
2213        if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
2214                // Generate the yearly and monthly dirs
2215                if ( ! $time ) {
2216                        $time = current_time( 'mysql' );
2217                }
2218                $y      = substr( $time, 0, 4 );
2219                $m      = substr( $time, 5, 2 );
2220                $subdir = "/$y/$m";
2221        }
2222
2223        $dir .= $subdir;
2224        $url .= $subdir;
2225
2226        return array(
2227                'path'    => $dir,
2228                'url'     => $url,
2229                'subdir'  => $subdir,
2230                'basedir' => $basedir,
2231                'baseurl' => $baseurl,
2232                'error'   => false,
2233        );
2234}
2235
2236/**
2237 * Get a filename that is sanitized and unique for the given directory.
2238 *
2239 * If the filename is not unique, then a number will be added to the filename
2240 * before the extension, and will continue adding numbers until the filename is
2241 * unique.
2242 *
2243 * The callback is passed three parameters, the first one is the directory, the
2244 * second is the filename, and the third is the extension.
2245 *
2246 * @since 2.5.0
2247 *
2248 * @param string   $dir                      Directory.
2249 * @param string   $filename                 File name.
2250 * @param callable $unique_filename_callback Callback. Default null.
2251 * @return string New filename, if given wasn't unique.
2252 */
2253function wp_unique_filename( $dir, $filename, $unique_filename_callback = null ) {
2254        // Sanitize the file name before we begin processing.
2255        $filename = sanitize_file_name( $filename );
2256
2257        // Unicode Normalization: Normalize Form D (decomposition) to Form C (composition).
2258        if ( Normalizer::isNormalized( $filename, Normalizer::FORM_D ) ) {
2259                $filename = Normalizer::normalize( $filename, Normalizer::FORM_C );
2260        }
2261       
2262        // Separate the filename into a name and extension.
2263        $ext  = pathinfo( $filename, PATHINFO_EXTENSION );
2264        $name = pathinfo( $filename, PATHINFO_BASENAME );
2265        if ( $ext ) {
2266                $ext = '.' . $ext;
2267        }
2268
2269        // Edge case: if file is named '.ext', treat as an empty name.
2270        if ( $name === $ext ) {
2271                $name = '';
2272        }
2273
2274        /*
2275         * Increment the file number until we have a unique file to save in $dir.
2276         * Use callback if supplied.
2277         */
2278        if ( $unique_filename_callback && is_callable( $unique_filename_callback ) ) {
2279                $filename = call_user_func( $unique_filename_callback, $dir, $name, $ext );
2280        } else {
2281                $number = '';
2282
2283                // Change '.ext' to lower case.
2284                if ( $ext && strtolower( $ext ) != $ext ) {
2285                        $ext2      = strtolower( $ext );
2286                        $filename2 = preg_replace( '|' . preg_quote( $ext ) . '$|', $ext2, $filename );
2287
2288                        // Check for both lower and upper case extension or image sub-sizes may be overwritten.
2289                        while ( file_exists( $dir . "/$filename" ) || file_exists( $dir . "/$filename2" ) ) {
2290                                $new_number = (int) $number + 1;
2291                                $filename   = str_replace( array( "-$number$ext", "$number$ext" ), "-$new_number$ext", $filename );
2292                                $filename2  = str_replace( array( "-$number$ext2", "$number$ext2" ), "-$new_number$ext2", $filename2 );
2293                                $number     = $new_number;
2294                        }
2295
2296                        /**
2297                         * Filters the result when generating a unique file name.
2298                         *
2299                         * @since 4.5.0
2300                         *
2301                         * @param string        $filename                 Unique file name.
2302                         * @param string        $ext                      File extension, eg. ".png".
2303                         * @param string        $dir                      Directory path.
2304                         * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
2305                         */
2306                        return apply_filters( 'wp_unique_filename', $filename2, $ext, $dir, $unique_filename_callback );
2307                }
2308
2309                while ( file_exists( $dir . "/$filename" ) ) {
2310                        $new_number = (int) $number + 1;
2311                        if ( '' == "$number$ext" ) {
2312                                $filename = "$filename-" . $new_number;
2313                        } else {
2314                                $filename = str_replace( array( "-$number$ext", "$number$ext" ), '-' . $new_number . $ext, $filename );
2315                        }
2316                        $number = $new_number;
2317                }
2318        }
2319
2320        /** This filter is documented in wp-includes/functions.php */
2321        return apply_filters( 'wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback );
2322}
2323
2324/**
2325 * Create a file in the upload folder with given content.
2326 *
2327 * If there is an error, then the key 'error' will exist with the error message.
2328 * If success, then the key 'file' will have the unique file path, the 'url' key
2329 * will have the link to the new file. and the 'error' key will be set to false.
2330 *
2331 * This function will not move an uploaded file to the upload folder. It will
2332 * create a new file with the content in $bits parameter. If you move the upload
2333 * file, read the content of the uploaded file, and then you can give the
2334 * filename and content to this function, which will add it to the upload
2335 * folder.
2336 *
2337 * The permissions will be set on the new file automatically by this function.
2338 *
2339 * @since 2.0.0
2340 *
2341 * @param string       $name       Filename.
2342 * @param null|string  $deprecated Never used. Set to null.
2343 * @param mixed        $bits       File content
2344 * @param string       $time       Optional. Time formatted in 'yyyy/mm'. Default null.
2345 * @return array
2346 */
2347function wp_upload_bits( $name, $deprecated, $bits, $time = null ) {
2348        if ( ! empty( $deprecated ) ) {
2349                _deprecated_argument( __FUNCTION__, '2.0.0' );
2350        }
2351
2352        if ( empty( $name ) ) {
2353                return array( 'error' => __( 'Empty filename' ) );
2354        }
2355
2356        $wp_filetype = wp_check_filetype( $name );
2357        if ( ! $wp_filetype['ext'] && ! current_user_can( 'unfiltered_upload' ) ) {
2358                return array( 'error' => __( 'Sorry, this file type is not permitted for security reasons.' ) );
2359        }
2360
2361        $upload = wp_upload_dir( $time );
2362
2363        if ( $upload['error'] !== false ) {
2364                return $upload;
2365        }
2366
2367        /**
2368         * Filters whether to treat the upload bits as an error.
2369         *
2370         * Passing a non-array to the filter will effectively short-circuit preparing
2371         * the upload bits, returning that value instead.
2372         *
2373         * @since 3.0.0
2374         *
2375         * @param mixed $upload_bits_error An array of upload bits data, or a non-array error to return.
2376         */
2377        $upload_bits_error = apply_filters(
2378                'wp_upload_bits',
2379                array(
2380                        'name' => $name,
2381                        'bits' => $bits,
2382                        'time' => $time,
2383                )
2384        );
2385        if ( ! is_array( $upload_bits_error ) ) {
2386                $upload['error'] = $upload_bits_error;
2387                return $upload;
2388        }
2389
2390        $filename = wp_unique_filename( $upload['path'], $name );
2391
2392        $new_file = $upload['path'] . "/$filename";
2393        if ( ! wp_mkdir_p( dirname( $new_file ) ) ) {
2394                if ( 0 === strpos( $upload['basedir'], ABSPATH ) ) {
2395                        $error_path = str_replace( ABSPATH, '', $upload['basedir'] ) . $upload['subdir'];
2396                } else {
2397                        $error_path = wp_basename( $upload['basedir'] ) . $upload['subdir'];
2398                }
2399
2400                $message = sprintf(
2401                        /* translators: %s: directory path */
2402                        __( 'Unable to create directory %s. Is its parent directory writable by the server?' ),
2403                        $error_path
2404                );
2405                return array( 'error' => $message );
2406        }
2407
2408        $ifp = @ fopen( $new_file, 'wb' );
2409        if ( ! $ifp ) {
2410                return array( 'error' => sprintf( __( 'Could not write file %s' ), $new_file ) );
2411        }
2412
2413        @fwrite( $ifp, $bits );
2414        fclose( $ifp );
2415        clearstatcache();
2416
2417        // Set correct file permissions
2418        $stat  = @ stat( dirname( $new_file ) );
2419        $perms = $stat['mode'] & 0007777;
2420        $perms = $perms & 0000666;
2421        @ chmod( $new_file, $perms );
2422        clearstatcache();
2423
2424        // Compute the URL
2425        $url = $upload['url'] . "/$filename";
2426
2427        /** This filter is documented in wp-admin/includes/file.php */
2428        return apply_filters(
2429                'wp_handle_upload',
2430                array(
2431                        'file'  => $new_file,
2432                        'url'   => $url,
2433                        'type'  => $wp_filetype['type'],
2434                        'error' => false,
2435                ),
2436                'sideload'
2437        );
2438}
2439
2440/**
2441 * Retrieve the file type based on the extension name.
2442 *
2443 * @since 2.5.0
2444 *
2445 * @param string $ext The extension to search.
2446 * @return string|void The file type, example: audio, video, document, spreadsheet, etc.
2447 */
2448function wp_ext2type( $ext ) {
2449        $ext = strtolower( $ext );
2450
2451        $ext2type = wp_get_ext_types();
2452        foreach ( $ext2type as $type => $exts ) {
2453                if ( in_array( $ext, $exts ) ) {
2454                        return $type;
2455                }
2456        }
2457}
2458
2459/**
2460 * Retrieve the file type from the file name.
2461 *
2462 * You can optionally define the mime array, if needed.
2463 *
2464 * @since 2.0.4
2465 *
2466 * @param string $filename File name or path.
2467 * @param array  $mimes    Optional. Key is the file extension with value as the mime type.
2468 * @return array Values with extension first and mime type.
2469 */
2470function wp_check_filetype( $filename, $mimes = null ) {
2471        if ( empty( $mimes ) ) {
2472                $mimes = get_allowed_mime_types();
2473        }
2474        $type = false;
2475        $ext  = false;
2476
2477        foreach ( $mimes as $ext_preg => $mime_match ) {
2478                $ext_preg = '!\.(' . $ext_preg . ')$!i';
2479                if ( preg_match( $ext_preg, $filename, $ext_matches ) ) {
2480                        $type = $mime_match;
2481                        $ext  = $ext_matches[1];
2482                        break;
2483                }
2484        }
2485
2486        return compact( 'ext', 'type' );
2487}
2488
2489/**
2490 * Attempt to determine the real file type of a file.
2491 *
2492 * If unable to, the file name extension will be used to determine type.
2493 *
2494 * If it's determined that the extension does not match the file's real type,
2495 * then the "proper_filename" value will be set with a proper filename and extension.
2496 *
2497 * Currently this function only supports renaming images validated via wp_get_image_mime().
2498 *
2499 * @since 3.0.0
2500 *
2501 * @param string $file     Full path to the file.
2502 * @param string $filename The name of the file (may differ from $file due to $file being
2503 *                         in a tmp directory).
2504 * @param array   $mimes   Optional. Key is the file extension with value as the mime type.
2505 * @return array Values for the extension, MIME, and either a corrected filename or false
2506 *               if original $filename is valid.
2507 */
2508function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) {
2509        $proper_filename = false;
2510
2511        // Do basic extension validation and MIME mapping
2512        $wp_filetype = wp_check_filetype( $filename, $mimes );
2513        $ext         = $wp_filetype['ext'];
2514        $type        = $wp_filetype['type'];
2515
2516        // We can't do any further validation without a file to work with
2517        if ( ! file_exists( $file ) ) {
2518                return compact( 'ext', 'type', 'proper_filename' );
2519        }
2520
2521        $real_mime = false;
2522
2523        // Validate image types.
2524        if ( $type && 0 === strpos( $type, 'image/' ) ) {
2525
2526                // Attempt to figure out what type of image it actually is
2527                $real_mime = wp_get_image_mime( $file );
2528
2529                if ( $real_mime && $real_mime != $type ) {
2530                        /**
2531                         * Filters the list mapping image mime types to their respective extensions.
2532                         *
2533                         * @since 3.0.0
2534                         *
2535                         * @param  array $mime_to_ext Array of image mime types and their matching extensions.
2536                         */
2537                        $mime_to_ext = apply_filters(
2538                                'getimagesize_mimes_to_exts',
2539                                array(
2540                                        'image/jpeg' => 'jpg',
2541                                        'image/png'  => 'png',
2542                                        'image/gif'  => 'gif',
2543                                        'image/bmp'  => 'bmp',
2544                                        'image/tiff' => 'tif',
2545                                )
2546                        );
2547
2548                        // Replace whatever is after the last period in the filename with the correct extension
2549                        if ( ! empty( $mime_to_ext[ $real_mime ] ) ) {
2550                                $filename_parts = explode( '.', $filename );
2551                                array_pop( $filename_parts );
2552                                $filename_parts[] = $mime_to_ext[ $real_mime ];
2553                                $new_filename     = implode( '.', $filename_parts );
2554
2555                                if ( $new_filename != $filename ) {
2556                                        $proper_filename = $new_filename; // Mark that it changed
2557                                }
2558                                // Redefine the extension / MIME
2559                                $wp_filetype = wp_check_filetype( $new_filename, $mimes );
2560                                $ext         = $wp_filetype['ext'];
2561                                $type        = $wp_filetype['type'];
2562                        } else {
2563                                // Reset $real_mime and try validating again.
2564                                $real_mime = false;
2565                        }
2566                }
2567        }
2568
2569        // Validate files that didn't get validated during previous checks.
2570        if ( $type && ! $real_mime && extension_loaded( 'fileinfo' ) ) {
2571                $finfo     = finfo_open( FILEINFO_MIME_TYPE );
2572                $real_mime = finfo_file( $finfo, $file );
2573                finfo_close( $finfo );
2574
2575                // fileinfo often misidentifies obscure files as one of these types
2576                $nonspecific_types = array(
2577                        'application/octet-stream',
2578                        'application/encrypted',
2579                        'application/CDFV2-encrypted',
2580                        'application/zip',
2581                );
2582
2583                /*
2584                 * If $real_mime doesn't match the content type we're expecting from the file's extension,
2585                 * we need to do some additional vetting. Media types and those listed in $nonspecific_types are
2586                 * allowed some leeway, but anything else must exactly match the real content type.
2587                 */
2588                if ( in_array( $real_mime, $nonspecific_types, true ) ) {
2589                        // File is a non-specific binary type. That's ok if it's a type that generally tends to be binary.
2590                        if ( ! in_array( substr( $type, 0, strcspn( $type, '/' ) ), array( 'application', 'video', 'audio' ) ) ) {
2591                                $type = $ext = false;
2592                        }
2593                } elseif ( 0 === strpos( $real_mime, 'video/' ) || 0 === strpos( $real_mime, 'audio/' ) ) {
2594                        /*
2595                         * For these types, only the major type must match the real value.
2596                         * This means that common mismatches are forgiven: application/vnd.apple.numbers is often misidentified as application/zip,
2597                         * and some media files are commonly named with the wrong extension (.mov instead of .mp4)
2598                         */
2599                        if ( substr( $real_mime, 0, strcspn( $real_mime, '/' ) ) !== substr( $type, 0, strcspn( $type, '/' ) ) ) {
2600                                $type = $ext = false;
2601                        }
2602                } elseif ( 'text/plain' === $real_mime ) {
2603                        // A few common file types are occasionally detected as text/plain; allow those.
2604                        if ( ! in_array(
2605                                $type,
2606                                array(
2607                                        'text/plain',
2608                                        'text/csv',
2609                                        'text/richtext',
2610                                        'text/tsv',
2611                                        'text/vtt',
2612                                )
2613                        )
2614                        ) {
2615                                $type = $ext = false;
2616                        }
2617                } elseif ( 'text/rtf' === $real_mime ) {
2618                        // Special casing for RTF files.
2619                        if ( ! in_array(
2620                                $type,
2621                                array(
2622                                        'text/rtf',
2623                                        'text/plain',
2624                                        'application/rtf',
2625                                )
2626                        )
2627                        ) {
2628                                $type = $ext = false;
2629                        }
2630                } else {
2631                        if ( $type !== $real_mime ) {
2632                                /*
2633                                 * Everything else including image/* and application/*:
2634                                 * If the real content type doesn't match the file extension, assume it's dangerous.
2635                                 */
2636                                $type = $ext = false;
2637                        }
2638                }
2639        }
2640
2641        // The mime type must be allowed
2642        if ( $type ) {
2643                $allowed = get_allowed_mime_types();
2644
2645                if ( ! in_array( $type, $allowed ) ) {
2646                        $type = $ext = false;
2647                }
2648        }
2649
2650        /**
2651         * Filters the "real" file type of the given file.
2652         *
2653         * @since 3.0.0
2654         * @since 5.1.0 The $real_mime parameter was added.
2655         *
2656         * @param array       $wp_check_filetype_and_ext File data array containing 'ext', 'type', and
2657         *                                               'proper_filename' keys.
2658         * @param string      $file                      Full path to the file.
2659         * @param string      $filename                  The name of the file (may differ from $file due to
2660         *                                               $file being in a tmp directory).
2661         * @param array       $mimes                     Key is the file extension with value as the mime type.
2662         * @param string|bool $real_mime                 The actual mime type or false if the type cannot be determined.
2663         */
2664        return apply_filters( 'wp_check_filetype_and_ext', compact( 'ext', 'type', 'proper_filename' ), $file, $filename, $mimes, $real_mime );
2665}
2666
2667/**
2668 * Returns the real mime type of an image file.
2669 *
2670 * This depends on exif_imagetype() or getimagesize() to determine real mime types.
2671 *
2672 * @since 4.7.1
2673 *
2674 * @param string $file Full path to the file.
2675 * @return string|false The actual mime type or false if the type cannot be determined.
2676 */
2677function wp_get_image_mime( $file ) {
2678        /*
2679         * Use exif_imagetype() to check the mimetype if available or fall back to
2680         * getimagesize() if exif isn't avaialbe. If either function throws an Exception
2681         * we assume the file could not be validated.
2682         */
2683        try {
2684                if ( is_callable( 'exif_imagetype' ) ) {
2685                        $imagetype = exif_imagetype( $file );
2686                        $mime      = ( $imagetype ) ? image_type_to_mime_type( $imagetype ) : false;
2687                } elseif ( function_exists( 'getimagesize' ) ) {
2688                        $imagesize = getimagesize( $file );
2689                        $mime      = ( isset( $imagesize['mime'] ) ) ? $imagesize['mime'] : false;
2690                } else {
2691                        $mime = false;
2692                }
2693        } catch ( Exception $e ) {
2694                $mime = false;
2695        }
2696
2697        return $mime;
2698}
2699
2700/**
2701 * Retrieve list of mime types and file extensions.
2702 *
2703 * @since 3.5.0
2704 * @since 4.2.0 Support was added for GIMP (xcf) files.
2705 *
2706 * @return array Array of mime types keyed by the file extension regex corresponding to those types.
2707 */
2708function wp_get_mime_types() {
2709        /**
2710         * Filters the list of mime types and file extensions.
2711         *
2712         * This filter should be used to add, not remove, mime types. To remove
2713         * mime types, use the {@see 'upload_mimes'} filter.
2714         *
2715         * @since 3.5.0
2716         *
2717         * @param array $wp_get_mime_types Mime types keyed by the file extension regex
2718         *                                 corresponding to those types.
2719         */
2720        return apply_filters(
2721                'mime_types',
2722                array(
2723                        // Image formats.
2724                        'jpg|jpeg|jpe'                 => 'image/jpeg',
2725                        'gif'                          => 'image/gif',
2726                        'png'                          => 'image/png',
2727                        'bmp'                          => 'image/bmp',
2728                        'tiff|tif'                     => 'image/tiff',
2729                        'ico'                          => 'image/x-icon',
2730                        // Video formats.
2731                        'asf|asx'                      => 'video/x-ms-asf',
2732                        'wmv'                          => 'video/x-ms-wmv',
2733                        'wmx'                          => 'video/x-ms-wmx',
2734                        'wm'                           => 'video/x-ms-wm',
2735                        'avi'                          => 'video/avi',
2736                        'divx'                         => 'video/divx',
2737                        'flv'                          => 'video/x-flv',
2738                        'mov|qt'                       => 'video/quicktime',
2739                        'mpeg|mpg|mpe'                 => 'video/mpeg',
2740                        'mp4|m4v'                      => 'video/mp4',
2741                        'ogv'                          => 'video/ogg',
2742                        'webm'                         => 'video/webm',
2743                        'mkv'                          => 'video/x-matroska',
2744                        '3gp|3gpp'                     => 'video/3gpp', // Can also be audio
2745                        '3g2|3gp2'                     => 'video/3gpp2', // Can also be audio
2746                        // Text formats.
2747                        'txt|asc|c|cc|h|srt'           => 'text/plain',
2748                        'csv'                          => 'text/csv',
2749                        'tsv'                          => 'text/tab-separated-values',
2750                        'ics'                          => 'text/calendar',
2751                        'rtx'                          => 'text/richtext',
2752                        'css'                          => 'text/css',
2753                        'htm|html'                     => 'text/html',
2754                        'vtt'                          => 'text/vtt',
2755                        'dfxp'                         => 'application/ttaf+xml',
2756                        // Audio formats.
2757                        'mp3|m4a|m4b'                  => 'audio/mpeg',
2758                        'aac'                          => 'audio/aac',
2759                        'ra|ram'                       => 'audio/x-realaudio',
2760                        'wav'                          => 'audio/wav',
2761                        'ogg|oga'                      => 'audio/ogg',
2762                        'flac'                         => 'audio/flac',
2763                        'mid|midi'                     => 'audio/midi',
2764                        'wma'                          => 'audio/x-ms-wma',
2765                        'wax'                          => 'audio/x-ms-wax',
2766                        'mka'                          => 'audio/x-matroska',
2767                        // Misc application formats.
2768                        'rtf'                          => 'application/rtf',
2769                        'js'                           => 'application/javascript',
2770                        'pdf'                          => 'application/pdf',
2771                        'swf'                          => 'application/x-shockwave-flash',
2772                        'class'                        => 'application/java',
2773                        'tar'                          => 'application/x-tar',
2774                        'zip'                          => 'application/zip',
2775                        'gz|gzip'                      => 'application/x-gzip',
2776                        'rar'                          => 'application/rar',
2777                        '7z'                           => 'application/x-7z-compressed',
2778                        'exe'                          => 'application/x-msdownload',
2779                        'psd'                          => 'application/octet-stream',
2780                        'xcf'                          => 'application/octet-stream',
2781                        // MS Office formats.
2782                        'doc'                          => 'application/msword',
2783                        'pot|pps|ppt'                  => 'application/vnd.ms-powerpoint',
2784                        'wri'                          => 'application/vnd.ms-write',
2785                        'xla|xls|xlt|xlw'              => 'application/vnd.ms-excel',
2786                        'mdb'                          => 'application/vnd.ms-access',
2787                        'mpp'                          => 'application/vnd.ms-project',
2788                        'docx'                         => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
2789                        'docm'                         => 'application/vnd.ms-word.document.macroEnabled.12',
2790                        'dotx'                         => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
2791                        'dotm'                         => 'application/vnd.ms-word.template.macroEnabled.12',
2792                        'xlsx'                         => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
2793                        'xlsm'                         => 'application/vnd.ms-excel.sheet.macroEnabled.12',
2794                        'xlsb'                         => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
2795                        'xltx'                         => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
2796                        'xltm'                         => 'application/vnd.ms-excel.template.macroEnabled.12',
2797                        'xlam'                         => 'application/vnd.ms-excel.addin.macroEnabled.12',
2798                        'pptx'                         => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
2799                        'pptm'                         => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
2800                        'ppsx'                         => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
2801                        'ppsm'                         => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
2802                        'potx'                         => 'application/vnd.openxmlformats-officedocument.presentationml.template',
2803                        'potm'                         => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
2804                        'ppam'                         => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
2805                        'sldx'                         => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
2806                        'sldm'                         => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
2807                        'onetoc|onetoc2|onetmp|onepkg' => 'application/onenote',
2808                        'oxps'                         => 'application/oxps',
2809                        'xps'                          => 'application/vnd.ms-xpsdocument',
2810                        // OpenOffice formats.
2811                        'odt'                          => 'application/vnd.oasis.opendocument.text',
2812                        'odp'                          => 'application/vnd.oasis.opendocument.presentation',
2813                        'ods'                          => 'application/vnd.oasis.opendocument.spreadsheet',
2814                        'odg'                          => 'application/vnd.oasis.opendocument.graphics',
2815                        'odc'                          => 'application/vnd.oasis.opendocument.chart',
2816                        'odb'                          => 'application/vnd.oasis.opendocument.database',
2817                        'odf'                          => 'application/vnd.oasis.opendocument.formula',
2818                        // WordPerfect formats.
2819                        'wp|wpd'                       => 'application/wordperfect',
2820                        // iWork formats.
2821                        'key'                          => 'application/vnd.apple.keynote',
2822                        'numbers'                      => 'application/vnd.apple.numbers',
2823                        'pages'                        => 'application/vnd.apple.pages',
2824                )
2825        );
2826}
2827
2828/**
2829 * Retrieves the list of common file extensions and their types.
2830 *
2831 * @since 4.6.0
2832 *
2833 * @return array Array of file extensions types keyed by the type of file.
2834 */
2835function wp_get_ext_types() {
2836
2837        /**
2838         * Filters file type based on the extension name.
2839         *
2840         * @since 2.5.0
2841         *
2842         * @see wp_ext2type()
2843         *
2844         * @param array $ext2type Multi-dimensional array with extensions for a default set
2845         *                        of file types.
2846         */
2847        return apply_filters(
2848                'ext2type',
2849                array(
2850                        'image'       => array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico' ),
2851                        'audio'       => array( 'aac', 'ac3', 'aif', 'aiff', 'flac', 'm3a', 'm4a', 'm4b', 'mka', 'mp1', 'mp2', 'mp3', 'ogg', 'oga', 'ram', 'wav', 'wma' ),
2852                        'video'       => array( '3g2', '3gp', '3gpp', 'asf', 'avi', 'divx', 'dv', 'flv', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'mpv', 'ogm', 'ogv', 'qt', 'rm', 'vob', 'wmv' ),
2853                        'document'    => array( 'doc', 'docx', 'docm', 'dotm', 'odt', 'pages', 'pdf', 'xps', 'oxps', 'rtf', 'wp', 'wpd', 'psd', 'xcf' ),
2854                        'spreadsheet' => array( 'numbers', 'ods', 'xls', 'xlsx', 'xlsm', 'xlsb' ),
2855                        'interactive' => array( 'swf', 'key', 'ppt', 'pptx', 'pptm', 'pps', 'ppsx', 'ppsm', 'sldx', 'sldm', 'odp' ),
2856                        'text'        => array( 'asc', 'csv', 'tsv', 'txt' ),
2857                        'archive'     => array( 'bz2', 'cab', 'dmg', 'gz', 'rar', 'sea', 'sit', 'sqx', 'tar', 'tgz', 'zip', '7z' ),
2858                        'code'        => array( 'css', 'htm', 'html', 'php', 'js' ),
2859                )
2860        );
2861}
2862
2863/**
2864 * Retrieve list of allowed mime types and file extensions.
2865 *
2866 * @since 2.8.6
2867 *
2868 * @param int|WP_User $user Optional. User to check. Defaults to current user.
2869 * @return array Array of mime types keyed by the file extension regex corresponding
2870 *               to those types.
2871 */
2872function get_allowed_mime_types( $user = null ) {
2873        $t = wp_get_mime_types();
2874
2875        unset( $t['swf'], $t['exe'] );
2876        if ( function_exists( 'current_user_can' ) ) {
2877                $unfiltered = $user ? user_can( $user, 'unfiltered_html' ) : current_user_can( 'unfiltered_html' );
2878        }
2879
2880        if ( empty( $unfiltered ) ) {
2881                unset( $t['htm|html'], $t['js'] );
2882        }
2883
2884        /**
2885         * Filters list of allowed mime types and file extensions.
2886         *
2887         * @since 2.0.0
2888         *
2889         * @param array            $t    Mime types keyed by the file extension regex corresponding to
2890         *                               those types. 'swf' and 'exe' removed from full list. 'htm|html' also
2891         *                               removed depending on '$user' capabilities.
2892         * @param int|WP_User|null $user User ID, User object or null if not provided (indicates current user).
2893         */
2894        return apply_filters( 'upload_mimes', $t, $user );
2895}
2896
2897/**
2898 * Display "Are You Sure" message to confirm the action being taken.
2899 *
2900 * If the action has the nonce explain message, then it will be displayed
2901 * along with the "Are you sure?" message.
2902 *
2903 * @since 2.0.4
2904 *
2905 * @param string $action The nonce action.
2906 */
2907function wp_nonce_ays( $action ) {
2908        if ( 'log-out' == $action ) {
2909                $html = sprintf(
2910                        /* translators: %s: site name */
2911                        __( 'You are attempting to log out of %s' ),
2912                        get_bloginfo( 'name' )
2913                );
2914                $html       .= '</p><p>';
2915                $redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '';
2916                $html       .= sprintf(
2917                        /* translators: %s: logout URL */
2918                        __( 'Do you really want to <a href="%s">log out</a>?' ),
2919                        wp_logout_url( $redirect_to )
2920                );
2921        } else {
2922                $html = __( 'The link you followed has expired.' );
2923                if ( wp_get_referer() ) {
2924                        $html .= '</p><p>';
2925                        $html .= sprintf(
2926                                '<a href="%s">%s</a>',
2927                                esc_url( remove_query_arg( 'updated', wp_get_referer() ) ),
2928                                __( 'Please try again.' )
2929                        );
2930                }
2931        }
2932
2933        wp_die( $html, __( 'Something went wrong.' ), 403 );
2934}
2935
2936/**
2937 * Kills WordPress execution and displays HTML page with an error message.
2938 *
2939 * This function complements the `die()` PHP function. The difference is that
2940 * HTML will be displayed to the user. It is recommended to use this function
2941 * only when the execution should not continue any further. It is not recommended
2942 * to call this function very often, and try to handle as many errors as possible
2943 * silently or more gracefully.
2944 *
2945 * As a shorthand, the desired HTTP response code may be passed as an integer to
2946 * the `$title` parameter (the default title would apply) or the `$args` parameter.
2947 *
2948 * @since 2.0.4
2949 * @since 4.1.0 The `$title` and `$args` parameters were changed to optionally accept
2950 *              an integer to be used as the response code.
2951 * @since 5.1.0 The `$link_url`, `$link_text`, and `$exit` arguments were added.
2952 *
2953 * @global WP_Query $wp_query Global WP_Query instance.
2954 *
2955 * @param string|WP_Error  $message Optional. Error message. If this is a WP_Error object,
2956 *                                  and not an Ajax or XML-RPC request, the error's messages are used.
2957 *                                  Default empty.
2958 * @param string|int       $title   Optional. Error title. If `$message` is a `WP_Error` object,
2959 *                                  error data with the key 'title' may be used to specify the title.
2960 *                                  If `$title` is an integer, then it is treated as the response
2961 *                                  code. Default empty.
2962 * @param string|array|int $args {
2963 *     Optional. Arguments to control behavior. If `$args` is an integer, then it is treated
2964 *     as the response code. Default empty array.
2965 *
2966 *     @type int    $response       The HTTP response code. Default 200 for Ajax requests, 500 otherwise.
2967 *     @type string $link_url       A URL to include a link to. Only works in combination with $link_text.
2968 *                                  Default empty string.
2969 *     @type string $link_text      A label for the link to include. Only works in combination with $link_url.
2970 *                                  Default empty string.
2971 *     @type bool   $back_link      Whether to include a link to go back. Default false.
2972 *     @type string $text_direction The text direction. This is only useful internally, when WordPress
2973 *                                  is still loading and the site's locale is not set up yet. Accepts 'rtl'.
2974 *                                  Default is the value of is_rtl().
2975 *     @type string $code           Error code to use. Default is 'wp_die', or the main error code if $message
2976 *                                  is a WP_Error.
2977 *     @type bool   $exit           Whether to exit the process after completion. Default true.
2978 * }
2979 */
2980function wp_die( $message = '', $title = '', $args = array() ) {
2981        global $wp_query;
2982
2983        if ( is_int( $args ) ) {
2984                $args = array( 'response' => $args );
2985        } elseif ( is_int( $title ) ) {
2986                $args  = array( 'response' => $title );
2987                $title = '';
2988        }
2989
2990        if ( wp_doing_ajax() ) {
2991                /**
2992                 * Filters the callback for killing WordPress execution for Ajax requests.
2993                 *
2994                 * @since 3.4.0
2995                 *
2996                 * @param callable $function Callback function name.
2997                 */
2998                $function = apply_filters( 'wp_die_ajax_handler', '_ajax_wp_die_handler' );
2999        } elseif ( wp_is_json_request() ) {
3000                /**
3001                 * Filters the callback for killing WordPress execution for JSON requests.
3002                 *
3003                 * @since 5.1.0
3004                 *
3005                 * @param callable $function Callback function name.
3006                 */
3007                $function = apply_filters( 'wp_die_json_handler', '_json_wp_die_handler' );
3008        } elseif ( wp_is_jsonp_request() ) {
3009                /**
3010                 * Filters the callback for killing WordPress execution for JSONP requests.
3011                 *
3012                 * @since 5.2.0
3013                 *
3014                 * @param callable $function Callback function name.
3015                 */
3016                $function = apply_filters( 'wp_die_jsonp_handler', '_jsonp_wp_die_handler' );
3017        } elseif ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
3018                /**
3019                 * Filters the callback for killing WordPress execution for XML-RPC requests.
3020                 *
3021                 * @since 3.4.0
3022                 *
3023                 * @param callable $function Callback function name.
3024                 */
3025                $function = apply_filters( 'wp_die_xmlrpc_handler', '_xmlrpc_wp_die_handler' );
3026        } elseif ( wp_is_xml_request()
3027                || isset( $wp_query ) &&
3028                        ( function_exists( 'is_feed' ) && is_feed()
3029                        || function_exists( 'is_comment_feed' ) && is_comment_feed()
3030                        || function_exists( 'is_trackback' ) && is_trackback() ) ) {
3031                /**
3032                 * Filters the callback for killing WordPress execution for XML requests.
3033                 *
3034                 * @since 5.2.0
3035                 *
3036                 * @param callable $function Callback function name.
3037                 */
3038                $function = apply_filters( 'wp_die_xml_handler', '_xml_wp_die_handler' );
3039        } else {
3040                /**
3041                 * Filters the callback for killing WordPress execution for all non-Ajax, non-JSON, non-XML requests.
3042                 *
3043                 * @since 3.0.0
3044                 *
3045                 * @param callable $function Callback function name.
3046                 */
3047                $function = apply_filters( 'wp_die_handler', '_default_wp_die_handler' );
3048        }
3049
3050        call_user_func( $function, $message, $title, $args );
3051}
3052
3053/**
3054 * Kills WordPress execution and displays HTML page with an error message.
3055 *
3056 * This is the default handler for wp_die(). If you want a custom one,
3057 * you can override this using the {@see 'wp_die_handler'} filter in wp_die().
3058 *
3059 * @since 3.0.0
3060 * @access private
3061 *
3062 * @param string|WP_Error $message Error message or WP_Error object.
3063 * @param string          $title   Optional. Error title. Default empty.
3064 * @param string|array    $args    Optional. Arguments to control behavior. Default empty array.
3065 */
3066function _default_wp_die_handler( $message, $title = '', $args = array() ) {
3067        list( $message, $title, $r ) = _wp_die_process_input( $message, $title, $args );
3068
3069        if ( is_string( $message ) ) {
3070                if ( ! empty( $r['additional_errors'] ) ) {
3071                        $message = array_merge(
3072                                array( $message ),
3073                                wp_list_pluck( $r['additional_errors'], 'message' )
3074                        );
3075                        $message = "<ul>\n\t\t<li>" . join( "</li>\n\t\t<li>", $message ) . "</li>\n\t</ul>";
3076                } else {
3077                        $message = "<p>$message</p>";
3078                }
3079        }
3080
3081        $have_gettext = function_exists( '__' );
3082
3083        if ( ! empty( $r['link_url'] ) && ! empty( $r['link_text'] ) ) {
3084                $link_url = $r['link_url'];
3085                if ( function_exists( 'esc_url' ) ) {
3086                        $link_url = esc_url( $link_url );
3087                }
3088                $link_text = $r['link_text'];
3089                $message  .= "\n<p><a href='{$link_url}'>{$link_text}</a></p>";
3090        }
3091
3092        if ( isset( $r['back_link'] ) && $r['back_link'] ) {
3093                $back_text = $have_gettext ? __( '&laquo; Back' ) : '&laquo; Back';
3094                $message  .= "\n<p><a href='javascript:history.back()'>$back_text</a></p>";
3095        }
3096
3097        if ( ! did_action( 'admin_head' ) ) :
3098                if ( ! headers_sent() ) {
3099                        header( 'Content-Type: text/html; charset=utf-8' );
3100                        status_header( $r['response'] );
3101                        nocache_headers();
3102                }
3103
3104                $text_direction = $r['text_direction'];
3105                if ( function_exists( 'language_attributes' ) && function_exists( 'is_rtl' ) ) {
3106                        $dir_attr = get_language_attributes();
3107                } else {
3108                        $dir_attr = "dir='$text_direction'";
3109                }
3110                ?>
3111<!DOCTYPE html>
3112<html xmlns="http://www.w3.org/1999/xhtml" <?php echo $dir_attr; ?>>
3113<head>
3114        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
3115        <meta name="viewport" content="width=device-width">
3116                <?php
3117                if ( function_exists( 'wp_no_robots' ) ) {
3118                        wp_no_robots();
3119                }
3120                ?>
3121        <title><?php echo $title; ?></title>
3122        <style type="text/css">
3123                html {
3124                        background: #f1f1f1;
3125                }
3126                body {
3127                        background: #fff;
3128                        color: #444;
3129                        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
3130                        margin: 2em auto;
3131                        padding: 1em 2em;
3132                        max-width: 700px;
3133                        -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.13);
3134                        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.13);
3135                }
3136                h1 {
3137                        border-bottom: 1px solid #dadada;
3138                        clear: both;
3139                        color: #666;
3140                        font-size: 24px;
3141                        margin: 30px 0 0 0;
3142                        padding: 0;
3143                        padding-bottom: 7px;
3144                }
3145                #error-page {
3146                        margin-top: 50px;
3147                }
3148                #error-page p {
3149                        font-size: 14px;
3150                        line-height: 1.5;
3151                        margin: 25px 0 20px;
3152                }
3153                #error-page code {
3154                        font-family: Consolas, Monaco, monospace;
3155                }
3156                ul li {
3157                        margin-bottom: 10px;
3158                        font-size: 14px ;
3159                }
3160                a {
3161                        color: #0073aa;
3162                }
3163                a:hover,
3164                a:active {
3165                        color: #00a0d2;
3166                }
3167                a:focus {
3168                        color: #124964;
3169                        -webkit-box-shadow:
3170                                0 0 0 1px #5b9dd9,
3171                                0 0 2px 1px rgba(30, 140, 190, 0.8);
3172                        box-shadow:
3173                                0 0 0 1px #5b9dd9,
3174                                0 0 2px 1px rgba(30, 140, 190, 0.8);
3175                        outline: none;
3176                }
3177                .button {
3178                        background: #f7f7f7;
3179                        border: 1px solid #ccc;
3180                        color: #555;
3181                        display: inline-block;
3182                        text-decoration: none;
3183                        font-size: 13px;
3184                        line-height: 26px;
3185                        height: 28px;
3186                        margin: 0;
3187                        padding: 0 10px 1px;
3188                        cursor: pointer;
3189                        -webkit-border-radius: 3px;
3190                        -webkit-appearance: none;
3191                        border-radius: 3px;
3192                        white-space: nowrap;
3193                        -webkit-box-sizing: border-box;
3194                        -moz-box-sizing:    border-box;
3195                        box-sizing:         border-box;
3196
3197                        -webkit-box-shadow: 0 1px 0 #ccc;
3198                        box-shadow: 0 1px 0 #ccc;
3199                         vertical-align: top;
3200                }
3201
3202                .button.button-large {
3203                        height: 30px;
3204                        line-height: 28px;
3205                        padding: 0 12px 2px;
3206                }
3207
3208                .button:hover,
3209                .button:focus {
3210                        background: #fafafa;
3211                        border-color: #999;
3212                        color: #23282d;
3213                }
3214
3215                .button:focus {
3216                        border-color: #5b9dd9;
3217                        -webkit-box-shadow: 0 0 3px rgba(0, 115, 170, 0.8);
3218                        box-shadow: 0 0 3px rgba(0, 115, 170, 0.8);
3219                        outline: none;
3220                }
3221
3222                .button:active {
3223                        background: #eee;
3224                        border-color: #999;
3225                         -webkit-box-shadow: inset 0 2px 5px -3px rgba(0, 0, 0, 0.5);
3226                         box-shadow: inset 0 2px 5px -3px rgba(0, 0, 0, 0.5);
3227                         -webkit-transform: translateY(1px);
3228                         -ms-transform: translateY(1px);
3229                         transform: translateY(1px);
3230                }
3231
3232                <?php
3233                if ( 'rtl' == $text_direction ) {
3234                        echo 'body { font-family: Tahoma, Arial; }';
3235                }
3236                ?>
3237        </style>
3238</head>
3239<body id="error-page">
3240<?php endif; // ! did_action( 'admin_head' ) ?>
3241        <?php echo $message; ?>
3242</body>
3243</html>
3244        <?php
3245        if ( $r['exit'] ) {
3246                die();
3247        }
3248}
3249
3250/**
3251 * Kills WordPress execution and displays Ajax response with an error message.
3252 *
3253 * This is the handler for wp_die() when processing Ajax requests.
3254 *
3255 * @since 3.4.0
3256 * @access private
3257 *
3258 * @param string       $message Error message.
3259 * @param string       $title   Optional. Error title (unused). Default empty.
3260 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3261 */
3262function _ajax_wp_die_handler( $message, $title = '', $args = array() ) {
3263        // Set default 'response' to 200 for AJAX requests.
3264        $args = wp_parse_args(
3265                $args,
3266                array( 'response' => 200 )
3267        );
3268
3269        list( $message, $title, $r ) = _wp_die_process_input( $message, $title, $args );
3270
3271        if ( ! headers_sent() ) {
3272                // This is intentional. For backward-compatibility, support passing null here.
3273                if ( null !== $args['response'] ) {
3274                        status_header( $r['response'] );
3275                }
3276                nocache_headers();
3277        }
3278
3279        if ( is_scalar( $message ) ) {
3280                $message = (string) $message;
3281        } else {
3282                $message = '0';
3283        }
3284
3285        if ( $r['exit'] ) {
3286                die( $message );
3287        }
3288
3289        echo $message;
3290}
3291
3292/**
3293 * Kills WordPress execution and displays JSON response with an error message.
3294 *
3295 * This is the handler for wp_die() when processing JSON requests.
3296 *
3297 * @since 5.1.0
3298 * @access private
3299 *
3300 * @param string       $message Error message.
3301 * @param string       $title   Optional. Error title. Default empty.
3302 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3303 */
3304function _json_wp_die_handler( $message, $title = '', $args = array() ) {
3305        list( $message, $title, $r ) = _wp_die_process_input( $message, $title, $args );
3306
3307        $data = array(
3308                'code'              => $r['code'],
3309                'message'           => $message,
3310                'data'              => array(
3311                        'status' => $r['response'],
3312                ),
3313                'additional_errors' => $r['additional_errors'],
3314        );
3315
3316        if ( ! headers_sent() ) {
3317                header( 'Content-Type: application/json; charset=utf-8' );
3318                if ( null !== $r['response'] ) {
3319                        status_header( $r['response'] );
3320                }
3321                nocache_headers();
3322        }
3323
3324        echo wp_json_encode( $data );
3325        if ( $r['exit'] ) {
3326                die();
3327        }
3328}
3329
3330/**
3331 * Kills WordPress execution and displays JSONP response with an error message.
3332 *
3333 * This is the handler for wp_die() when processing JSONP requests.
3334 *
3335 * @since 5.2.0
3336 * @access private
3337 *
3338 * @param string       $message Error message.
3339 * @param string       $title   Optional. Error title. Default empty.
3340 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3341 */
3342function _jsonp_wp_die_handler( $message, $title = '', $args = array() ) {
3343        list( $message, $title, $r ) = _wp_die_process_input( $message, $title, $args );
3344
3345        $data = array(
3346                'code'              => $r['code'],
3347                'message'           => $message,
3348                'data'              => array(
3349                        'status' => $r['response'],
3350                ),
3351                'additional_errors' => $r['additional_errors'],
3352        );
3353
3354        if ( ! headers_sent() ) {
3355                header( 'Content-Type: application/javascript; charset=utf-8' );
3356                header( 'X-Content-Type-Options: nosniff' );
3357                header( 'X-Robots-Tag: noindex' );
3358                if ( null !== $r['response'] ) {
3359                        status_header( $r['response'] );
3360                }
3361                nocache_headers();
3362        }
3363
3364        $result         = wp_json_encode( $data );
3365        $jsonp_callback = $_GET['_jsonp'];
3366        echo '/**/' . $jsonp_callback . '(' . $result . ')';
3367        if ( $r['exit'] ) {
3368                die();
3369        }
3370}
3371
3372/**
3373 * Kills WordPress execution and displays XML response with an error message.
3374 *
3375 * This is the handler for wp_die() when processing XMLRPC requests.
3376 *
3377 * @since 3.2.0
3378 * @access private
3379 *
3380 * @global wp_xmlrpc_server $wp_xmlrpc_server
3381 *
3382 * @param string       $message Error message.
3383 * @param string       $title   Optional. Error title. Default empty.
3384 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3385 */
3386function _xmlrpc_wp_die_handler( $message, $title = '', $args = array() ) {
3387        global $wp_xmlrpc_server;
3388
3389        list( $message, $title, $r ) = _wp_die_process_input( $message, $title, $args );
3390
3391        if ( ! headers_sent() ) {
3392                nocache_headers();
3393        }
3394
3395        if ( $wp_xmlrpc_server ) {
3396                $error = new IXR_Error( $r['response'], $message );
3397                $wp_xmlrpc_server->output( $error->getXml() );
3398        }
3399        if ( $r['exit'] ) {
3400                die();
3401        }
3402}
3403
3404/**
3405 * Kills WordPress execution and displays XML response with an error message.
3406 *
3407 * This is the handler for wp_die() when processing XML requests.
3408 *
3409 * @since 5.2.0
3410 * @access private
3411 *
3412 * @param string       $message Error message.
3413 * @param string       $title   Optional. Error title. Default empty.
3414 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3415 */
3416function _xml_wp_die_handler( $message, $title = '', $args = array() ) {
3417        list( $message, $title, $r ) = _wp_die_process_input( $message, $title, $args );
3418
3419        $message = htmlspecialchars( $message );
3420        $title   = htmlspecialchars( $title );
3421
3422        $xml = <<<EOD
3423<error>
3424    <code>{$r['code']}</code>
3425    <title><![CDATA[{$title}]]></title>
3426    <message><![CDATA[{$message}]]></message>
3427    <data>
3428        <status>{$r['response']}</status>
3429    </data>
3430</error>
3431
3432EOD;
3433
3434        if ( ! headers_sent() ) {
3435                header( 'Content-Type: text/xml; charset=utf-8' );
3436                if ( null !== $r['response'] ) {
3437                        status_header( $r['response'] );
3438                }
3439                nocache_headers();
3440        }
3441
3442        echo $xml;
3443        if ( $r['exit'] ) {
3444                die();
3445        }
3446}
3447
3448/**
3449 * Kills WordPress execution and displays an error message.
3450 *
3451 * This is the handler for wp_die() when processing APP requests.
3452 *
3453 * @since 3.4.0
3454 * @since 5.1.0 Added the $title and $args parameters.
3455 * @access private
3456 *
3457 * @param string       $message Optional. Response to print. Default empty.
3458 * @param string       $title   Optional. Error title (unused). Default empty.
3459 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3460 */
3461function _scalar_wp_die_handler( $message = '', $title = '', $args = array() ) {
3462        list( $message, $title, $r ) = _wp_die_process_input( $message, $title, $args );
3463
3464        if ( $r['exit'] ) {
3465                if ( is_scalar( $message ) ) {
3466                        die( (string) $message );
3467                }
3468                die();
3469        }
3470
3471        if ( is_scalar( $message ) ) {
3472                echo (string) $message;
3473        }
3474}
3475
3476/**
3477 * Processes arguments passed to wp_die() consistently for its handlers.
3478 *
3479 * @since 5.1.0
3480 * @access private
3481 *
3482 * @param string       $message Error message.
3483 * @param string       $title   Optional. Error title. Default empty.
3484 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
3485 * @return array List of processed $message string, $title string, and $args array.
3486 */
3487function _wp_die_process_input( $message, $title = '', $args = array() ) {
3488        $defaults = array(
3489                'response'          => 0,
3490                'code'              => '',
3491                'exit'              => true,
3492                'back_link'         => false,
3493                'link_url'          => '',
3494                'link_text'         => '',
3495                'text_direction'    => '',
3496                'additional_errors' => array(),
3497        );
3498
3499        $args = wp_parse_args( $args, $defaults );
3500
3501        if ( function_exists( 'is_wp_error' ) && is_wp_error( $message ) ) {
3502                if ( ! empty( $message->errors ) ) {
3503                        $errors = array();
3504                        foreach ( (array) $message->errors as $error_code => $error_messages ) {
3505                                foreach ( (array) $error_messages as $error_message ) {
3506                                        $errors[] = array(
3507                                                'code'    => $error_code,
3508                                                'message' => $error_message,
3509                                                'data'    => $message->get_error_data( $error_code ),
3510                                        );
3511                                }
3512                        }
3513
3514                        $message = $errors[0]['message'];
3515                        if ( empty( $args['code'] ) ) {
3516                                $args['code'] = $errors[0]['code'];
3517                        }
3518                        if ( empty( $args['response'] ) && is_array( $errors[0]['data'] ) && ! empty( $errors[0]['data']['status'] ) ) {
3519                                $args['response'] = $errors[0]['data']['status'];
3520                        }
3521                        if ( empty( $title ) && is_array( $errors[0]['data'] ) && ! empty( $errors[0]['data']['title'] ) ) {
3522                                $title = $errors[0]['data']['title'];
3523                        }
3524
3525                        unset( $errors[0] );
3526                        $args['additional_errors'] = array_values( $errors );
3527                } else {
3528                        $message = '';
3529                }
3530        }
3531
3532        $have_gettext = function_exists( '__' );
3533
3534        // The $title and these specific $args must always have a non-empty value.
3535        if ( empty( $args['code'] ) ) {
3536                $args['code'] = 'wp_die';
3537        }
3538        if ( empty( $args['response'] ) ) {
3539                $args['response'] = 500;
3540        }
3541        if ( empty( $title ) ) {
3542                $title = $have_gettext ? __( 'WordPress &rsaquo; Error' ) : 'WordPress &rsaquo; Error';
3543        }
3544        if ( empty( $args['text_direction'] ) || ! in_array( $args['text_direction'], array( 'ltr', 'rtl' ), true ) ) {
3545                $args['text_direction'] = 'ltr';
3546                if ( function_exists( 'is_rtl' ) && is_rtl() ) {
3547                        $args['text_direction'] = 'rtl';
3548                }
3549        }
3550
3551        return array( $message, $title, $args );
3552}
3553
3554/**
3555 * Encode a variable into JSON, with some sanity checks.
3556 *
3557 * @since 4.1.0
3558 *
3559 * @param mixed $data    Variable (usually an array or object) to encode as JSON.
3560 * @param int   $options Optional. Options to be passed to json_encode(). Default 0.
3561 * @param int   $depth   Optional. Maximum depth to walk through $data. Must be
3562 *                       greater than 0. Default 512.
3563 * @return string|false The JSON encoded string, or false if it cannot be encoded.
3564 */
3565function wp_json_encode( $data, $options = 0, $depth = 512 ) {
3566        /*
3567         * json_encode() has had extra params added over the years.
3568         * $options was added in 5.3, and $depth in 5.5.
3569         * We need to make sure we call it with the correct arguments.
3570         */
3571        if ( version_compare( PHP_VERSION, '5.5', '>=' ) ) {
3572                $args = array( $data, $options, $depth );
3573        } elseif ( version_compare( PHP_VERSION, '5.3', '>=' ) ) {
3574                $args = array( $data, $options );
3575        } else {
3576                $args = array( $data );
3577        }
3578
3579        // Prepare the data for JSON serialization.
3580        $args[0] = _wp_json_prepare_data( $data );
3581
3582        $json = @call_user_func_array( 'json_encode', $args );
3583
3584        // If json_encode() was successful, no need to do more sanity checking.
3585        // ... unless we're in an old version of PHP, and json_encode() returned
3586        // a string containing 'null'. Then we need to do more sanity checking.
3587        if ( false !== $json && ( version_compare( PHP_VERSION, '5.5', '>=' ) || false === strpos( $json, 'null' ) ) ) {
3588                return $json;
3589        }
3590
3591        try {
3592                $args[0] = _wp_json_sanity_check( $data, $depth );
3593        } catch ( Exception $e ) {
3594                return false;
3595        }
3596
3597        return call_user_func_array( 'json_encode', $args );
3598}
3599
3600/**
3601 * Perform sanity checks on data that shall be encoded to JSON.
3602 *
3603 * @ignore
3604 * @since 4.1.0
3605 * @access private
3606 *
3607 * @see wp_json_encode()
3608 *
3609 * @param mixed $data  Variable (usually an array or object) to encode as JSON.
3610 * @param int   $depth Maximum depth to walk through $data. Must be greater than 0.
3611 * @return mixed The sanitized data that shall be encoded to JSON.
3612 */
3613function _wp_json_sanity_check( $data, $depth ) {
3614        if ( $depth < 0 ) {
3615                throw new Exception( 'Reached depth limit' );
3616        }
3617
3618        if ( is_array( $data ) ) {
3619                $output = array();
3620                foreach ( $data as $id => $el ) {
3621                        // Don't forget to sanitize the ID!
3622                        if ( is_string( $id ) ) {
3623                                $clean_id = _wp_json_convert_string( $id );
3624                        } else {
3625                                $clean_id = $id;
3626                        }
3627
3628                        // Check the element type, so that we're only recursing if we really have to.
3629                        if ( is_array( $el ) || is_object( $el ) ) {
3630                                $output[ $clean_id ] = _wp_json_sanity_check( $el, $depth - 1 );
3631                        } elseif ( is_string( $el ) ) {
3632                                $output[ $clean_id ] = _wp_json_convert_string( $el );
3633                        } else {
3634                                $output[ $clean_id ] = $el;
3635                        }
3636                }
3637        } elseif ( is_object( $data ) ) {
3638                $output = new stdClass;
3639                foreach ( $data as $id => $el ) {
3640                        if ( is_string( $id ) ) {
3641                                $clean_id = _wp_json_convert_string( $id );
3642                        } else {
3643                                $clean_id = $id;
3644                        }
3645
3646                        if ( is_array( $el ) || is_object( $el ) ) {
3647                                $output->$clean_id = _wp_json_sanity_check( $el, $depth - 1 );
3648                        } elseif ( is_string( $el ) ) {
3649                                $output->$clean_id = _wp_json_convert_string( $el );
3650                        } else {
3651                                $output->$clean_id = $el;
3652                        }
3653                }
3654        } elseif ( is_string( $data ) ) {
3655                return _wp_json_convert_string( $data );
3656        } else {
3657                return $data;
3658        }
3659
3660        return $output;
3661}
3662
3663/**
3664 * Convert a string to UTF-8, so that it can be safely encoded to JSON.
3665 *
3666 * @ignore
3667 * @since 4.1.0
3668 * @access private
3669 *
3670 * @see _wp_json_sanity_check()
3671 *
3672 * @staticvar bool $use_mb
3673 *
3674 * @param string $string The string which is to be converted.
3675 * @return string The checked string.
3676 */
3677function _wp_json_convert_string( $string ) {
3678        static $use_mb = null;
3679        if ( is_null( $use_mb ) ) {
3680                $use_mb = function_exists( 'mb_convert_encoding' );
3681        }
3682
3683        if ( $use_mb ) {
3684                $encoding = mb_detect_encoding( $string, mb_detect_order(), true );
3685                if ( $encoding ) {
3686                        return mb_convert_encoding( $string, 'UTF-8', $encoding );
3687                } else {
3688                        return mb_convert_encoding( $string, 'UTF-8', 'UTF-8' );
3689                }
3690        } else {
3691                return wp_check_invalid_utf8( $string, true );
3692        }
3693}
3694
3695/**
3696 * Prepares response data to be serialized to JSON.
3697 *
3698 * This supports the JsonSerializable interface for PHP 5.2-5.3 as well.
3699 *
3700 * @ignore
3701 * @since 4.4.0
3702 * @access private
3703 *
3704 * @param mixed $data Native representation.
3705 * @return bool|int|float|null|string|array Data ready for `json_encode()`.
3706 */
3707function _wp_json_prepare_data( $data ) {
3708        if ( ! defined( 'WP_JSON_SERIALIZE_COMPATIBLE' ) || WP_JSON_SERIALIZE_COMPATIBLE === false ) {
3709                return $data;
3710        }
3711
3712        switch ( gettype( $data ) ) {
3713                case 'boolean':
3714                case 'integer':
3715                case 'double':
3716                case 'string':
3717                case 'NULL':
3718                        // These values can be passed through.
3719                        return $data;
3720
3721                case 'array':
3722                        // Arrays must be mapped in case they also return objects.
3723                        return array_map( '_wp_json_prepare_data', $data );
3724
3725                case 'object':
3726                        // If this is an incomplete object (__PHP_Incomplete_Class), bail.
3727                        if ( ! is_object( $data ) ) {
3728                                return null;
3729                        }
3730
3731                        if ( $data instanceof JsonSerializable ) {
3732                                $data = $data->jsonSerialize();
3733                        } else {
3734                                $data = get_object_vars( $data );
3735                        }
3736
3737                        // Now, pass the array (or whatever was returned from jsonSerialize through).
3738                        return _wp_json_prepare_data( $data );
3739
3740                default:
3741                        return null;
3742        }
3743}
3744
3745/**
3746 * Send a JSON response back to an Ajax request.
3747 *
3748 * @since 3.5.0
3749 * @since 4.7.0 The `$status_code` parameter was added.
3750 *
3751 * @param mixed $response    Variable (usually an array or object) to encode as JSON,
3752 *                           then print and die.
3753 * @param int   $status_code The HTTP status code to output.
3754 */
3755function wp_send_json( $response, $status_code = null ) {
3756        @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
3757        if ( null !== $status_code ) {
3758                status_header( $status_code );
3759        }
3760        echo wp_json_encode( $response );
3761
3762        if ( wp_doing_ajax() ) {
3763                wp_die(
3764                        '',
3765                        '',
3766                        array(
3767                                'response' => null,
3768                        )
3769                );
3770        } else {
3771                die;
3772        }
3773}
3774
3775/**
3776 * Send a JSON response back to an Ajax request, indicating success.
3777 *
3778 * @since 3.5.0
3779 * @since 4.7.0 The `$status_code` parameter was added.
3780 *
3781 * @param mixed $data        Data to encode as JSON, then print and die.
3782 * @param int   $status_code The HTTP status code to output.
3783 */
3784function wp_send_json_success( $data = null, $status_code = null ) {
3785        $response = array( 'success' => true );
3786
3787        if ( isset( $data ) ) {
3788                $response['data'] = $data;
3789        }
3790
3791        wp_send_json( $response, $status_code );
3792}
3793
3794/**
3795 * Send a JSON response back to an Ajax request, indicating failure.
3796 *
3797 * If the `$data` parameter is a WP_Error object, the errors
3798 * within the object are processed and output as an array of error
3799 * codes and corresponding messages. All other types are output
3800 * without further processing.
3801 *
3802 * @since 3.5.0
3803 * @since 4.1.0 The `$data` parameter is now processed if a WP_Error object is passed in.
3804 * @since 4.7.0 The `$status_code` parameter was added.
3805 *
3806 * @param mixed $data        Data to encode as JSON, then print and die.
3807 * @param int   $status_code The HTTP status code to output.
3808 */
3809function wp_send_json_error( $data = null, $status_code = null ) {
3810        $response = array( 'success' => false );
3811
3812        if ( isset( $data ) ) {
3813                if ( is_wp_error( $data ) ) {
3814                        $result = array();
3815                        foreach ( $data->errors as $code => $messages ) {
3816                                foreach ( $messages as $message ) {
3817                                        $result[] = array(
3818                                                'code'    => $code,
3819                                                'message' => $message,
3820                                        );
3821                                }
3822                        }
3823
3824                        $response['data'] = $result;
3825                } else {
3826                        $response['data'] = $data;
3827                }
3828        }
3829
3830        wp_send_json( $response, $status_code );
3831}
3832
3833/**
3834 * Checks that a JSONP callback is a valid JavaScript callback.
3835 *
3836 * Only allows alphanumeric characters and the dot character in callback
3837 * function names. This helps to mitigate XSS attacks caused by directly
3838 * outputting user input.
3839 *
3840 * @since 4.6.0
3841 *
3842 * @param string $callback Supplied JSONP callback function.
3843 * @return bool True if valid callback, otherwise false.
3844 */
3845function wp_check_jsonp_callback( $callback ) {
3846        if ( ! is_string( $callback ) ) {
3847                return false;
3848        }
3849
3850        preg_replace( '/[^\w\.]/', '', $callback, -1, $illegal_char_count );
3851
3852        return 0 === $illegal_char_count;
3853}
3854
3855/**
3856 * Retrieve the WordPress home page URL.
3857 *
3858 * If the constant named 'WP_HOME' exists, then it will be used and returned
3859 * by the function. This can be used to counter the redirection on your local
3860 * development environment.
3861 *
3862 * @since 2.2.0
3863 * @access private
3864 *
3865 * @see WP_HOME
3866 *
3867 * @param string $url URL for the home location.
3868 * @return string Homepage location.
3869 */
3870function _config_wp_home( $url = '' ) {
3871        if ( defined( 'WP_HOME' ) ) {
3872                return untrailingslashit( WP_HOME );
3873        }
3874        return $url;
3875}
3876
3877/**
3878 * Retrieve the WordPress site URL.
3879 *
3880 * If the constant named 'WP_SITEURL' is defined, then the value in that
3881 * constant will always be returned. This can be used for debugging a site
3882 * on your localhost while not having to change the database to your URL.
3883 *
3884 * @since 2.2.0
3885 * @access private
3886 *
3887 * @see WP_SITEURL
3888 *
3889 * @param string $url URL to set the WordPress site location.
3890 * @return string The WordPress Site URL.
3891 */
3892function _config_wp_siteurl( $url = '' ) {
3893        if ( defined( 'WP_SITEURL' ) ) {
3894                return untrailingslashit( WP_SITEURL );
3895        }
3896        return $url;
3897}
3898
3899/**
3900 * Delete the fresh site option.
3901 *
3902 * @since 4.7.0
3903 * @access private
3904 */
3905function _delete_option_fresh_site() {
3906        update_option( 'fresh_site', '0' );
3907}
3908
3909/**
3910 * Set the localized direction for MCE plugin.
3911 *
3912 * Will only set the direction to 'rtl', if the WordPress locale has
3913 * the text direction set to 'rtl'.
3914 *
3915 * Fills in the 'directionality' setting, enables the 'directionality'
3916 * plugin, and adds the 'ltr' button to 'toolbar1', formerly
3917 * 'theme_advanced_buttons1' array keys. These keys are then returned
3918 * in the $mce_init (TinyMCE settings) array.
3919 *
3920 * @since 2.1.0
3921 * @access private
3922 *
3923 * @param array $mce_init MCE settings array.
3924 * @return array Direction set for 'rtl', if needed by locale.
3925 */
3926function _mce_set_direction( $mce_init ) {
3927        if ( is_rtl() ) {
3928                $mce_init['directionality'] = 'rtl';
3929                $mce_init['rtl_ui']         = true;
3930
3931                if ( ! empty( $mce_init['plugins'] ) && strpos( $mce_init['plugins'], 'directionality' ) === false ) {
3932                        $mce_init['plugins'] .= ',directionality';
3933                }
3934
3935                if ( ! empty( $mce_init['toolbar1'] ) && ! preg_match( '/\bltr\b/', $mce_init['toolbar1'] ) ) {
3936                        $mce_init['toolbar1'] .= ',ltr';
3937                }
3938        }
3939
3940        return $mce_init;
3941}
3942
3943
3944/**
3945 * Convert smiley code to the icon graphic file equivalent.
3946 *
3947 * You can turn off smilies, by going to the write setting screen and unchecking
3948 * the box, or by setting 'use_smilies' option to false or removing the option.
3949 *
3950 * Plugins may override the default smiley list by setting the $wpsmiliestrans
3951 * to an array, with the key the code the blogger types in and the value the
3952 * image file.
3953 *
3954 * The $wp_smiliessearch global is for the regular expression and is set each
3955 * time the function is called.
3956 *
3957 * The full list of smilies can be found in the function and won't be listed in
3958 * the description. Probably should create a Codex page for it, so that it is
3959 * available.
3960 *
3961 * @global array $wpsmiliestrans
3962 * @global array $wp_smiliessearch
3963 *
3964 * @since 2.2.0
3965 */
3966function smilies_init() {
3967        global $wpsmiliestrans, $wp_smiliessearch;
3968
3969        // don't bother setting up smilies if they are disabled
3970        if ( ! get_option( 'use_smilies' ) ) {
3971                return;
3972        }
3973
3974        if ( ! isset( $wpsmiliestrans ) ) {
3975                $wpsmiliestrans = array(
3976                        ':mrgreen:' => 'mrgreen.png',
3977                        ':neutral:' => "\xf0\x9f\x98\x90",
3978                        ':twisted:' => "\xf0\x9f\x98\x88",
3979                        ':arrow:'   => "\xe2\x9e\xa1",
3980                        ':shock:'   => "\xf0\x9f\x98\xaf",
3981                        ':smile:'   => "\xf0\x9f\x99\x82",
3982                        ':???:'     => "\xf0\x9f\x98\x95",
3983                        ':cool:'    => "\xf0\x9f\x98\x8e",
3984                        ':evil:'    => "\xf0\x9f\x91\xbf",
3985                        ':grin:'    => "\xf0\x9f\x98\x80",
3986                        ':idea:'    => "\xf0\x9f\x92\xa1",
3987                        ':oops:'    => "\xf0\x9f\x98\xb3",
3988                        ':razz:'    => "\xf0\x9f\x98\x9b",
3989                        ':roll:'    => "\xf0\x9f\x99\x84",
3990                        ':wink:'    => "\xf0\x9f\x98\x89",
3991                        ':cry:'     => "\xf0\x9f\x98\xa5",
3992                        ':eek:'     => "\xf0\x9f\x98\xae",
3993                        ':lol:'     => "\xf0\x9f\x98\x86",
3994                        ':mad:'     => "\xf0\x9f\x98\xa1",
3995                        ':sad:'     => "\xf0\x9f\x99\x81",
3996                        '8-)'       => "\xf0\x9f\x98\x8e",
3997                        '8-O'       => "\xf0\x9f\x98\xaf",
3998                        ':-('       => "\xf0\x9f\x99\x81",
3999                        ':-)'       => "\xf0\x9f\x99\x82",
4000                        ':-?'       => "\xf0\x9f\x98\x95",
4001                        ':-D'       => "\xf0\x9f\x98\x80",
4002                        ':-P'       => "\xf0\x9f\x98\x9b",
4003                        ':-o'       => "\xf0\x9f\x98\xae",
4004                        ':-x'       => "\xf0\x9f\x98\xa1",
4005                        ':-|'       => "\xf0\x9f\x98\x90",
4006                        ';-)'       => "\xf0\x9f\x98\x89",
4007                        // This one transformation breaks regular text with frequency.
4008                        //     '8)' => "\xf0\x9f\x98\x8e",
4009                        '8O'        => "\xf0\x9f\x98\xaf",
4010                        ':('        => "\xf0\x9f\x99\x81",
4011                        ':)'        => "\xf0\x9f\x99\x82",
4012                        ':?'        => "\xf0\x9f\x98\x95",
4013                        ':D'        => "\xf0\x9f\x98\x80",
4014                        ':P'        => "\xf0\x9f\x98\x9b",
4015                        ':o'        => "\xf0\x9f\x98\xae",
4016                        ':x'        => "\xf0\x9f\x98\xa1",
4017                        ':|'        => "\xf0\x9f\x98\x90",
4018                        ';)'        => "\xf0\x9f\x98\x89",
4019                        ':!:'       => "\xe2\x9d\x97",
4020                        ':?:'       => "\xe2\x9d\x93",
4021                );
4022        }
4023
4024        /**
4025         * Filters all the smilies.
4026         *
4027         * This filter must be added before `smilies_init` is run, as
4028         * it is normally only run once to setup the smilies regex.
4029         *
4030         * @since 4.7.0
4031         *
4032         * @param array $wpsmiliestrans List of the smilies.
4033         */
4034        $wpsmiliestrans = apply_filters( 'smilies', $wpsmiliestrans );
4035
4036        if ( count( $wpsmiliestrans ) == 0 ) {
4037                return;
4038        }
4039
4040        /*
4041         * NOTE: we sort the smilies in reverse key order. This is to make sure
4042         * we match the longest possible smilie (:???: vs :?) as the regular
4043         * expression used below is first-match
4044         */
4045        krsort( $wpsmiliestrans );
4046
4047        $spaces = wp_spaces_regexp();
4048
4049        // Begin first "subpattern"
4050        $wp_smiliessearch = '/(?<=' . $spaces . '|^)';
4051
4052        $subchar = '';
4053        foreach ( (array) $wpsmiliestrans as $smiley => $img ) {
4054                $firstchar = substr( $smiley, 0, 1 );
4055                $rest      = substr( $smiley, 1 );
4056
4057                // new subpattern?
4058                if ( $firstchar != $subchar ) {
4059                        if ( $subchar != '' ) {
4060                                $wp_smiliessearch .= ')(?=' . $spaces . '|$)';  // End previous "subpattern"
4061                                $wp_smiliessearch .= '|(?<=' . $spaces . '|^)'; // Begin another "subpattern"
4062                        }
4063                        $subchar           = $firstchar;
4064                        $wp_smiliessearch .= preg_quote( $firstchar, '/' ) . '(?:';
4065                } else {
4066                        $wp_smiliessearch .= '|';
4067                }
4068                $wp_smiliessearch .= preg_quote( $rest, '/' );
4069        }
4070
4071        $wp_smiliessearch .= ')(?=' . $spaces . '|$)/m';
4072
4073}
4074
4075/**
4076 * Merge user defined arguments into defaults array.
4077 *
4078 * This function is used throughout WordPress to allow for both string or array
4079 * to be merged into another array.
4080 *
4081 * @since 2.2.0
4082 * @since 2.3.0 `$args` can now also be an object.
4083 *
4084 * @param string|array|object $args     Value to merge with $defaults.
4085 * @param array               $defaults Optional. Array that serves as the defaults. Default empty.
4086 * @return array Merged user defined values with defaults.
4087 */
4088function wp_parse_args( $args, $defaults = '' ) {
4089        if ( is_object( $args ) ) {
4090                $r = get_object_vars( $args );
4091        } elseif ( is_array( $args ) ) {
4092                $r =& $args;
4093        } else {
4094                wp_parse_str( $args, $r );
4095        }
4096
4097        if ( is_array( $defaults ) ) {
4098                return array_merge( $defaults, $r );
4099        }
4100        return $r;
4101}
4102
4103/**
4104 * Cleans up an array, comma- or space-separated list of scalar values.
4105 *
4106 * @since 5.1.0
4107 *
4108 * @param array|string $list List of values.
4109 * @return array Sanitized array of values.
4110 */
4111function wp_parse_list( $list ) {
4112        if ( ! is_array( $list ) ) {
4113                return preg_split( '/[\s,]+/', $list, -1, PREG_SPLIT_NO_EMPTY );
4114        }
4115
4116        return $list;
4117}
4118
4119/**
4120 * Clean up an array, comma- or space-separated list of IDs.
4121 *
4122 * @since 3.0.0
4123 *
4124 * @param array|string $list List of ids.
4125 * @return array Sanitized array of IDs.
4126 */
4127function wp_parse_id_list( $list ) {
4128        $list = wp_parse_list( $list );
4129
4130        return array_unique( array_map( 'absint', $list ) );
4131}
4132
4133/**
4134 * Clean up an array, comma- or space-separated list of slugs.
4135 *
4136 * @since 4.7.0
4137 *
4138 * @param  array|string $list List of slugs.
4139 * @return array Sanitized array of slugs.
4140 */
4141function wp_parse_slug_list( $list ) {
4142        $list = wp_parse_list( $list );
4143
4144        return array_unique( array_map( 'sanitize_title', $list ) );
4145}
4146
4147/**
4148 * Extract a slice of an array, given a list of keys.
4149 *
4150 * @since 3.1.0
4151 *
4152 * @param array $array The original array.
4153 * @param array $keys  The list of keys.
4154 * @return array The array slice.
4155 */
4156function wp_array_slice_assoc( $array, $keys ) {
4157        $slice = array();
4158        foreach ( $keys as $key ) {
4159                if ( isset( $array[ $key ] ) ) {
4160                        $slice[ $key ] = $array[ $key ];
4161                }
4162        }
4163
4164        return $slice;
4165}
4166
4167/**
4168 * Determines if the variable is a numeric-indexed array.
4169 *
4170 * @since 4.4.0
4171 *
4172 * @param mixed $data Variable to check.
4173 * @return bool Whether the variable is a list.
4174 */
4175function wp_is_numeric_array( $data ) {
4176        if ( ! is_array( $data ) ) {
4177                return false;
4178        }
4179
4180        $keys        = array_keys( $data );
4181        $string_keys = array_filter( $keys, 'is_string' );
4182        return count( $string_keys ) === 0;
4183}
4184
4185/**
4186 * Filters a list of objects, based on a set of key => value arguments.
4187 *
4188 * @since 3.0.0
4189 * @since 4.7.0 Uses `WP_List_Util` class.
4190 *
4191 * @param array       $list     An array of objects to filter
4192 * @param array       $args     Optional. An array of key => value arguments to match
4193 *                              against each object. Default empty array.
4194 * @param string      $operator Optional. The logical operation to perform. 'or' means
4195 *                              only one element from the array needs to match; 'and'
4196 *                              means all elements must match; 'not' means no elements may
4197 *                              match. Default 'and'.
4198 * @param bool|string $field    A field from the object to place instead of the entire object.
4199 *                              Default false.
4200 * @return array A list of objects or object fields.
4201 */
4202function wp_filter_object_list( $list, $args = array(), $operator = 'and', $field = false ) {
4203        if ( ! is_array( $list ) ) {
4204                return array();
4205        }
4206
4207        $util = new WP_List_Util( $list );
4208
4209        $util->filter( $args, $operator );
4210
4211        if ( $field ) {
4212                $util->pluck( $field );
4213        }
4214
4215        return $util->get_output();
4216}
4217
4218/**
4219 * Filters a list of objects, based on a set of key => value arguments.
4220 *
4221 * @since 3.1.0
4222 * @since 4.7.0 Uses `WP_List_Util` class.
4223 *
4224 * @param array  $list     An array of objects to filter.
4225 * @param array  $args     Optional. An array of key => value arguments to match
4226 *                         against each object. Default empty array.
4227 * @param string $operator Optional. The logical operation to perform. 'AND' means
4228 *                         all elements from the array must match. 'OR' means only
4229 *                         one element needs to match. 'NOT' means no elements may
4230 *                         match. Default 'AND'.
4231 * @return array Array of found values.
4232 */
4233function wp_list_filter( $list, $args = array(), $operator = 'AND' ) {
4234        if ( ! is_array( $list ) ) {
4235                return array();
4236        }
4237
4238        $util = new WP_List_Util( $list );
4239        return $util->filter( $args, $operator );
4240}
4241
4242/**
4243 * Pluck a certain field out of each object in a list.
4244 *
4245 * This has the same functionality and prototype of
4246 * array_column() (PHP 5.5) but also supports objects.
4247 *
4248 * @since 3.1.0
4249 * @since 4.0.0 $index_key parameter added.
4250 * @since 4.7.0 Uses `WP_List_Util` class.
4251 *
4252 * @param array      $list      List of objects or arrays
4253 * @param int|string $field     Field from the object to place instead of the entire object
4254 * @param int|string $index_key Optional. Field from the object to use as keys for the new array.
4255 *                              Default null.
4256 * @return array Array of found values. If `$index_key` is set, an array of found values with keys
4257 *               corresponding to `$index_key`. If `$index_key` is null, array keys from the original
4258 *               `$list` will be preserved in the results.
4259 */
4260function wp_list_pluck( $list, $field, $index_key = null ) {
4261        $util = new WP_List_Util( $list );
4262        return $util->pluck( $field, $index_key );
4263}
4264
4265/**
4266 * Sorts a list of objects, based on one or more orderby arguments.
4267 *
4268 * @since 4.7.0
4269 *
4270 * @param array        $list          An array of objects to sort.
4271 * @param string|array $orderby       Optional. Either the field name to order by or an array
4272 *                                    of multiple orderby fields as $orderby => $order.
4273 * @param string       $order         Optional. Either 'ASC' or 'DESC'. Only used if $orderby
4274 *                                    is a string.
4275 * @param bool         $preserve_keys Optional. Whether to preserve keys. Default false.
4276 * @return array The sorted array.
4277 */
4278function wp_list_sort( $list, $orderby = array(), $order = 'ASC', $preserve_keys = false ) {
4279        if ( ! is_array( $list ) ) {
4280                return array();
4281        }
4282
4283        $util = new WP_List_Util( $list );
4284        return $util->sort( $orderby, $order, $preserve_keys );
4285}
4286
4287/**
4288 * Determines if Widgets library should be loaded.
4289 *
4290 * Checks to make sure that the widgets library hasn't already been loaded.
4291 * If it hasn't, then it will load the widgets library and run an action hook.
4292 *
4293 * @since 2.2.0
4294 */
4295function wp_maybe_load_widgets() {
4296        /**
4297         * Filters whether to load the Widgets library.
4298         *
4299         * Passing a falsey value to the filter will effectively short-circuit
4300         * the Widgets library from loading.
4301         *
4302         * @since 2.8.0
4303         *
4304         * @param bool $wp_maybe_load_widgets Whether to load the Widgets library.
4305         *                                    Default true.
4306         */
4307        if ( ! apply_filters( 'load_default_widgets', true ) ) {
4308                return;
4309        }
4310
4311        require_once( ABSPATH . WPINC . '/default-widgets.php' );
4312
4313        add_action( '_admin_menu', 'wp_widgets_add_menu' );
4314}
4315
4316/**
4317 * Append the Widgets menu to the themes main menu.
4318 *
4319 * @since 2.2.0
4320 *
4321 * @global array $submenu
4322 */
4323function wp_widgets_add_menu() {
4324        global $submenu;
4325
4326        if ( ! current_theme_supports( 'widgets' ) ) {
4327                return;
4328        }
4329
4330        $submenu['themes.php'][7] = array( __( 'Widgets' ), 'edit_theme_options', 'widgets.php' );
4331        ksort( $submenu['themes.php'], SORT_NUMERIC );
4332}
4333
4334/**
4335 * Flush all output buffers for PHP 5.2.
4336 *
4337 * Make sure all output buffers are flushed before our singletons are destroyed.
4338 *
4339 * @since 2.2.0
4340 */
4341function wp_ob_end_flush_all() {
4342        $levels = ob_get_level();
4343        for ( $i = 0; $i < $levels; $i++ ) {
4344                ob_end_flush();
4345        }
4346}
4347
4348/**
4349 * Load custom DB error or display WordPress DB error.
4350 *
4351 * If a file exists in the wp-content directory named db-error.php, then it will
4352 * be loaded instead of displaying the WordPress DB error. If it is not found,
4353 * then the WordPress DB error will be displayed instead.
4354 *
4355 * The WordPress DB error sets the HTTP status header to 500 to try to prevent
4356 * search engines from caching the message. Custom DB messages should do the
4357 * same.
4358 *
4359 * This function was backported to WordPress 2.3.2, but originally was added
4360 * in WordPress 2.5.0.
4361 *
4362 * @since 2.3.2
4363 *
4364 * @global wpdb $wpdb WordPress database abstraction object.
4365 */
4366function dead_db() {
4367        global $wpdb;
4368
4369        wp_load_translations_early();
4370
4371        // Load custom DB error template, if present.
4372        if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
4373                require_once( WP_CONTENT_DIR . '/db-error.php' );
4374                die();
4375        }
4376
4377        // If installing or in the admin, provide the verbose message.
4378        if ( wp_installing() || defined( 'WP_ADMIN' ) ) {
4379                wp_die( $wpdb->error );
4380        }
4381
4382        // Otherwise, be terse.
4383        wp_die( '<h1>' . __( 'Error establishing a database connection' ) . '</h1>', __( 'Database Error' ) );
4384}
4385
4386/**
4387 * Convert a value to non-negative integer.
4388 *
4389 * @since 2.5.0
4390 *
4391 * @param mixed $maybeint Data you wish to have converted to a non-negative integer.
4392 * @return int A non-negative integer.
4393 */
4394function absint( $maybeint ) {
4395        return abs( intval( $maybeint ) );
4396}
4397
4398/**
4399 * Mark a function as deprecated and inform when it has been used.
4400 *
4401 * There is a {@see 'hook deprecated_function_run'} that will be called that can be used
4402 * to get the backtrace up to what file and function called the deprecated
4403 * function.
4404 *
4405 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
4406 *
4407 * This function is to be used in every function that is deprecated.
4408 *
4409 * @since 2.5.0
4410 * @access private
4411 *
4412 * @param string $function    The function that was called.
4413 * @param string $version     The version of WordPress that deprecated the function.
4414 * @param string $replacement Optional. The function that should have been called. Default null.
4415 */
4416function _deprecated_function( $function, $version, $replacement = null ) {
4417
4418        /**
4419         * Fires when a deprecated function is called.
4420         *
4421         * @since 2.5.0
4422         *
4423         * @param string $function    The function that was called.
4424         * @param string $replacement The function that should have been called.
4425         * @param string $version     The version of WordPress that deprecated the function.
4426         */
4427        do_action( 'deprecated_function_run', $function, $replacement, $version );
4428
4429        /**
4430         * Filters whether to trigger an error for deprecated functions.
4431         *
4432         * @since 2.5.0
4433         *
4434         * @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
4435         */
4436        if ( WP_DEBUG && apply_filters( 'deprecated_function_trigger_error', true ) ) {
4437                if ( function_exists( '__' ) ) {
4438                        if ( ! is_null( $replacement ) ) {
4439                                /* translators: 1: PHP function name, 2: version number, 3: alternative function name */
4440                                trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ), $function, $version, $replacement ) );
4441                        } else {
4442                                /* translators: 1: PHP function name, 2: version number */
4443                                trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.' ), $function, $version ) );
4444                        }
4445                } else {
4446                        if ( ! is_null( $replacement ) ) {
4447                                trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.', $function, $version, $replacement ) );
4448                        } else {
4449                                trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.', $function, $version ) );
4450                        }
4451                }
4452        }
4453}
4454
4455/**
4456 * Marks a constructor as deprecated and informs when it has been used.
4457 *
4458 * Similar to _deprecated_function(), but with different strings. Used to
4459 * remove PHP4 style constructors.
4460 *
4461 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
4462 *
4463 * This function is to be used in every PHP4 style constructor method that is deprecated.
4464 *
4465 * @since 4.3.0
4466 * @since 4.5.0 Added the `$parent_class` parameter.
4467 *
4468 * @access private
4469 *
4470 * @param string $class        The class containing the deprecated constructor.
4471 * @param string $version      The version of WordPress that deprecated the function.
4472 * @param string $parent_class Optional. The parent class calling the deprecated constructor.
4473 *                             Default empty string.
4474 */
4475function _deprecated_constructor( $class, $version, $parent_class = '' ) {
4476
4477        /**
4478         * Fires when a deprecated constructor is called.
4479         *
4480         * @since 4.3.0
4481         * @since 4.5.0 Added the `$parent_class` parameter.
4482         *
4483         * @param string $class        The class containing the deprecated constructor.
4484         * @param string $version      The version of WordPress that deprecated the function.
4485         * @param string $parent_class The parent class calling the deprecated constructor.
4486         */
4487        do_action( 'deprecated_constructor_run', $class, $version, $parent_class );
4488
4489        /**
4490         * Filters whether to trigger an error for deprecated functions.
4491         *
4492         * `WP_DEBUG` must be true in addition to the filter evaluating to true.
4493         *
4494         * @since 4.3.0
4495         *
4496         * @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
4497         */
4498        if ( WP_DEBUG && apply_filters( 'deprecated_constructor_trigger_error', true ) ) {
4499                if ( function_exists( '__' ) ) {
4500                        if ( ! empty( $parent_class ) ) {
4501                                /* translators: 1: PHP class name, 2: PHP parent class name, 3: version number, 4: __construct() method */
4502                                trigger_error(
4503                                        sprintf(
4504                                                __( 'The called constructor method for %1$s in %2$s is <strong>deprecated</strong> since version %3$s! Use %4$s instead.' ),
4505                                                $class,
4506                                                $parent_class,
4507                                                $version,
4508                                                '<pre>__construct()</pre>'
4509                                        )
4510                                );
4511                        } else {
4512                                /* translators: 1: PHP class name, 2: version number, 3: __construct() method */
4513                                trigger_error(
4514                                        sprintf(
4515                                                __( 'The called constructor method for %1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ),
4516                                                $class,
4517                                                $version,
4518                                                '<pre>__construct()</pre>'
4519                                        )
4520                                );
4521                        }
4522                } else {
4523                        if ( ! empty( $parent_class ) ) {
4524                                trigger_error(
4525                                        sprintf(
4526                                                'The called constructor method for %1$s in %2$s is <strong>deprecated</strong> since version %3$s! Use %4$s instead.',
4527                                                $class,
4528                                                $parent_class,
4529                                                $version,
4530                                                '<pre>__construct()</pre>'
4531                                        )
4532                                );
4533                        } else {
4534                                trigger_error(
4535                                        sprintf(
4536                                                'The called constructor method for %1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.',
4537                                                $class,
4538                                                $version,
4539                                                '<pre>__construct()</pre>'
4540                                        )
4541                                );
4542                        }
4543                }
4544        }
4545
4546}
4547
4548/**
4549 * Mark a file as deprecated and inform when it has been used.
4550 *
4551 * There is a hook {@see 'deprecated_file_included'} that will be called that can be used
4552 * to get the backtrace up to what file and function included the deprecated
4553 * file.
4554 *
4555 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
4556 *
4557 * This function is to be used in every file that is deprecated.
4558 *
4559 * @since 2.5.0
4560 * @access private
4561 *
4562 * @param string $file        The file that was included.
4563 * @param string $version     The version of WordPress that deprecated the file.
4564 * @param string $replacement Optional. The file that should have been included based on ABSPATH.
4565 *                            Default null.
4566 * @param string $message     Optional. A message regarding the change. Default empty.
4567 */
4568function _deprecated_file( $file, $version, $replacement = null, $message = '' ) {
4569
4570        /**
4571         * Fires when a deprecated file is called.
4572         *
4573         * @since 2.5.0
4574         *
4575         * @param string $file        The file that was called.
4576         * @param string $replacement The file that should have been included based on ABSPATH.
4577         * @param string $version     The version of WordPress that deprecated the file.
4578         * @param string $message     A message regarding the change.
4579         */
4580        do_action( 'deprecated_file_included', $file, $replacement, $version, $message );
4581
4582        /**
4583         * Filters whether to trigger an error for deprecated files.
4584         *
4585         * @since 2.5.0
4586         *
4587         * @param bool $trigger Whether to trigger the error for deprecated files. Default true.
4588         */
4589        if ( WP_DEBUG && apply_filters( 'deprecated_file_trigger_error', true ) ) {
4590                $message = empty( $message ) ? '' : ' ' . $message;
4591                if ( function_exists( '__' ) ) {
4592                        if ( ! is_null( $replacement ) ) {
4593                                /* translators: 1: PHP file name, 2: version number, 3: alternative file name */
4594                                trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ), $file, $version, $replacement ) . $message );
4595                        } else {
4596                                /* translators: 1: PHP file name, 2: version number */
4597                                trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.' ), $file, $version ) . $message );
4598                        }
4599                } else {
4600                        if ( ! is_null( $replacement ) ) {
4601                                trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.', $file, $version, $replacement ) . $message );
4602                        } else {
4603                                trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.', $file, $version ) . $message );
4604                        }
4605                }
4606        }
4607}
4608/**
4609 * Mark a function argument as deprecated and inform when it has been used.
4610 *
4611 * This function is to be used whenever a deprecated function argument is used.
4612 * Before this function is called, the argument must be checked for whether it was
4613 * used by comparing it to its default value or evaluating whether it is empty.
4614 * For example:
4615 *
4616 *     if ( ! empty( $deprecated ) ) {
4617 *         _deprecated_argument( __FUNCTION__, '3.0.0' );
4618 *     }
4619 *
4620 * There is a hook deprecated_argument_run that will be called that can be used
4621 * to get the backtrace up to what file and function used the deprecated
4622 * argument.
4623 *
4624 * The current behavior is to trigger a user error if WP_DEBUG is true.
4625 *
4626 * @since 3.0.0
4627 * @access private
4628 *
4629 * @param string $function The function that was called.
4630 * @param string $version  The version of WordPress that deprecated the argument used.
4631 * @param string $message  Optional. A message regarding the change. Default null.
4632 */
4633function _deprecated_argument( $function, $version, $message = null ) {
4634
4635        /**
4636         * Fires when a deprecated argument is called.
4637         *
4638         * @since 3.0.0
4639         *
4640         * @param string $function The function that was called.
4641         * @param string $message  A message regarding the change.
4642         * @param string $version  The version of WordPress that deprecated the argument used.
4643         */
4644        do_action( 'deprecated_argument_run', $function, $message, $version );
4645
4646        /**
4647         * Filters whether to trigger an error for deprecated arguments.
4648         *
4649         * @since 3.0.0
4650         *
4651         * @param bool $trigger Whether to trigger the error for deprecated arguments. Default true.
4652         */
4653        if ( WP_DEBUG && apply_filters( 'deprecated_argument_trigger_error', true ) ) {
4654                if ( function_exists( '__' ) ) {
4655                        if ( ! is_null( $message ) ) {
4656                                /* translators: 1: PHP function name, 2: version number, 3: optional message regarding the change */
4657                                trigger_error( sprintf( __( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s' ), $function, $version, $message ) );
4658                        } else {
4659                                /* translators: 1: PHP function name, 2: version number */
4660                                trigger_error( sprintf( __( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s with no alternative available.' ), $function, $version ) );
4661                        }
4662                } else {
4663                        if ( ! is_null( $message ) ) {
4664                                trigger_error( sprintf( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s', $function, $version, $message ) );
4665                        } else {
4666                                trigger_error( sprintf( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s with no alternative available.', $function, $version ) );
4667                        }
4668                }
4669        }
4670}
4671
4672/**
4673 * Marks a deprecated action or filter hook as deprecated and throws a notice.
4674 *
4675 * Use the {@see 'deprecated_hook_run'} action to get the backtrace describing where
4676 * the deprecated hook was called.
4677 *
4678 * Default behavior is to trigger a user error if `WP_DEBUG` is true.
4679 *
4680 * This function is called by the do_action_deprecated() and apply_filters_deprecated()
4681 * functions, and so generally does not need to be called directly.
4682 *
4683 * @since 4.6.0
4684 * @access private
4685 *
4686 * @param string $hook        The hook that was used.
4687 * @param string $version     The version of WordPress that deprecated the hook.
4688 * @param string $replacement Optional. The hook that should have been used.
4689 * @param string $message     Optional. A message regarding the change.
4690 */
4691function _deprecated_hook( $hook, $version, $replacement = null, $message = null ) {
4692        /**
4693         * Fires when a deprecated hook is called.
4694         *
4695         * @since 4.6.0
4696         *
4697         * @param string $hook        The hook that was called.
4698         * @param string $replacement The hook that should be used as a replacement.
4699         * @param string $version     The version of WordPress that deprecated the argument used.
4700         * @param string $message     A message regarding the change.
4701         */
4702        do_action( 'deprecated_hook_run', $hook, $replacement, $version, $message );
4703
4704        /**
4705         * Filters whether to trigger deprecated hook errors.
4706         *
4707         * @since 4.6.0
4708         *
4709         * @param bool $trigger Whether to trigger deprecated hook errors. Requires
4710         *                      `WP_DEBUG` to be defined true.
4711         */
4712        if ( WP_DEBUG && apply_filters( 'deprecated_hook_trigger_error', true ) ) {
4713                $message = empty( $message ) ? '' : ' ' . $message;
4714                if ( ! is_null( $replacement ) ) {
4715                        /* translators: 1: WordPress hook name, 2: version number, 3: alternative hook name */
4716                        trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ), $hook, $version, $replacement ) . $message );
4717                } else {
4718                        /* translators: 1: WordPress hook name, 2: version number */
4719                        trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.' ), $hook, $version ) . $message );
4720                }
4721        }
4722}
4723
4724/**
4725 * Mark something as being incorrectly called.
4726 *
4727 * There is a hook {@see 'doing_it_wrong_run'} that will be called that can be used
4728 * to get the backtrace up to what file and function called the deprecated
4729 * function.
4730 *
4731 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
4732 *
4733 * @since 3.1.0
4734 * @access private
4735 *
4736 * @param string $function The function that was called.
4737 * @param string $message  A message explaining what has been done incorrectly.
4738 * @param string $version  The version of WordPress where the message was added.
4739 */
4740function _doing_it_wrong( $function, $message, $version ) {
4741
4742        /**
4743         * Fires when the given function is being used incorrectly.
4744         *
4745         * @since 3.1.0
4746         *
4747         * @param string $function The function that was called.
4748         * @param string $message  A message explaining what has been done incorrectly.
4749         * @param string $version  The version of WordPress where the message was added.
4750         */
4751        do_action( 'doing_it_wrong_run', $function, $message, $version );
4752
4753        /**
4754         * Filters whether to trigger an error for _doing_it_wrong() calls.
4755         *
4756         * @since 3.1.0
4757         * @since 5.1.0 Added the $function, $message and $version parameters.
4758         *
4759         * @param bool   $trigger  Whether to trigger the error for _doing_it_wrong() calls. Default true.
4760         * @param string $function The function that was called.
4761         * @param string $message  A message explaining what has been done incorrectly.
4762         * @param string $version  The version of WordPress where the message was added.
4763         */
4764        if ( WP_DEBUG && apply_filters( 'doing_it_wrong_trigger_error', true, $function, $message, $version ) ) {
4765                if ( function_exists( '__' ) ) {
4766                        if ( is_null( $version ) ) {
4767                                $version = '';
4768                        } else {
4769                                /* translators: %s: version number */
4770                                $version = sprintf( __( '(This message was added in version %s.)' ), $version );
4771                        }
4772                        /* translators: %s: Codex URL */
4773                        $message .= ' ' . sprintf(
4774                                __( 'Please see <a href="%s">Debugging in WordPress</a> for more information.' ),
4775                                __( 'https://codex.wordpress.org/Debugging_in_WordPress' )
4776                        );
4777                        /* translators: Developer debugging message. 1: PHP function name, 2: Explanatory message, 3: Version information message */
4778                        trigger_error( sprintf( __( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s' ), $function, $message, $version ) );
4779                } else {
4780                        if ( is_null( $version ) ) {
4781                                $version = '';
4782                        } else {
4783                                $version = sprintf( '(This message was added in version %s.)', $version );
4784                        }
4785                        $message .= sprintf(
4786                                ' Please see <a href="%s">Debugging in WordPress</a> for more information.',
4787                                'https://codex.wordpress.org/Debugging_in_WordPress'
4788                        );
4789                        trigger_error( sprintf( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s', $function, $message, $version ) );
4790                }
4791        }
4792}
4793
4794/**
4795 * Is the server running earlier than 1.5.0 version of lighttpd?
4796 *
4797 * @since 2.5.0
4798 *
4799 * @return bool Whether the server is running lighttpd < 1.5.0.
4800 */
4801function is_lighttpd_before_150() {
4802        $server_parts    = explode( '/', isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '' );
4803        $server_parts[1] = isset( $server_parts[1] ) ? $server_parts[1] : '';
4804        return  'lighttpd' == $server_parts[0] && -1 == version_compare( $server_parts[1], '1.5.0' );
4805}
4806
4807/**
4808 * Does the specified module exist in the Apache config?
4809 *
4810 * @since 2.5.0
4811 *
4812 * @global bool $is_apache
4813 *
4814 * @param string $mod     The module, e.g. mod_rewrite.
4815 * @param bool   $default Optional. The default return value if the module is not found. Default false.
4816 * @return bool Whether the specified module is loaded.
4817 */
4818function apache_mod_loaded( $mod, $default = false ) {
4819        global $is_apache;
4820
4821        if ( ! $is_apache ) {
4822                return false;
4823        }
4824
4825        if ( function_exists( 'apache_get_modules' ) ) {
4826                $mods = apache_get_modules();
4827                if ( in_array( $mod, $mods ) ) {
4828                        return true;
4829                }
4830        } elseif ( function_exists( 'phpinfo' ) && false === strpos( ini_get( 'disable_functions' ), 'phpinfo' ) ) {
4831                        ob_start();
4832                        phpinfo( 8 );
4833                        $phpinfo = ob_get_clean();
4834                if ( false !== strpos( $phpinfo, $mod ) ) {
4835                        return true;
4836                }
4837        }
4838        return $default;
4839}
4840
4841/**
4842 * Check if IIS 7+ supports pretty permalinks.
4843 *
4844 * @since 2.8.0
4845 *
4846 * @global bool $is_iis7
4847 *
4848 * @return bool Whether IIS7 supports permalinks.
4849 */
4850function iis7_supports_permalinks() {
4851        global $is_iis7;
4852
4853        $supports_permalinks = false;
4854        if ( $is_iis7 ) {
4855                /* First we check if the DOMDocument class exists. If it does not exist, then we cannot
4856                 * easily update the xml configuration file, hence we just bail out and tell user that
4857                 * pretty permalinks cannot be used.
4858                 *
4859                 * Next we check if the URL Rewrite Module 1.1 is loaded and enabled for the web site. When
4860                 * URL Rewrite 1.1 is loaded it always sets a server variable called 'IIS_UrlRewriteModule'.
4861                 * Lastly we make sure that PHP is running via FastCGI. This is important because if it runs
4862                 * via ISAPI then pretty permalinks will not work.
4863                 */
4864                $supports_permalinks = class_exists( 'DOMDocument', false ) && isset( $_SERVER['IIS_UrlRewriteModule'] ) && ( PHP_SAPI == 'cgi-fcgi' );
4865        }
4866
4867        /**
4868         * Filters whether IIS 7+ supports pretty permalinks.
4869         *
4870         * @since 2.8.0
4871         *
4872         * @param bool $supports_permalinks Whether IIS7 supports permalinks. Default false.
4873         */
4874        return apply_filters( 'iis7_supports_permalinks', $supports_permalinks );
4875}
4876
4877/**
4878 * Validates a file name and path against an allowed set of rules.
4879 *
4880 * A return value of `1` means the file path contains directory traversal.
4881 *
4882 * A return value of `2` means the file path contains a Windows drive path.
4883 *
4884 * A return value of `3` means the file is not in the allowed files list.
4885 *
4886 * @since 1.2.0
4887 *
4888 * @param string $file          File path.
4889 * @param array  $allowed_files Optional. List of allowed files.
4890 * @return int 0 means nothing is wrong, greater than 0 means something was wrong.
4891 */
4892function validate_file( $file, $allowed_files = array() ) {
4893        // `../` on its own is not allowed:
4894        if ( '../' === $file ) {
4895                return 1;
4896        }
4897
4898        // More than one occurence of `../` is not allowed:
4899        if ( preg_match_all( '#\.\./#', $file, $matches, PREG_SET_ORDER ) && ( count( $matches ) > 1 ) ) {
4900                return 1;
4901        }
4902
4903        // `../` which does not occur at the end of the path is not allowed:
4904        if ( false !== strpos( $file, '../' ) && '../' !== mb_substr( $file, -3, 3 ) ) {
4905                return 1;
4906        }
4907
4908        // Files not in the allowed file list are not allowed:
4909        if ( ! empty( $allowed_files ) && ! in_array( $file, $allowed_files ) ) {
4910                return 3;
4911        }
4912
4913        // Absolute Windows drive paths are not allowed:
4914        if ( ':' == substr( $file, 1, 1 ) ) {
4915                return 2;
4916        }
4917
4918        return 0;
4919}
4920
4921/**
4922 * Whether to force SSL used for the Administration Screens.
4923 *
4924 * @since 2.6.0
4925 *
4926 * @staticvar bool $forced
4927 *
4928 * @param string|bool $force Optional. Whether to force SSL in admin screens. Default null.
4929 * @return bool True if forced, false if not forced.
4930 */
4931function force_ssl_admin( $force = null ) {
4932        static $forced = false;
4933
4934        if ( ! is_null( $force ) ) {
4935                $old_forced = $forced;
4936                $forced     = $force;
4937                return $old_forced;
4938        }
4939
4940        return $forced;
4941}
4942
4943/**
4944 * Guess the URL for the site.
4945 *
4946 * Will remove wp-admin links to retrieve only return URLs not in the wp-admin
4947 * directory.
4948 *
4949 * @since 2.6.0
4950 *
4951 * @return string The guessed URL.
4952 */
4953function wp_guess_url() {
4954        if ( defined( 'WP_SITEURL' ) && '' != WP_SITEURL ) {
4955                $url = WP_SITEURL;
4956        } else {
4957                $abspath_fix         = str_replace( '\\', '/', ABSPATH );
4958                $script_filename_dir = dirname( $_SERVER['SCRIPT_FILENAME'] );
4959
4960                // The request is for the admin
4961                if ( strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) !== false || strpos( $_SERVER['REQUEST_URI'], 'wp-login.php' ) !== false ) {
4962                        $path = preg_replace( '#/(wp-admin/.*|wp-login.php)#i', '', $_SERVER['REQUEST_URI'] );
4963
4964                        // The request is for a file in ABSPATH
4965                } elseif ( $script_filename_dir . '/' == $abspath_fix ) {
4966                        // Strip off any file/query params in the path
4967                        $path = preg_replace( '#/[^/]*$#i', '', $_SERVER['PHP_SELF'] );
4968
4969                } else {
4970                        if ( false !== strpos( $_SERVER['SCRIPT_FILENAME'], $abspath_fix ) ) {
4971                                // Request is hitting a file inside ABSPATH
4972                                $directory = str_replace( ABSPATH, '', $script_filename_dir );
4973                                // Strip off the sub directory, and any file/query params
4974                                $path = preg_replace( '#/' . preg_quote( $directory, '#' ) . '/[^/]*$#i', '', $_SERVER['REQUEST_URI'] );
4975                        } elseif ( false !== strpos( $abspath_fix, $script_filename_dir ) ) {
4976                                // Request is hitting a file above ABSPATH
4977                                $subdirectory = substr( $abspath_fix, strpos( $abspath_fix, $script_filename_dir ) + strlen( $script_filename_dir ) );
4978                                // Strip off any file/query params from the path, appending the sub directory to the installation
4979                                $path = preg_replace( '#/[^/]*$#i', '', $_SERVER['REQUEST_URI'] ) . $subdirectory;
4980                        } else {
4981                                $path = $_SERVER['REQUEST_URI'];
4982                        }
4983                }
4984
4985                $schema = is_ssl() ? 'https://' : 'http://'; // set_url_scheme() is not defined yet
4986                $url    = $schema . $_SERVER['HTTP_HOST'] . $path;
4987        }
4988
4989        return rtrim( $url, '/' );
4990}
4991
4992/**
4993 * Temporarily suspend cache additions.
4994 *
4995 * Stops more data being added to the cache, but still allows cache retrieval.
4996 * This is useful for actions, such as imports, when a lot of data would otherwise
4997 * be almost uselessly added to the cache.
4998 *
4999 * Suspension lasts for a single page load at most. Remember to call this
5000 * function again if you wish to re-enable cache adds earlier.
5001 *
5002 * @since 3.3.0
5003 *
5004 * @staticvar bool $_suspend
5005 *
5006 * @param bool $suspend Optional. Suspends additions if true, re-enables them if false.
5007 * @return bool The current suspend setting
5008 */
5009function wp_suspend_cache_addition( $suspend = null ) {
5010        static $_suspend = false;
5011
5012        if ( is_bool( $suspend ) ) {
5013                $_suspend = $suspend;
5014        }
5015
5016        return $_suspend;
5017}
5018
5019/**
5020 * Suspend cache invalidation.
5021 *
5022 * Turns cache invalidation on and off. Useful during imports where you don't want to do
5023 * invalidations every time a post is inserted. Callers must be sure that what they are
5024 * doing won't lead to an inconsistent cache when invalidation is suspended.
5025 *
5026 * @since 2.7.0
5027 *
5028 * @global bool $_wp_suspend_cache_invalidation
5029 *
5030 * @param bool $suspend Optional. Whether to suspend or enable cache invalidation. Default true.
5031 * @return bool The current suspend setting.
5032 */
5033function wp_suspend_cache_invalidation( $suspend = true ) {
5034        global $_wp_suspend_cache_invalidation;
5035
5036        $current_suspend                = $_wp_suspend_cache_invalidation;
5037        $_wp_suspend_cache_invalidation = $suspend;
5038        return $current_suspend;
5039}
5040
5041/**
5042 * Determine whether a site is the main site of the current network.
5043 *
5044 * @since 3.0.0
5045 * @since 4.9.0 The `$network_id` parameter was added.
5046 *
5047 * @param int $site_id    Optional. Site ID to test. Defaults to current site.
5048 * @param int $network_id Optional. Network ID of the network to check for.
5049 *                        Defaults to current network.
5050 * @return bool True if $site_id is the main site of the network, or if not
5051 *              running Multisite.
5052 */
5053function is_main_site( $site_id = null, $network_id = null ) {
5054        if ( ! is_multisite() ) {
5055                return true;
5056        }
5057
5058        if ( ! $site_id ) {
5059                $site_id = get_current_blog_id();
5060        }
5061
5062        $site_id = (int) $site_id;
5063
5064        return $site_id === get_main_site_id( $network_id );
5065}
5066
5067/**
5068 * Gets the main site ID.
5069 *
5070 * @since 4.9.0
5071 *
5072 * @param int $network_id Optional. The ID of the network for which to get the main site.
5073 *                        Defaults to the current network.
5074 * @return int The ID of the main site.
5075 */
5076function get_main_site_id( $network_id = null ) {
5077        if ( ! is_multisite() ) {
5078                return get_current_blog_id();
5079        }
5080
5081        $network = get_network( $network_id );
5082        if ( ! $network ) {
5083                return 0;
5084        }
5085
5086        return $network->site_id;
5087}
5088
5089/**
5090 * Determine whether a network is the main network of the Multisite installation.
5091 *
5092 * @since 3.7.0
5093 *
5094 * @param int $network_id Optional. Network ID to test. Defaults to current network.
5095 * @return bool True if $network_id is the main network, or if not running Multisite.
5096 */
5097function is_main_network( $network_id = null ) {
5098        if ( ! is_multisite() ) {
5099                return true;
5100        }
5101
5102        if ( null === $network_id ) {
5103                $network_id = get_current_network_id();
5104        }
5105
5106        $network_id = (int) $network_id;
5107
5108        return ( $network_id === get_main_network_id() );
5109}
5110
5111/**
5112 * Get the main network ID.
5113 *
5114 * @since 4.3.0
5115 *
5116 * @return int The ID of the main network.
5117 */
5118function get_main_network_id() {
5119        if ( ! is_multisite() ) {
5120                return 1;
5121        }
5122
5123        $current_network = get_network();
5124
5125        if ( defined( 'PRIMARY_NETWORK_ID' ) ) {
5126                $main_network_id = PRIMARY_NETWORK_ID;
5127        } elseif ( isset( $current_network->id ) && 1 === (int) $current_network->id ) {
5128                // If the current network has an ID of 1, assume it is the main network.
5129                $main_network_id = 1;
5130        } else {
5131                $_networks       = get_networks(
5132                        array(
5133                                'fields' => 'ids',
5134                                'number' => 1,
5135                        )
5136                );
5137                $main_network_id = array_shift( $_networks );
5138        }
5139
5140        /**
5141         * Filters the main network ID.
5142         *
5143         * @since 4.3.0
5144         *
5145         * @param int $main_network_id The ID of the main network.
5146         */
5147        return (int) apply_filters( 'get_main_network_id', $main_network_id );
5148}
5149
5150/**
5151 * Determine whether global terms are enabled.
5152 *
5153 * @since 3.0.0
5154 *
5155 * @staticvar bool $global_terms
5156 *
5157 * @return bool True if multisite and global terms enabled.
5158 */
5159function global_terms_enabled() {
5160        if ( ! is_multisite() ) {
5161                return false;
5162        }
5163
5164        static $global_terms = null;
5165        if ( is_null( $global_terms ) ) {
5166
5167                /**
5168                 * Filters whether global terms are enabled.
5169                 *
5170                 * Passing a non-null value to the filter will effectively short-circuit the function,
5171                 * returning the value of the 'global_terms_enabled' site option instead.
5172                 *
5173                 * @since 3.0.0
5174                 *
5175                 * @param null $enabled Whether global terms are enabled.
5176                 */
5177                $filter = apply_filters( 'global_terms_enabled', null );
5178                if ( ! is_null( $filter ) ) {
5179                        $global_terms = (bool) $filter;
5180                } else {
5181                        $global_terms = (bool) get_site_option( 'global_terms_enabled', false );
5182                }
5183        }
5184        return $global_terms;
5185}
5186
5187/**
5188 * Determines whether site meta is enabled.
5189 *
5190 * This function checks whether the 'blogmeta' database table exists. The result is saved as
5191 * a setting for the main network, making it essentially a global setting. Subsequent requests
5192 * will refer to this setting instead of running the query.
5193 *
5194 * @since 5.1.0
5195 *
5196 * @global wpdb $wpdb WordPress database abstraction object.
5197 *
5198 * @return bool True if site meta is supported, false otherwise.
5199 */
5200function is_site_meta_supported() {
5201        global $wpdb;
5202
5203        if ( ! is_multisite() ) {
5204                return false;
5205        }
5206
5207        $network_id = get_main_network_id();
5208
5209        $supported = get_network_option( $network_id, 'site_meta_supported', false );
5210        if ( false === $supported ) {
5211                $supported = $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->blogmeta}'" ) ? 1 : 0;
5212
5213                update_network_option( $network_id, 'site_meta_supported', $supported );
5214        }
5215
5216        return (bool) $supported;
5217}
5218
5219/**
5220 * gmt_offset modification for smart timezone handling.
5221 *
5222 * Overrides the gmt_offset option if we have a timezone_string available.
5223 *
5224 * @since 2.8.0
5225 *
5226 * @return float|false Timezone GMT offset, false otherwise.
5227 */
5228function wp_timezone_override_offset() {
5229        if ( ! $timezone_string = get_option( 'timezone_string' ) ) {
5230                return false;
5231        }
5232
5233        $timezone_object = timezone_open( $timezone_string );
5234        $datetime_object = date_create();
5235        if ( false === $timezone_object || false === $datetime_object ) {
5236                return false;
5237        }
5238        return round( timezone_offset_get( $timezone_object, $datetime_object ) / HOUR_IN_SECONDS, 2 );
5239}
5240
5241/**
5242 * Sort-helper for timezones.
5243 *
5244 * @since 2.9.0
5245 * @access private
5246 *
5247 * @param array $a
5248 * @param array $b
5249 * @return int
5250 */
5251function _wp_timezone_choice_usort_callback( $a, $b ) {
5252        // Don't use translated versions of Etc
5253        if ( 'Etc' === $a['continent'] && 'Etc' === $b['continent'] ) {
5254                // Make the order of these more like the old dropdown
5255                if ( 'GMT+' === substr( $a['city'], 0, 4 ) && 'GMT+' === substr( $b['city'], 0, 4 ) ) {
5256                        return -1 * ( strnatcasecmp( $a['city'], $b['city'] ) );
5257                }
5258                if ( 'UTC' === $a['city'] ) {
5259                        if ( 'GMT+' === substr( $b['city'], 0, 4 ) ) {
5260                                return 1;
5261                        }
5262                        return -1;
5263                }
5264                if ( 'UTC' === $b['city'] ) {
5265                        if ( 'GMT+' === substr( $a['city'], 0, 4 ) ) {
5266                                return -1;
5267                        }
5268                        return 1;
5269                }
5270                return strnatcasecmp( $a['city'], $b['city'] );
5271        }
5272        if ( $a['t_continent'] == $b['t_continent'] ) {
5273                if ( $a['t_city'] == $b['t_city'] ) {
5274                        return strnatcasecmp( $a['t_subcity'], $b['t_subcity'] );
5275                }
5276                return strnatcasecmp( $a['t_city'], $b['t_city'] );
5277        } else {
5278                // Force Etc to the bottom of the list
5279                if ( 'Etc' === $a['continent'] ) {
5280                        return 1;
5281                }
5282                if ( 'Etc' === $b['continent'] ) {
5283                        return -1;
5284                }
5285                return strnatcasecmp( $a['t_continent'], $b['t_continent'] );
5286        }
5287}
5288
5289/**
5290 * Gives a nicely-formatted list of timezone strings.
5291 *
5292 * @since 2.9.0
5293 * @since 4.7.0 Added the `$locale` parameter.
5294 *
5295 * @staticvar bool $mo_loaded
5296 * @staticvar string $locale_loaded
5297 *
5298 * @param string $selected_zone Selected timezone.
5299 * @param string $locale        Optional. Locale to load the timezones in. Default current site locale.
5300 * @return string
5301 */
5302function wp_timezone_choice( $selected_zone, $locale = null ) {
5303        static $mo_loaded = false, $locale_loaded = null;
5304
5305        $continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific' );
5306
5307        // Load translations for continents and cities.
5308        if ( ! $mo_loaded || $locale !== $locale_loaded ) {
5309                $locale_loaded = $locale ? $locale : get_locale();
5310                $mofile        = WP_LANG_DIR . '/continents-cities-' . $locale_loaded . '.mo';
5311                unload_textdomain( 'continents-cities' );
5312                load_textdomain( 'continents-cities', $mofile );
5313                $mo_loaded = true;
5314        }
5315
5316        $zonen = array();
5317        foreach ( timezone_identifiers_list() as $zone ) {
5318                $zone = explode( '/', $zone );
5319                if ( ! in_array( $zone[0], $continents ) ) {
5320                        continue;
5321                }
5322
5323                // This determines what gets set and translated - we don't translate Etc/* strings here, they are done later
5324                $exists    = array(
5325                        0 => ( isset( $zone[0] ) && $zone[0] ),
5326                        1 => ( isset( $zone[1] ) && $zone[1] ),
5327                        2 => ( isset( $zone[2] ) && $zone[2] ),
5328                );
5329                $exists[3] = ( $exists[0] && 'Etc' !== $zone[0] );
5330                $exists[4] = ( $exists[1] && $exists[3] );
5331                $exists[5] = ( $exists[2] && $exists[3] );
5332
5333                // phpcs:disable WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText
5334                $zonen[] = array(
5335                        'continent'   => ( $exists[0] ? $zone[0] : '' ),
5336                        'city'        => ( $exists[1] ? $zone[1] : '' ),
5337                        'subcity'     => ( $exists[2] ? $zone[2] : '' ),
5338                        't_continent' => ( $exists[3] ? translate( str_replace( '_', ' ', $zone[0] ), 'continents-cities' ) : '' ),
5339                        't_city'      => ( $exists[4] ? translate( str_replace( '_', ' ', $zone[1] ), 'continents-cities' ) : '' ),
5340                        't_subcity'   => ( $exists[5] ? translate( str_replace( '_', ' ', $zone[2] ), 'continents-cities' ) : '' ),
5341                );
5342                // phpcs:enable
5343        }
5344        usort( $zonen, '_wp_timezone_choice_usort_callback' );
5345
5346        $structure = array();
5347
5348        if ( empty( $selected_zone ) ) {
5349                $structure[] = '<option selected="selected" value="">' . __( 'Select a city' ) . '</option>';
5350        }
5351
5352        foreach ( $zonen as $key => $zone ) {
5353                // Build value in an array to join later
5354                $value = array( $zone['continent'] );
5355
5356                if ( empty( $zone['city'] ) ) {
5357                        // It's at the continent level (generally won't happen)
5358                        $display = $zone['t_continent'];
5359                } else {
5360                        // It's inside a continent group
5361
5362                        // Continent optgroup
5363                        if ( ! isset( $zonen[ $key - 1 ] ) || $zonen[ $key - 1 ]['continent'] !== $zone['continent'] ) {
5364                                $label       = $zone['t_continent'];
5365                                $structure[] = '<optgroup label="' . esc_attr( $label ) . '">';
5366                        }
5367
5368                        // Add the city to the value
5369                        $value[] = $zone['city'];
5370
5371                        $display = $zone['t_city'];
5372                        if ( ! empty( $zone['subcity'] ) ) {
5373                                // Add the subcity to the value
5374                                $value[]  = $zone['subcity'];
5375                                $display .= ' - ' . $zone['t_subcity'];
5376                        }
5377                }
5378
5379                // Build the value
5380                $value    = join( '/', $value );
5381                $selected = '';
5382                if ( $value === $selected_zone ) {
5383                        $selected = 'selected="selected" ';
5384                }
5385                $structure[] = '<option ' . $selected . 'value="' . esc_attr( $value ) . '">' . esc_html( $display ) . '</option>';
5386
5387                // Close continent optgroup
5388                if ( ! empty( $zone['city'] ) && ( ! isset( $zonen[ $key + 1 ] ) || ( isset( $zonen[ $key + 1 ] ) && $zonen[ $key + 1 ]['continent'] !== $zone['continent'] ) ) ) {
5389                        $structure[] = '</optgroup>';
5390                }
5391        }
5392
5393        // Do UTC
5394        $structure[] = '<optgroup label="' . esc_attr__( 'UTC' ) . '">';
5395        $selected    = '';
5396        if ( 'UTC' === $selected_zone ) {
5397                $selected = 'selected="selected" ';
5398        }
5399        $structure[] = '<option ' . $selected . 'value="' . esc_attr( 'UTC' ) . '">' . __( 'UTC' ) . '</option>';
5400        $structure[] = '</optgroup>';
5401
5402        // Do manual UTC offsets
5403        $structure[]  = '<optgroup label="' . esc_attr__( 'Manual Offsets' ) . '">';
5404        $offset_range = array(
5405                -12,
5406                -11.5,
5407                -11,
5408                -10.5,
5409                -10,
5410                -9.5,
5411                -9,
5412                -8.5,
5413                -8,
5414                -7.5,
5415                -7,
5416                -6.5,
5417                -6,
5418                -5.5,
5419                -5,
5420                -4.5,
5421                -4,
5422                -3.5,
5423                -3,
5424                -2.5,
5425                -2,
5426                -1.5,
5427                -1,
5428                -0.5,
5429                0,
5430                0.5,
5431                1,
5432                1.5,
5433                2,
5434                2.5,
5435                3,
5436                3.5,
5437                4,
5438                4.5,
5439                5,
5440                5.5,
5441                5.75,
5442                6,
5443                6.5,
5444                7,
5445                7.5,
5446                8,
5447                8.5,
5448                8.75,
5449                9,
5450                9.5,
5451                10,
5452                10.5,
5453                11,
5454                11.5,
5455                12,
5456                12.75,
5457                13,
5458                13.75,
5459                14,
5460        );
5461        foreach ( $offset_range as $offset ) {
5462                if ( 0 <= $offset ) {
5463                        $offset_name = '+' . $offset;
5464                } else {
5465                        $offset_name = (string) $offset;
5466                }
5467
5468                $offset_value = $offset_name;
5469                $offset_name  = str_replace( array( '.25', '.5', '.75' ), array( ':15', ':30', ':45' ), $offset_name );
5470                $offset_name  = 'UTC' . $offset_name;
5471                $offset_value = 'UTC' . $offset_value;
5472                $selected     = '';
5473                if ( $offset_value === $selected_zone ) {
5474                        $selected = 'selected="selected" ';
5475                }
5476                $structure[] = '<option ' . $selected . 'value="' . esc_attr( $offset_value ) . '">' . esc_html( $offset_name ) . '</option>';
5477
5478        }
5479        $structure[] = '</optgroup>';
5480
5481        return join( "\n", $structure );
5482}
5483
5484/**
5485 * Strip close comment and close php tags from file headers used by WP.
5486 *
5487 * @since 2.8.0
5488 * @access private
5489 *
5490 * @see https://core.trac.wordpress.org/ticket/8497
5491 *
5492 * @param string $str Header comment to clean up.
5493 * @return string
5494 */
5495function _cleanup_header_comment( $str ) {
5496        return trim( preg_replace( '/\s*(?:\*\/|\?>).*/', '', $str ) );
5497}
5498
5499/**
5500 * Permanently delete comments or posts of any type that have held a status
5501 * of 'trash' for the number of days defined in EMPTY_TRASH_DAYS.
5502 *
5503 * The default value of `EMPTY_TRASH_DAYS` is 30 (days).
5504 *
5505 * @since 2.9.0
5506 *
5507 * @global wpdb $wpdb WordPress database abstraction object.
5508 */
5509function wp_scheduled_delete() {
5510        global $wpdb;
5511
5512        $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
5513
5514        $posts_to_delete = $wpdb->get_results( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_trash_meta_time' AND meta_value < %d", $delete_timestamp ), ARRAY_A );
5515
5516        foreach ( (array) $posts_to_delete as $post ) {
5517                $post_id = (int) $post['post_id'];
5518                if ( ! $post_id ) {
5519                        continue;
5520                }
5521
5522                $del_post = get_post( $post_id );
5523
5524                if ( ! $del_post || 'trash' != $del_post->post_status ) {
5525                        delete_post_meta( $post_id, '_wp_trash_meta_status' );
5526                        delete_post_meta( $post_id, '_wp_trash_meta_time' );
5527                } else {
5528                        wp_delete_post( $post_id );
5529                }
5530        }
5531
5532        $comments_to_delete = $wpdb->get_results( $wpdb->prepare( "SELECT comment_id FROM $wpdb->commentmeta WHERE meta_key = '_wp_trash_meta_time' AND meta_value < %d", $delete_timestamp ), ARRAY_A );
5533
5534        foreach ( (array) $comments_to_delete as $comment ) {
5535                $comment_id = (int) $comment['comment_id'];
5536                if ( ! $comment_id ) {
5537                        continue;
5538                }
5539
5540                $del_comment = get_comment( $comment_id );
5541
5542                if ( ! $del_comment || 'trash' != $del_comment->comment_approved ) {
5543                        delete_comment_meta( $comment_id, '_wp_trash_meta_time' );
5544                        delete_comment_meta( $comment_id, '_wp_trash_meta_status' );
5545                } else {
5546                        wp_delete_comment( $del_comment );
5547                }
5548        }
5549}
5550
5551/**
5552 * Retrieve metadata from a file.
5553 *
5554 * Searches for metadata in the first 8kiB of a file, such as a plugin or theme.
5555 * Each piece of metadata must be on its own line. Fields can not span multiple
5556 * lines, the value will get cut at the end of the first line.
5557 *
5558 * If the file data is not within that first 8kiB, then the author should correct
5559 * their plugin file and move the data headers to the top.
5560 *
5561 * @link https://codex.wordpress.org/File_Header
5562 *
5563 * @since 2.9.0
5564 *
5565 * @param string $file            Absolute path to the file.
5566 * @param array  $default_headers List of headers, in the format `array('HeaderKey' => 'Header Name')`.
5567 * @param string $context         Optional. If specified adds filter hook {@see 'extra_$context_headers'}.
5568 *                                Default empty.
5569 * @return array Array of file headers in `HeaderKey => Header Value` format.
5570 */
5571function get_file_data( $file, $default_headers, $context = '' ) {
5572        // We don't need to write to the file, so just open for reading.
5573        $fp = fopen( $file, 'r' );
5574
5575        // Pull only the first 8kiB of the file in.
5576        $file_data = fread( $fp, 8192 );
5577
5578        // PHP will close file handle, but we are good citizens.
5579        fclose( $fp );
5580
5581        // Make sure we catch CR-only line endings.
5582        $file_data = str_replace( "\r", "\n", $file_data );
5583
5584        /**
5585         * Filters extra file headers by context.
5586         *
5587         * The dynamic portion of the hook name, `$context`, refers to
5588         * the context where extra headers might be loaded.
5589         *
5590         * @since 2.9.0
5591         *
5592         * @param array $extra_context_headers Empty array by default.
5593         */
5594        if ( $context && $extra_headers = apply_filters( "extra_{$context}_headers", array() ) ) {
5595                $extra_headers = array_combine( $extra_headers, $extra_headers ); // keys equal values
5596                $all_headers   = array_merge( $extra_headers, (array) $default_headers );
5597        } else {
5598                $all_headers = $default_headers;
5599        }
5600
5601        foreach ( $all_headers as $field => $regex ) {
5602                if ( preg_match( '/^[ \t\/*#@]*' . preg_quote( $regex, '/' ) . ':(.*)$/mi', $file_data, $match ) && $match[1] ) {
5603                        $all_headers[ $field ] = _cleanup_header_comment( $match[1] );
5604                } else {
5605                        $all_headers[ $field ] = '';
5606                }
5607        }
5608
5609        return $all_headers;
5610}
5611
5612/**
5613 * Returns true.
5614 *
5615 * Useful for returning true to filters easily.
5616 *
5617 * @since 3.0.0
5618 *
5619 * @see __return_false()
5620 *
5621 * @return true True.
5622 */
5623function __return_true() {
5624        return true;
5625}
5626
5627/**
5628 * Returns false.
5629 *
5630 * Useful for returning false to filters easily.
5631 *
5632 * @since 3.0.0
5633 *
5634 * @see __return_true()
5635 *
5636 * @return false False.
5637 */
5638function __return_false() {
5639        return false;
5640}
5641
5642/**
5643 * Returns 0.
5644 *
5645 * Useful for returning 0 to filters easily.
5646 *
5647 * @since 3.0.0
5648 *
5649 * @return int 0.
5650 */
5651function __return_zero() {
5652        return 0;
5653}
5654
5655/**
5656 * Returns an empty array.
5657 *
5658 * Useful for returning an empty array to filters easily.
5659 *
5660 * @since 3.0.0
5661 *
5662 * @return array Empty array.
5663 */
5664function __return_empty_array() {
5665        return array();
5666}
5667
5668/**
5669 * Returns null.
5670 *
5671 * Useful for returning null to filters easily.
5672 *
5673 * @since 3.4.0
5674 *
5675 * @return null Null value.
5676 */
5677function __return_null() {
5678        return null;
5679}
5680
5681/**
5682 * Returns an empty string.
5683 *
5684 * Useful for returning an empty string to filters easily.
5685 *
5686 * @since 3.7.0
5687 *
5688 * @see __return_null()
5689 *
5690 * @return string Empty string.
5691 */
5692function __return_empty_string() {
5693        return '';
5694}
5695
5696/**
5697 * Send a HTTP header to disable content type sniffing in browsers which support it.
5698 *
5699 * @since 3.0.0
5700 *
5701 * @see https://blogs.msdn.com/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx
5702 * @see https://src.chromium.org/viewvc/chrome?view=rev&revision=6985
5703 */
5704function send_nosniff_header() {
5705        @header( 'X-Content-Type-Options: nosniff' );
5706}
5707
5708/**
5709 * Return a MySQL expression for selecting the week number based on the start_of_week option.
5710 *
5711 * @ignore
5712 * @since 3.0.0
5713