Make WordPress Core

Ticket #47763: functions.php

File functions.php, 214.5 KB (added by dxd5001, 5 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[