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