~canonical-sysadmins/wordpress/5.0

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