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