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