Issues (2010)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

wp-includes/functions.php (21 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
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 View Code Duplication
		case 'timestamp':
66
			return ( $gmt ) ? time() : time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
67 View Code Duplication
		default:
68
			return ( $gmt ) ? date( $type ) : date( $type, time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) );
69
	}
70
}
71
72
/**
73
 * Retrieve the date in localized format, based on timestamp.
74
 *
75
 * If the locale specifies the locale month and weekday, then the locale will
76
 * take over the format for the date. If it isn't, then the date format string
77
 * will be used instead.
78
 *
79
 * @since 0.71
80
 *
81
 * @global WP_Locale $wp_locale
82
 *
83
 * @param string   $dateformatstring Format to display the date.
84
 * @param bool|int $unixtimestamp    Optional. Unix timestamp. Default false.
85
 * @param bool     $gmt              Optional. Whether to use GMT timezone. Default false.
86
 *
87
 * @return string The date, translated if locale specifies it.
88
 */
89
function date_i18n( $dateformatstring, $unixtimestamp = false, $gmt = false ) {
90
	global $wp_locale;
91
	$i = $unixtimestamp;
92
93
	if ( false === $i ) {
94
		if ( ! $gmt )
95
			$i = current_time( 'timestamp' );
96
		else
97
			$i = time();
98
		// we should not let date() interfere with our
99
		// specially computed timestamp
100
		$gmt = true;
101
	}
102
103
	/*
104
	 * Store original value for language with untypical grammars.
105
	 * See https://core.trac.wordpress.org/ticket/9396
106
	 */
107
	$req_format = $dateformatstring;
108
109
	$datefunc = $gmt? 'gmdate' : 'date';
110
111
	if ( ( !empty( $wp_locale->month ) ) && ( !empty( $wp_locale->weekday ) ) ) {
112
		$datemonth = $wp_locale->get_month( $datefunc( 'm', $i ) );
113
		$datemonth_abbrev = $wp_locale->get_month_abbrev( $datemonth );
114
		$dateweekday = $wp_locale->get_weekday( $datefunc( 'w', $i ) );
115
		$dateweekday_abbrev = $wp_locale->get_weekday_abbrev( $dateweekday );
116
		$datemeridiem = $wp_locale->get_meridiem( $datefunc( 'a', $i ) );
117
		$datemeridiem_capital = $wp_locale->get_meridiem( $datefunc( 'A', $i ) );
118
		$dateformatstring = ' '.$dateformatstring;
119
		$dateformatstring = preg_replace( "/([^\\\])D/", "\\1" . backslashit( $dateweekday_abbrev ), $dateformatstring );
120
		$dateformatstring = preg_replace( "/([^\\\])F/", "\\1" . backslashit( $datemonth ), $dateformatstring );
121
		$dateformatstring = preg_replace( "/([^\\\])l/", "\\1" . backslashit( $dateweekday ), $dateformatstring );
122
		$dateformatstring = preg_replace( "/([^\\\])M/", "\\1" . backslashit( $datemonth_abbrev ), $dateformatstring );
123
		$dateformatstring = preg_replace( "/([^\\\])a/", "\\1" . backslashit( $datemeridiem ), $dateformatstring );
124
		$dateformatstring = preg_replace( "/([^\\\])A/", "\\1" . backslashit( $datemeridiem_capital ), $dateformatstring );
125
126
		$dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
127
	}
128
	$timezone_formats = array( 'P', 'I', 'O', 'T', 'Z', 'e' );
129
	$timezone_formats_re = implode( '|', $timezone_formats );
130
	if ( preg_match( "/$timezone_formats_re/", $dateformatstring ) ) {
131
		$timezone_string = get_option( 'timezone_string' );
132
		if ( $timezone_string ) {
133
			$timezone_object = timezone_open( $timezone_string );
134
			$date_object = date_create( null, $timezone_object );
135
			foreach ( $timezone_formats as $timezone_format ) {
136
				if ( false !== strpos( $dateformatstring, $timezone_format ) ) {
137
					$formatted = date_format( $date_object, $timezone_format );
138
					$dateformatstring = ' '.$dateformatstring;
139
					$dateformatstring = preg_replace( "/([^\\\])$timezone_format/", "\\1" . backslashit( $formatted ), $dateformatstring );
140
					$dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
141
				}
142
			}
143
		}
144
	}
145
	$j = @$datefunc( $dateformatstring, $i );
146
147
	/**
148
	 * Filters the date formatted based on the locale.
149
	 *
150
	 * @since 2.8.0
151
	 *
152
	 * @param string $j          Formatted date string.
153
	 * @param string $req_format Format to display the date.
154
	 * @param int    $i          Unix timestamp.
155
	 * @param bool   $gmt        Whether to convert to GMT for time. Default false.
156
	 */
157
	$j = apply_filters( 'date_i18n', $j, $req_format, $i, $gmt );
158
	return $j;
159
}
160
161
/**
162
 * Determines if the date should be declined.
163
 *
164
 * If the locale specifies that month names require a genitive case in certain
165
 * formats (like 'j F Y'), the month name will be replaced with a correct form.
166
 *
167
 * @since 4.4.0
168
 *
169
 * @param string $date Formatted date string.
170
 * @return string The date, declined if locale specifies it.
171
 */
172
function wp_maybe_decline_date( $date ) {
173
	global $wp_locale;
174
175
	// i18n functions are not available in SHORTINIT mode
176
	if ( ! function_exists( '_x' ) ) {
177
		return $date;
178
	}
179
180
	/* translators: If months in your language require a genitive case,
181
	 * translate this to 'on'. Do not translate into your own language.
182
	 */
183
	if ( 'on' === _x( 'off', 'decline months names: on or off' ) ) {
184
		// Match a format like 'j F Y' or 'j. F'
185
		if ( @preg_match( '#^\d{1,2}\.? [^\d ]+#u', $date ) ) {
186
			$months          = $wp_locale->month;
187
			$months_genitive = $wp_locale->month_genitive;
188
189
			foreach ( $months as $key => $month ) {
190
				$months[ $key ] = '# ' . $month . '( |$)#u';
191
			}
192
193
			foreach ( $months_genitive as $key => $month ) {
194
				$months_genitive[ $key ] = ' ' . $month . '$1';
195
			}
196
197
			$date = preg_replace( $months, $months_genitive, $date );
198
		}
199
	}
200
201
	// Used for locale-specific rules
202
	$locale = get_locale();
203
204
	if ( 'ca' === $locale ) {
205
		// " de abril| de agost| de octubre..." -> " d'abril| d'agost| d'octubre..."
206
		$date = preg_replace( '# de ([ao])#i', " d'\\1", $date );
207
	}
208
209
	return $date;
210
}
211
212
/**
213
 * Convert float number to format based on the locale.
214
 *
215
 * @since 2.3.0
216
 *
217
 * @global WP_Locale $wp_locale
218
 *
219
 * @param float $number   The number to convert based on locale.
220
 * @param int   $decimals Optional. Precision of the number of decimal places. Default 0.
221
 * @return string Converted number in string format.
222
 */
223
function number_format_i18n( $number, $decimals = 0 ) {
224
	global $wp_locale;
225
226
	if ( isset( $wp_locale ) ) {
227
		$formatted = number_format( $number, absint( $decimals ), $wp_locale->number_format['decimal_point'], $wp_locale->number_format['thousands_sep'] );
228
	} else {
229
		$formatted = number_format( $number, absint( $decimals ) );
230
	}
231
232
	/**
233
	 * Filters the number formatted based on the locale.
234
	 *
235
	 * @since  2.8.0
236
	 *
237
	 * @param string $formatted Converted number in string format.
238
	 */
239
	return apply_filters( 'number_format_i18n', $formatted );
240
}
241
242
/**
243
 * Convert number of bytes largest unit bytes will fit into.
244
 *
245
 * It is easier to read 1 KB than 1024 bytes and 1 MB than 1048576 bytes. Converts
246
 * number of bytes to human readable number by taking the number of that unit
247
 * that the bytes will go into it. Supports TB value.
248
 *
249
 * Please note that integers in PHP are limited to 32 bits, unless they are on
250
 * 64 bit architecture, then they have 64 bit size. If you need to place the
251
 * larger size then what PHP integer type will hold, then use a string. It will
252
 * be converted to a double, which should always have 64 bit length.
253
 *
254
 * Technically the correct unit names for powers of 1024 are KiB, MiB etc.
255
 *
256
 * @since 2.3.0
257
 *
258
 * @param int|string $bytes    Number of bytes. Note max integer size for integers.
259
 * @param int        $decimals Optional. Precision of number of decimal places. Default 0.
260
 * @return string|false False on failure. Number string on success.
261
 */
262
function size_format( $bytes, $decimals = 0 ) {
263
	$quant = array(
264
		'TB' => TB_IN_BYTES,
265
		'GB' => GB_IN_BYTES,
266
		'MB' => MB_IN_BYTES,
267
		'KB' => KB_IN_BYTES,
268
		'B'  => 1,
269
	);
270
271
	if ( 0 === $bytes ) {
272
		return number_format_i18n( 0, $decimals ) . ' B';
273
	}
274
275
	foreach ( $quant as $unit => $mag ) {
276
		if ( doubleval( $bytes ) >= $mag ) {
277
			return number_format_i18n( $bytes / $mag, $decimals ) . ' ' . $unit;
278
		}
279
	}
280
281
	return false;
282
}
283
284
/**
285
 * Get the week start and end from the datetime or date string from MySQL.
286
 *
287
 * @since 0.71
288
 *
289
 * @param string     $mysqlstring   Date or datetime field type from MySQL.
290
 * @param int|string $start_of_week Optional. Start of the week as an integer. Default empty string.
291
 * @return array Keys are 'start' and 'end'.
292
 */
293
function get_weekstartend( $mysqlstring, $start_of_week = '' ) {
294
	// MySQL string year.
295
	$my = substr( $mysqlstring, 0, 4 );
296
297
	// MySQL string month.
298
	$mm = substr( $mysqlstring, 8, 2 );
299
300
	// MySQL string day.
301
	$md = substr( $mysqlstring, 5, 2 );
302
303
	// The timestamp for MySQL string day.
304
	$day = mktime( 0, 0, 0, $md, $mm, $my );
305
306
	// The day of the week from the timestamp.
307
	$weekday = date( 'w', $day );
308
309
	if ( !is_numeric($start_of_week) )
310
		$start_of_week = get_option( 'start_of_week' );
311
312
	if ( $weekday < $start_of_week )
313
		$weekday += 7;
314
315
	// The most recent week start day on or before $day.
316
	$start = $day - DAY_IN_SECONDS * ( $weekday - $start_of_week );
317
318
	// $start + 1 week - 1 second.
319
	$end = $start + WEEK_IN_SECONDS - 1;
320
	return compact( 'start', 'end' );
321
}
322
323
/**
324
 * Unserialize value only if it was serialized.
325
 *
326
 * @since 2.0.0
327
 *
328
 * @param string $original Maybe unserialized original, if is needed.
329
 * @return mixed Unserialized data can be any type.
330
 */
331
function maybe_unserialize( $original ) {
332
	if ( is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in
333
		return @unserialize( $original );
334
	return $original;
335
}
336
337
/**
338
 * Check value to find if it was serialized.
339
 *
340
 * If $data is not an string, then returned value will always be false.
341
 * Serialized data is always a string.
342
 *
343
 * @since 2.0.5
344
 *
345
 * @param string $data   Value to check to see if was serialized.
346
 * @param bool   $strict Optional. Whether to be strict about the end of the string. Default true.
347
 * @return bool False if not serialized and true if it was.
348
 */
349
function is_serialized( $data, $strict = true ) {
350
	// if it isn't a string, it isn't serialized.
351
	if ( ! is_string( $data ) ) {
352
		return false;
353
	}
354
	$data = trim( $data );
355
 	if ( 'N;' == $data ) {
356
		return true;
357
	}
358
	if ( strlen( $data ) < 4 ) {
359
		return false;
360
	}
361
	if ( ':' !== $data[1] ) {
362
		return false;
363
	}
364
	if ( $strict ) {
365
		$lastc = substr( $data, -1 );
366
		if ( ';' !== $lastc && '}' !== $lastc ) {
367
			return false;
368
		}
369
	} else {
370
		$semicolon = strpos( $data, ';' );
371
		$brace     = strpos( $data, '}' );
372
		// Either ; or } must exist.
373
		if ( false === $semicolon && false === $brace )
374
			return false;
375
		// But neither must be in the first X characters.
376
		if ( false !== $semicolon && $semicolon < 3 )
377
			return false;
378
		if ( false !== $brace && $brace < 4 )
379
			return false;
380
	}
381
	$token = $data[0];
382
	switch ( $token ) {
383
		case 's' :
384
			if ( $strict ) {
385
				if ( '"' !== substr( $data, -2, 1 ) ) {
386
					return false;
387
				}
388
			} elseif ( false === strpos( $data, '"' ) ) {
389
				return false;
390
			}
391
			// or else fall through
392
		case 'a' :
393
		case 'O' :
394
			return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
395
		case 'b' :
396
		case 'i' :
397
		case 'd' :
398
			$end = $strict ? '$' : '';
399
			return (bool) preg_match( "/^{$token}:[0-9.E-]+;$end/", $data );
400
	}
401
	return false;
402
}
403
404
/**
405
 * Check whether serialized data is of string type.
406
 *
407
 * @since 2.0.5
408
 *
409
 * @param string $data Serialized data.
410
 * @return bool False if not a serialized string, true if it is.
411
 */
412
function is_serialized_string( $data ) {
413
	// if it isn't a string, it isn't a serialized string.
414
	if ( ! is_string( $data ) ) {
415
		return false;
416
	}
417
	$data = trim( $data );
418
	if ( strlen( $data ) < 4 ) {
419
		return false;
420
	} elseif ( ':' !== $data[1] ) {
421
		return false;
422
	} elseif ( ';' !== substr( $data, -1 ) ) {
423
		return false;
424
	} elseif ( $data[0] !== 's' ) {
425
		return false;
426
	} elseif ( '"' !== substr( $data, -2, 1 ) ) {
427
		return false;
428
	} else {
429
		return true;
430
	}
431
}
432
433
/**
434
 * Serialize data, if needed.
435
 *
436
 * @since 2.0.5
437
 *
438
 * @param string|array|object $data Data that might be serialized.
439
 * @return mixed A scalar data
440
 */
441
function maybe_serialize( $data ) {
442
	if ( is_array( $data ) || is_object( $data ) )
443
		return serialize( $data );
444
445
	// Double serialization is required for backward compatibility.
446
	// See https://core.trac.wordpress.org/ticket/12930
447
	// Also the world will end. See WP 3.6.1.
448
	if ( is_serialized( $data, false ) )
449
		return serialize( $data );
450
451
	return $data;
452
}
453
454
/**
455
 * Retrieve post title from XMLRPC XML.
456
 *
457
 * If the title element is not part of the XML, then the default post title from
458
 * the $post_default_title will be used instead.
459
 *
460
 * @since 0.71
461
 *
462
 * @global string $post_default_title Default XML-RPC post title.
463
 *
464
 * @param string $content XMLRPC XML Request content
465
 * @return string Post title
466
 */
467
function xmlrpc_getposttitle( $content ) {
468
	global $post_default_title;
469
	if ( preg_match( '/<title>(.+?)<\/title>/is', $content, $matchtitle ) ) {
470
		$post_title = $matchtitle[1];
471
	} else {
472
		$post_title = $post_default_title;
473
	}
474
	return $post_title;
475
}
476
477
/**
478
 * Retrieve the post category or categories from XMLRPC XML.
479
 *
480
 * If the category element is not found, then the default post category will be
481
 * used. The return type then would be what $post_default_category. If the
482
 * category is found, then it will always be an array.
483
 *
484
 * @since 0.71
485
 *
486
 * @global string $post_default_category Default XML-RPC post category.
487
 *
488
 * @param string $content XMLRPC XML Request content
489
 * @return string|array List of categories or category name.
490
 */
491
function xmlrpc_getpostcategory( $content ) {
492
	global $post_default_category;
493
	if ( preg_match( '/<category>(.+?)<\/category>/is', $content, $matchcat ) ) {
494
		$post_category = trim( $matchcat[1], ',' );
495
		$post_category = explode( ',', $post_category );
496
	} else {
497
		$post_category = $post_default_category;
498
	}
499
	return $post_category;
500
}
501
502
/**
503
 * XMLRPC XML content without title and category elements.
504
 *
505
 * @since 0.71
506
 *
507
 * @param string $content XML-RPC XML Request content.
508
 * @return string XMLRPC XML Request content without title and category elements.
509
 */
510 View Code Duplication
function xmlrpc_removepostdata( $content ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
511
	$content = preg_replace( '/<title>(.+?)<\/title>/si', '', $content );
512
	$content = preg_replace( '/<category>(.+?)<\/category>/si', '', $content );
513
	$content = trim( $content );
514
	return $content;
515
}
516
517
/**
518
 * Use RegEx to extract URLs from arbitrary content.
519
 *
520
 * @since 3.7.0
521
 *
522
 * @param string $content Content to extract URLs from.
523
 * @return array URLs found in passed string.
524
 */
525
function wp_extract_urls( $content ) {
526
	preg_match_all(
527
		"#([\"']?)("
528
			. "(?:([\w-]+:)?//?)"
529
			. "[^\s()<>]+"
530
			. "[.]"
531
			. "(?:"
532
				. "\([\w\d]+\)|"
533
				. "(?:"
534
					. "[^`!()\[\]{};:'\".,<>«»“”‘’\s]|"
535
					. "(?:[:]\d+)?/?"
536
				. ")+"
537
			. ")"
538
		. ")\\1#",
539
		$content,
540
		$post_links
541
	);
542
543
	$post_links = array_unique( array_map( 'html_entity_decode', $post_links[2] ) );
544
545
	return array_values( $post_links );
546
}
547
548
/**
549
 * Check content for video and audio links to add as enclosures.
550
 *
551
 * Will not add enclosures that have already been added and will
552
 * remove enclosures that are no longer in the post. This is called as
553
 * pingbacks and trackbacks.
554
 *
555
 * @since 1.5.0
556
 *
557
 * @global wpdb $wpdb WordPress database abstraction object.
558
 *
559
 * @param string $content Post Content.
560
 * @param int    $post_ID Post ID.
561
 */
562
function do_enclose( $content, $post_ID ) {
563
	global $wpdb;
564
565
	//TODO: Tidy this ghetto code up and make the debug code optional
566
	include_once( ABSPATH . WPINC . '/class-IXR.php' );
567
568
	$post_links = array();
569
570
	$pung = get_enclosed( $post_ID );
571
572
	$post_links_temp = wp_extract_urls( $content );
573
574
	foreach ( $pung as $link_test ) {
575
		if ( ! in_array( $link_test, $post_links_temp ) ) { // link no longer in post
576
			$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 ) . '%') );
577
			foreach ( $mids as $mid )
578
				delete_metadata_by_mid( 'post', $mid );
579
		}
580
	}
581
582
	foreach ( (array) $post_links_temp as $link_test ) {
583
		if ( !in_array( $link_test, $pung ) ) { // If we haven't pung it already
584
			$test = @parse_url( $link_test );
585
			if ( false === $test )
586
				continue;
587 View Code Duplication
			if ( isset( $test['query'] ) )
588
				$post_links[] = $link_test;
589
			elseif ( isset($test['path']) && ( $test['path'] != '/' ) &&  ($test['path'] != '' ) )
590
				$post_links[] = $link_test;
591
		}
592
	}
593
594
	/**
595
	 * Filters the list of enclosure links before querying the database.
596
	 *
597
	 * Allows for the addition and/or removal of potential enclosures to save
598
	 * to postmeta before checking the database for existing enclosures.
599
	 *
600
	 * @since 4.4.0
601
	 *
602
	 * @param array $post_links An array of enclosure links.
603
	 * @param int   $post_ID    Post ID.
604
	 */
605
	$post_links = apply_filters( 'enclosure_links', $post_links, $post_ID );
606
607
	foreach ( (array) $post_links as $url ) {
608
		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 ) . '%' ) ) ) {
609
610
			if ( $headers = wp_get_http_headers( $url) ) {
611
				$len = isset( $headers['content-length'] ) ? (int) $headers['content-length'] : 0;
612
				$type = isset( $headers['content-type'] ) ? $headers['content-type'] : '';
613
				$allowed_types = array( 'video', 'audio' );
614
615
				// Check to see if we can figure out the mime type from
616
				// the extension
617
				$url_parts = @parse_url( $url );
618
				if ( false !== $url_parts ) {
619
					$extension = pathinfo( $url_parts['path'], PATHINFO_EXTENSION );
620
					if ( !empty( $extension ) ) {
621
						foreach ( wp_get_mime_types() as $exts => $mime ) {
622
							if ( preg_match( '!^(' . $exts . ')$!i', $extension ) ) {
623
								$type = $mime;
624
								break;
625
							}
626
						}
627
					}
628
				}
629
630
				if ( in_array( substr( $type, 0, strpos( $type, "/" ) ), $allowed_types ) ) {
631
					add_post_meta( $post_ID, 'enclosure', "$url\n$len\n$mime\n" );
0 ignored issues
show
The variable $mime seems to be defined by a foreach iteration on line 621. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
632
				}
633
			}
634
		}
635
	}
636
}
637
638
/**
639
 * Retrieve HTTP Headers from URL.
640
 *
641
 * @since 1.5.1
642
 *
643
 * @param string $url        URL to retrieve HTTP headers from.
644
 * @param bool   $deprecated Not Used.
645
 * @return bool|string False on failure, headers on success.
646
 */
647
function wp_get_http_headers( $url, $deprecated = false ) {
648
	if ( !empty( $deprecated ) )
649
		_deprecated_argument( __FUNCTION__, '2.7.0' );
650
651
	$response = wp_safe_remote_head( $url );
652
653
	if ( is_wp_error( $response ) )
654
		return false;
655
656
	return wp_remote_retrieve_headers( $response );
657
}
658
659
/**
660
 * Whether the publish date of the current post in the loop is different from the
661
 * publish date of the previous post in the loop.
662
 *
663
 * @since 0.71
664
 *
665
 * @global string $currentday  The day of the current post in the loop.
666
 * @global string $previousday The day of the previous post in the loop.
667
 *
668
 * @return int 1 when new day, 0 if not a new day.
669
 */
670
function is_new_day() {
671
	global $currentday, $previousday;
672
	if ( $currentday != $previousday )
673
		return 1;
674
	else
675
		return 0;
676
}
677
678
/**
679
 * Build URL query based on an associative and, or indexed array.
680
 *
681
 * This is a convenient function for easily building url queries. It sets the
682
 * separator to '&' and uses _http_build_query() function.
683
 *
684
 * @since 2.3.0
685
 *
686
 * @see _http_build_query() Used to build the query
687
 * @link https://secure.php.net/manual/en/function.http-build-query.php for more on what
688
 *		 http_build_query() does.
689
 *
690
 * @param array $data URL-encode key/value pairs.
691
 * @return string URL-encoded string.
692
 */
693
function build_query( $data ) {
694
	return _http_build_query( $data, null, '&', '', false );
695
}
696
697
/**
698
 * From php.net (modified by Mark Jaquith to behave like the native PHP5 function).
699
 *
700
 * @since 3.2.0
701
 * @access private
702
 *
703
 * @see https://secure.php.net/manual/en/function.http-build-query.php
704
 *
705
 * @param array|object  $data       An array or object of data. Converted to array.
706
 * @param string        $prefix     Optional. Numeric index. If set, start parameter numbering with it.
707
 *                                  Default null.
708
 * @param string        $sep        Optional. Argument separator; defaults to 'arg_separator.output'.
709
 *                                  Default null.
710
 * @param string        $key        Optional. Used to prefix key name. Default empty.
711
 * @param bool          $urlencode  Optional. Whether to use urlencode() in the result. Default true.
712
 *
713
 * @return string The query string.
714
 */
715
function _http_build_query( $data, $prefix = null, $sep = null, $key = '', $urlencode = true ) {
716
	$ret = array();
717
718
	foreach ( (array) $data as $k => $v ) {
719
		if ( $urlencode)
720
			$k = urlencode($k);
721
		if ( is_int($k) && $prefix != null )
0 ignored issues
show
It seems like you are loosely comparing $prefix of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
722
			$k = $prefix.$k;
723
		if ( !empty($key) )
724
			$k = $key . '%5B' . $k . '%5D';
725
		if ( $v === null )
726
			continue;
727
		elseif ( $v === false )
728
			$v = '0';
729
730
		if ( is_array($v) || is_object($v) )
731
			array_push($ret,_http_build_query($v, '', $sep, $k, $urlencode));
732
		elseif ( $urlencode )
733
			array_push($ret, $k.'='.urlencode($v));
734
		else
735
			array_push($ret, $k.'='.$v);
736
	}
737
738
	if ( null === $sep )
739
		$sep = ini_get('arg_separator.output');
740
741
	return implode($sep, $ret);
742
}
743
744
/**
745
 * Retrieves a modified URL query string.
746
 *
747
 * You can rebuild the URL and append query variables to the URL query by using this function.
748
 * There are two ways to use this function; either a single key and value, or an associative array.
749
 *
750
 * Using a single key and value:
751
 *
752
 *     add_query_arg( 'key', 'value', 'http://example.com' );
753
 *
754
 * Using an associative array:
755
 *
756
 *     add_query_arg( array(
757
 *         'key1' => 'value1',
758
 *         'key2' => 'value2',
759
 *     ), 'http://example.com' );
760
 *
761
 * Omitting the URL from either use results in the current URL being used
762
 * (the value of `$_SERVER['REQUEST_URI']`).
763
 *
764
 * Values are expected to be encoded appropriately with urlencode() or rawurlencode().
765
 *
766
 * Setting any query variable's value to boolean false removes the key (see remove_query_arg()).
767
 *
768
 * Important: The return value of add_query_arg() is not escaped by default. Output should be
769
 * late-escaped with esc_url() or similar to help prevent vulnerability to cross-site scripting
770
 * (XSS) attacks.
771
 *
772
 * @since 1.5.0
773
 *
774
 * @param string|array $key   Either a query variable key, or an associative array of query variables.
0 ignored issues
show
There is no parameter named $key. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
775
 * @param string       $value Optional. Either a query variable value, or a URL to act upon.
0 ignored issues
show
There is no parameter named $value. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
776
 * @param string       $url   Optional. A URL to act upon.
0 ignored issues
show
There is no parameter named $url. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
777
 * @return string New URL query string (unescaped).
778
 */
779
function add_query_arg() {
780
	$args = func_get_args();
781
	if ( is_array( $args[0] ) ) {
782 View Code Duplication
		if ( count( $args ) < 2 || false === $args[1] )
783
			$uri = $_SERVER['REQUEST_URI'];
784
		else
785
			$uri = $args[1];
786 View Code Duplication
	} else {
787
		if ( count( $args ) < 3 || false === $args[2] )
788
			$uri = $_SERVER['REQUEST_URI'];
789
		else
790
			$uri = $args[2];
791
	}
792
793
	if ( $frag = strstr( $uri, '#' ) )
794
		$uri = substr( $uri, 0, -strlen( $frag ) );
795
	else
796
		$frag = '';
797
798
	if ( 0 === stripos( $uri, 'http://' ) ) {
799
		$protocol = 'http://';
800
		$uri = substr( $uri, 7 );
801
	} elseif ( 0 === stripos( $uri, 'https://' ) ) {
802
		$protocol = 'https://';
803
		$uri = substr( $uri, 8 );
804
	} else {
805
		$protocol = '';
806
	}
807
808
	if ( strpos( $uri, '?' ) !== false ) {
809
		list( $base, $query ) = explode( '?', $uri, 2 );
810
		$base .= '?';
811
	} elseif ( $protocol || strpos( $uri, '=' ) === false ) {
812
		$base = $uri . '?';
813
		$query = '';
814
	} else {
815
		$base = '';
816
		$query = $uri;
817
	}
818
819
	wp_parse_str( $query, $qs );
820
	$qs = urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string
821
	if ( is_array( $args[0] ) ) {
822
		foreach ( $args[0] as $k => $v ) {
823
			$qs[ $k ] = $v;
824
		}
825
	} else {
826
		$qs[ $args[0] ] = $args[1];
827
	}
828
829
	foreach ( $qs as $k => $v ) {
830
		if ( $v === false )
831
			unset( $qs[$k] );
832
	}
833
834
	$ret = build_query( $qs );
835
	$ret = trim( $ret, '?' );
836
	$ret = preg_replace( '#=(&|$)#', '$1', $ret );
837
	$ret = $protocol . $base . $ret . $frag;
838
	$ret = rtrim( $ret, '?' );
839
	return $ret;
840
}
841
842
/**
843
 * Removes an item or items from a query string.
844
 *
845
 * @since 1.5.0
846
 *
847
 * @param string|array $key   Query key or keys to remove.
848
 * @param bool|string  $query Optional. When false uses the current URL. Default false.
849
 * @return string New URL query string.
850
 */
851
function remove_query_arg( $key, $query = false ) {
852
	if ( is_array( $key ) ) { // removing multiple keys
853
		foreach ( $key as $k )
854
			$query = add_query_arg( $k, false, $query );
855
		return $query;
856
	}
857
	return add_query_arg( $key, false, $query );
858
}
859
860
/**
861
 * Returns an array of single-use query variable names that can be removed from a URL.
862
 *
863
 * @since 4.4.0
864
 *
865
 * @return array An array of parameters to remove from the URL.
866
 */
867
function wp_removable_query_args() {
868
	$removable_query_args = array(
869
		'activate',
870
		'activated',
871
		'approved',
872
		'deactivate',
873
		'deleted',
874
		'disabled',
875
		'enabled',
876
		'error',
877
		'hotkeys_highlight_first',
878
		'hotkeys_highlight_last',
879
		'locked',
880
		'message',
881
		'same',
882
		'saved',
883
		'settings-updated',
884
		'skipped',
885
		'spammed',
886
		'trashed',
887
		'unspammed',
888
		'untrashed',
889
		'update',
890
		'updated',
891
		'wp-post-new-reload',
892
	);
893
894
	/**
895
	 * Filters the list of query variables to remove.
896
	 *
897
	 * @since 4.2.0
898
	 *
899
	 * @param array $removable_query_args An array of query variables to remove from a URL.
900
	 */
901
	return apply_filters( 'removable_query_args', $removable_query_args );
902
}
903
904
/**
905
 * Walks the array while sanitizing the contents.
906
 *
907
 * @since 0.71
908
 *
909
 * @param array $array Array to walk while sanitizing contents.
910
 * @return array Sanitized $array.
911
 */
912
function add_magic_quotes( $array ) {
913 View Code Duplication
	foreach ( (array) $array as $k => $v ) {
914
		if ( is_array( $v ) ) {
915
			$array[$k] = add_magic_quotes( $v );
916
		} else {
917
			$array[$k] = addslashes( $v );
918
		}
919
	}
920
	return $array;
921
}
922
923
/**
924
 * HTTP request for URI to retrieve content.
925
 *
926
 * @since 1.5.1
927
 *
928
 * @see wp_safe_remote_get()
929
 *
930
 * @param string $uri URI/URL of web page to retrieve.
931
 * @return false|string HTTP content. False on failure.
932
 */
933
function wp_remote_fopen( $uri ) {
934
	$parsed_url = @parse_url( $uri );
935
936
	if ( !$parsed_url || !is_array( $parsed_url ) )
937
		return false;
938
939
	$options = array();
940
	$options['timeout'] = 10;
941
942
	$response = wp_safe_remote_get( $uri, $options );
943
944
	if ( is_wp_error( $response ) )
945
		return false;
946
947
	return wp_remote_retrieve_body( $response );
948
}
949
950
/**
951
 * Set up the WordPress query.
952
 *
953
 * @since 2.0.0
954
 *
955
 * @global WP       $wp_locale
956
 * @global WP_Query $wp_query
957
 * @global WP_Query $wp_the_query
958
 *
959
 * @param string|array $query_vars Default WP_Query arguments.
960
 */
961
function wp( $query_vars = '' ) {
962
	global $wp, $wp_query, $wp_the_query;
963
	$wp->main( $query_vars );
964
965
	if ( !isset($wp_the_query) )
966
		$wp_the_query = $wp_query;
967
}
968
969
/**
970
 * Retrieve the description for the HTTP status.
971
 *
972
 * @since 2.3.0
973
 *
974
 * @global array $wp_header_to_desc
975
 *
976
 * @param int $code HTTP status code.
977
 * @return string Empty string if not found, or description if found.
978
 */
979
function get_status_header_desc( $code ) {
980
	global $wp_header_to_desc;
981
982
	$code = absint( $code );
983
984
	if ( !isset( $wp_header_to_desc ) ) {
985
		$wp_header_to_desc = array(
986
			100 => 'Continue',
987
			101 => 'Switching Protocols',
988
			102 => 'Processing',
989
990
			200 => 'OK',
991
			201 => 'Created',
992
			202 => 'Accepted',
993
			203 => 'Non-Authoritative Information',
994
			204 => 'No Content',
995
			205 => 'Reset Content',
996
			206 => 'Partial Content',
997
			207 => 'Multi-Status',
998
			226 => 'IM Used',
999
1000
			300 => 'Multiple Choices',
1001
			301 => 'Moved Permanently',
1002
			302 => 'Found',
1003
			303 => 'See Other',
1004
			304 => 'Not Modified',
1005
			305 => 'Use Proxy',
1006
			306 => 'Reserved',
1007
			307 => 'Temporary Redirect',
1008
			308 => 'Permanent Redirect',
1009
1010
			400 => 'Bad Request',
1011
			401 => 'Unauthorized',
1012
			402 => 'Payment Required',
1013
			403 => 'Forbidden',
1014
			404 => 'Not Found',
1015
			405 => 'Method Not Allowed',
1016
			406 => 'Not Acceptable',
1017
			407 => 'Proxy Authentication Required',
1018
			408 => 'Request Timeout',
1019
			409 => 'Conflict',
1020
			410 => 'Gone',
1021
			411 => 'Length Required',
1022
			412 => 'Precondition Failed',
1023
			413 => 'Request Entity Too Large',
1024
			414 => 'Request-URI Too Long',
1025
			415 => 'Unsupported Media Type',
1026
			416 => 'Requested Range Not Satisfiable',
1027
			417 => 'Expectation Failed',
1028
			418 => 'I\'m a teapot',
1029
			421 => 'Misdirected Request',
1030
			422 => 'Unprocessable Entity',
1031
			423 => 'Locked',
1032
			424 => 'Failed Dependency',
1033
			426 => 'Upgrade Required',
1034
			428 => 'Precondition Required',
1035
			429 => 'Too Many Requests',
1036
			431 => 'Request Header Fields Too Large',
1037
			451 => 'Unavailable For Legal Reasons',
1038
1039
			500 => 'Internal Server Error',
1040
			501 => 'Not Implemented',
1041
			502 => 'Bad Gateway',
1042
			503 => 'Service Unavailable',
1043
			504 => 'Gateway Timeout',
1044
			505 => 'HTTP Version Not Supported',
1045
			506 => 'Variant Also Negotiates',
1046
			507 => 'Insufficient Storage',
1047
			510 => 'Not Extended',
1048
			511 => 'Network Authentication Required',
1049
		);
1050
	}
1051
1052
	if ( isset( $wp_header_to_desc[$code] ) )
1053
		return $wp_header_to_desc[$code];
1054
	else
1055
		return '';
1056
}
1057
1058
/**
1059
 * Set HTTP status header.
1060
 *
1061
 * @since 2.0.0
1062
 * @since 4.4.0 Added the `$description` parameter.
1063
 *
1064
 * @see get_status_header_desc()
1065
 *
1066
 * @param int    $code        HTTP status code.
1067
 * @param string $description Optional. A custom description for the HTTP status.
1068
 */
1069
function status_header( $code, $description = '' ) {
1070
	if ( ! $description ) {
1071
		$description = get_status_header_desc( $code );
1072
	}
1073
1074
	if ( empty( $description ) ) {
1075
		return;
1076
	}
1077
1078
	$protocol = wp_get_server_protocol();
1079
	$status_header = "$protocol $code $description";
1080
	if ( function_exists( 'apply_filters' ) )
1081
1082
		/**
1083
		 * Filters an HTTP status header.
1084
		 *
1085
		 * @since 2.2.0
1086
		 *
1087
		 * @param string $status_header HTTP status header.
1088
		 * @param int    $code          HTTP status code.
1089
		 * @param string $description   Description for the status code.
1090
		 * @param string $protocol      Server protocol.
1091
		 */
1092
		$status_header = apply_filters( 'status_header', $status_header, $code, $description, $protocol );
1093
1094
	@header( $status_header, true, $code );
1095
}
1096
1097
/**
1098
 * Get the header information to prevent caching.
1099
 *
1100
 * The several different headers cover the different ways cache prevention
1101
 * is handled by different browsers
1102
 *
1103
 * @since 2.8.0
1104
 *
1105
 * @return array The associative array of header names and field values.
1106
 */
1107
function wp_get_nocache_headers() {
1108
	$headers = array(
1109
		'Expires' => 'Wed, 11 Jan 1984 05:00:00 GMT',
1110
		'Cache-Control' => 'no-cache, must-revalidate, max-age=0',
1111
	);
1112
1113
	if ( function_exists('apply_filters') ) {
1114
		/**
1115
		 * Filters the cache-controlling headers.
1116
		 *
1117
		 * @since 2.8.0
1118
		 *
1119
		 * @see wp_get_nocache_headers()
1120
		 *
1121
		 * @param array $headers {
1122
		 *     Header names and field values.
1123
		 *
1124
		 *     @type string $Expires       Expires header.
1125
		 *     @type string $Cache-Control Cache-Control header.
1126
		 * }
1127
		 */
1128
		$headers = (array) apply_filters( 'nocache_headers', $headers );
1129
	}
1130
	$headers['Last-Modified'] = false;
1131
	return $headers;
1132
}
1133
1134
/**
1135
 * Set the headers to prevent caching for the different browsers.
1136
 *
1137
 * Different browsers support different nocache headers, so several
1138
 * headers must be sent so that all of them get the point that no
1139
 * caching should occur.
1140
 *
1141
 * @since 2.0.0
1142
 *
1143
 * @see wp_get_nocache_headers()
1144
 */
1145
function nocache_headers() {
1146
	$headers = wp_get_nocache_headers();
1147
1148
	unset( $headers['Last-Modified'] );
1149
1150
	// In PHP 5.3+, make sure we are not sending a Last-Modified header.
1151 View Code Duplication
	if ( function_exists( 'header_remove' ) ) {
1152
		@header_remove( 'Last-Modified' );
1153
	} else {
1154
		// In PHP 5.2, send an empty Last-Modified header, but only as a
1155
		// last resort to override a header already sent. #WP23021
1156
		foreach ( headers_list() as $header ) {
1157
			if ( 0 === stripos( $header, 'Last-Modified' ) ) {
1158
				$headers['Last-Modified'] = '';
1159
				break;
1160
			}
1161
		}
1162
	}
1163
1164
	foreach ( $headers as $name => $field_value )
1165
		@header("{$name}: {$field_value}");
1166
}
1167
1168
/**
1169
 * Set the headers for caching for 10 days with JavaScript content type.
1170
 *
1171
 * @since 2.1.0
1172
 */
1173
function cache_javascript_headers() {
1174
	$expiresOffset = 10 * DAY_IN_SECONDS;
1175
1176
	header( "Content-Type: text/javascript; charset=" . get_bloginfo( 'charset' ) );
1177
	header( "Vary: Accept-Encoding" ); // Handle proxies
1178
	header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $expiresOffset ) . " GMT" );
1179
}
1180
1181
/**
1182
 * Retrieve the number of database queries during the WordPress execution.
1183
 *
1184
 * @since 2.0.0
1185
 *
1186
 * @global wpdb $wpdb WordPress database abstraction object.
1187
 *
1188
 * @return int Number of database queries.
1189
 */
1190
function get_num_queries() {
1191
	global $wpdb;
1192
	return $wpdb->num_queries;
1193
}
1194
1195
/**
1196
 * Whether input is yes or no.
1197
 *
1198
 * Must be 'y' to be true.
1199
 *
1200
 * @since 1.0.0
1201
 *
1202
 * @param string $yn Character string containing either 'y' (yes) or 'n' (no).
1203
 * @return bool True if yes, false on anything else.
1204
 */
1205
function bool_from_yn( $yn ) {
1206
	return ( strtolower( $yn ) == 'y' );
1207
}
1208
1209
/**
1210
 * Load the feed template from the use of an action hook.
1211
 *
1212
 * If the feed action does not have a hook, then the function will die with a
1213
 * message telling the visitor that the feed is not valid.
1214
 *
1215
 * It is better to only have one hook for each feed.
1216
 *
1217
 * @since 2.1.0
1218
 *
1219
 * @global WP_Query $wp_query Used to tell if the use a comment feed.
1220
 */
1221
function do_feed() {
1222
	global $wp_query;
1223
1224
	$feed = get_query_var( 'feed' );
1225
1226
	// Remove the pad, if present.
1227
	$feed = preg_replace( '/^_+/', '', $feed );
1228
1229
	if ( $feed == '' || $feed == 'feed' )
1230
		$feed = get_default_feed();
1231
1232
	if ( ! has_action( "do_feed_{$feed}" ) ) {
1233
		wp_die( __( 'ERROR: This is not a valid feed template.' ), '', array( 'response' => 404 ) );
1234
	}
1235
1236
	/**
1237
	 * Fires once the given feed is loaded.
1238
	 *
1239
	 * The dynamic portion of the hook name, `$feed`, refers to the feed template name.
1240
	 * Possible values include: 'rdf', 'rss', 'rss2', and 'atom'.
1241
	 *
1242
	 * @since 2.1.0
1243
	 * @since 4.4.0 The `$feed` parameter was added.
1244
	 *
1245
	 * @param bool   $is_comment_feed Whether the feed is a comment feed.
1246
	 * @param string $feed            The feed name.
1247
	 */
1248
	do_action( "do_feed_{$feed}", $wp_query->is_comment_feed, $feed );
1249
}
1250
1251
/**
1252
 * Load the RDF RSS 0.91 Feed template.
1253
 *
1254
 * @since 2.1.0
1255
 *
1256
 * @see load_template()
1257
 */
1258
function do_feed_rdf() {
1259
	load_template( ABSPATH . WPINC . '/feed-rdf.php' );
1260
}
1261
1262
/**
1263
 * Load the RSS 1.0 Feed Template.
1264
 *
1265
 * @since 2.1.0
1266
 *
1267
 * @see load_template()
1268
 */
1269
function do_feed_rss() {
1270
	load_template( ABSPATH . WPINC . '/feed-rss.php' );
1271
}
1272
1273
/**
1274
 * Load either the RSS2 comment feed or the RSS2 posts feed.
1275
 *
1276
 * @since 2.1.0
1277
 *
1278
 * @see load_template()
1279
 *
1280
 * @param bool $for_comments True for the comment feed, false for normal feed.
1281
 */
1282
function do_feed_rss2( $for_comments ) {
1283 View Code Duplication
	if ( $for_comments )
1284
		load_template( ABSPATH . WPINC . '/feed-rss2-comments.php' );
1285
	else
1286
		load_template( ABSPATH . WPINC . '/feed-rss2.php' );
1287
}
1288
1289
/**
1290
 * Load either Atom comment feed or Atom posts feed.
1291
 *
1292
 * @since 2.1.0
1293
 *
1294
 * @see load_template()
1295
 *
1296
 * @param bool $for_comments True for the comment feed, false for normal feed.
1297
 */
1298
function do_feed_atom( $for_comments ) {
1299 View Code Duplication
	if ($for_comments)
1300
		load_template( ABSPATH . WPINC . '/feed-atom-comments.php');
1301
	else
1302
		load_template( ABSPATH . WPINC . '/feed-atom.php' );
1303
}
1304
1305
/**
1306
 * Display the robots.txt file content.
1307
 *
1308
 * The echo content should be with usage of the permalinks or for creating the
1309
 * robots.txt file.
1310
 *
1311
 * @since 2.1.0
1312
 */
1313
function do_robots() {
1314
	header( 'Content-Type: text/plain; charset=utf-8' );
1315
1316
	/**
1317
	 * Fires when displaying the robots.txt file.
1318
	 *
1319
	 * @since 2.1.0
1320
	 */
1321
	do_action( 'do_robotstxt' );
1322
1323
	$output = "User-agent: *\n";
1324
	$public = get_option( 'blog_public' );
1325
	if ( '0' == $public ) {
1326
		$output .= "Disallow: /\n";
1327
	} else {
1328
		$site_url = parse_url( site_url() );
1329
		$path = ( !empty( $site_url['path'] ) ) ? $site_url['path'] : '';
1330
		$output .= "Disallow: $path/wp-admin/\n";
1331
		$output .= "Allow: $path/wp-admin/admin-ajax.php\n";
1332
	}
1333
1334
	/**
1335
	 * Filters the robots.txt output.
1336
	 *
1337
	 * @since 3.0.0
1338
	 *
1339
	 * @param string $output Robots.txt output.
1340
	 * @param bool   $public Whether the site is considered "public".
1341
	 */
1342
	echo apply_filters( 'robots_txt', $output, $public );
1343
}
1344
1345
/**
1346
 * Test whether WordPress is already installed.
1347
 *
1348
 * The cache will be checked first. If you have a cache plugin, which saves
1349
 * the cache values, then this will work. If you use the default WordPress
1350
 * cache, and the database goes away, then you might have problems.
1351
 *
1352
 * Checks for the 'siteurl' option for whether WordPress is installed.
1353
 *
1354
 * @since 2.1.0
1355
 *
1356
 * @global wpdb $wpdb WordPress database abstraction object.
1357
 *
1358
 * @return bool Whether the site is already installed.
1359
 */
1360
function is_blog_installed() {
1361
	global $wpdb;
1362
1363
	/*
1364
	 * Check cache first. If options table goes away and we have true
1365
	 * cached, oh well.
1366
	 */
1367
	if ( wp_cache_get( 'is_blog_installed' ) )
1368
		return true;
1369
1370
	$suppress = $wpdb->suppress_errors();
1371
	if ( ! wp_installing() ) {
1372
		$alloptions = wp_load_alloptions();
1373
	}
1374
	// If siteurl is not set to autoload, check it specifically
1375
	if ( !isset( $alloptions['siteurl'] ) )
1376
		$installed = $wpdb->get_var( "SELECT option_value FROM $wpdb->options WHERE option_name = 'siteurl'" );
1377
	else
1378
		$installed = $alloptions['siteurl'];
1379
	$wpdb->suppress_errors( $suppress );
1380
1381
	$installed = !empty( $installed );
1382
	wp_cache_set( 'is_blog_installed', $installed );
1383
1384
	if ( $installed )
1385
		return true;
1386
1387
	// If visiting repair.php, return true and let it take over.
1388
	if ( defined( 'WP_REPAIRING' ) )
1389
		return true;
1390
1391
	$suppress = $wpdb->suppress_errors();
1392
1393
	/*
1394
	 * Loop over the WP tables. If none exist, then scratch install is allowed.
1395
	 * If one or more exist, suggest table repair since we got here because the
1396
	 * options table could not be accessed.
1397
	 */
1398
	$wp_tables = $wpdb->tables();
1399
	foreach ( $wp_tables as $table ) {
1400
		// The existence of custom user tables shouldn't suggest an insane state or prevent a clean install.
1401
		if ( defined( 'CUSTOM_USER_TABLE' ) && CUSTOM_USER_TABLE == $table )
1402
			continue;
1403
		if ( defined( 'CUSTOM_USER_META_TABLE' ) && CUSTOM_USER_META_TABLE == $table )
1404
			continue;
1405
1406
		if ( ! $wpdb->get_results( "DESCRIBE $table;" ) )
1407
			continue;
1408
1409
		// One or more tables exist. We are insane.
1410
1411
		wp_load_translations_early();
1412
1413
		// Die with a DB error.
1414
		$wpdb->error = sprintf( __( 'One or more database tables are unavailable. The database may need to be <a href="%s">repaired</a>.' ), 'maint/repair.php?referrer=is_blog_installed' );
1415
		dead_db();
1416
	}
1417
1418
	$wpdb->suppress_errors( $suppress );
1419
1420
	wp_cache_set( 'is_blog_installed', false );
1421
1422
	return false;
1423
}
1424
1425
/**
1426
 * Retrieve URL with nonce added to URL query.
1427
 *
1428
 * @since 2.0.4
1429
 *
1430
 * @param string     $actionurl URL to add nonce action.
1431
 * @param int|string $action    Optional. Nonce action name. Default -1.
1432
 * @param string     $name      Optional. Nonce name. Default '_wpnonce'.
1433
 * @return string Escaped URL with nonce action added.
1434
 */
1435
function wp_nonce_url( $actionurl, $action = -1, $name = '_wpnonce' ) {
1436
	$actionurl = str_replace( '&amp;', '&', $actionurl );
1437
	return esc_html( add_query_arg( $name, wp_create_nonce( $action ), $actionurl ) );
1438
}
1439
1440
/**
1441
 * Retrieve or display nonce hidden field for forms.
1442
 *
1443
 * The nonce field is used to validate that the contents of the form came from
1444
 * the location on the current site and not somewhere else. The nonce does not
1445
 * offer absolute protection, but should protect against most cases. It is very
1446
 * important to use nonce field in forms.
1447
 *
1448
 * The $action and $name are optional, but if you want to have better security,
1449
 * it is strongly suggested to set those two parameters. It is easier to just
1450
 * call the function without any parameters, because validation of the nonce
1451
 * doesn't require any parameters, but since crackers know what the default is
1452
 * it won't be difficult for them to find a way around your nonce and cause
1453
 * damage.
1454
 *
1455
 * The input name will be whatever $name value you gave. The input value will be
1456
 * the nonce creation value.
1457
 *
1458
 * @since 2.0.4
1459
 *
1460
 * @param int|string $action  Optional. Action name. Default -1.
1461
 * @param string     $name    Optional. Nonce name. Default '_wpnonce'.
1462
 * @param bool       $referer Optional. Whether to set the referer field for validation. Default true.
1463
 * @param bool       $echo    Optional. Whether to display or return hidden form field. Default true.
1464
 * @return string Nonce field HTML markup.
1465
 */
1466
function wp_nonce_field( $action = -1, $name = "_wpnonce", $referer = true , $echo = true ) {
1467
	$name = esc_attr( $name );
1468
	$nonce_field = '<input type="hidden" id="' . $name . '" name="' . $name . '" value="' . wp_create_nonce( $action ) . '" />';
1469
1470
	if ( $referer )
1471
		$nonce_field .= wp_referer_field( false );
1472
1473
	if ( $echo )
1474
		echo $nonce_field;
1475
1476
	return $nonce_field;
1477
}
1478
1479
/**
1480
 * Retrieve or display referer hidden field for forms.
1481
 *
1482
 * The referer link is the current Request URI from the server super global. The
1483
 * input name is '_wp_http_referer', in case you wanted to check manually.
1484
 *
1485
 * @since 2.0.4
1486
 *
1487
 * @param bool $echo Optional. Whether to echo or return the referer field. Default true.
1488
 * @return string Referer field HTML markup.
1489
 */
1490
function wp_referer_field( $echo = true ) {
1491
	$referer_field = '<input type="hidden" name="_wp_http_referer" value="'. esc_attr( wp_unslash( $_SERVER['REQUEST_URI'] ) ) . '" />';
0 ignored issues
show
It seems like wp_unslash($_SERVER['REQUEST_URI']) targeting wp_unslash() can also be of type array; however, esc_attr() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1492
1493
	if ( $echo )
1494
		echo $referer_field;
1495
	return $referer_field;
1496
}
1497
1498
/**
1499
 * Retrieve or display original referer hidden field for forms.
1500
 *
1501
 * The input name is '_wp_original_http_referer' and will be either the same
1502
 * value of wp_referer_field(), if that was posted already or it will be the
1503
 * current page, if it doesn't exist.
1504
 *
1505
 * @since 2.0.4
1506
 *
1507
 * @param bool   $echo         Optional. Whether to echo the original http referer. Default true.
1508
 * @param string $jump_back_to Optional. Can be 'previous' or page you want to jump back to.
1509
 *                             Default 'current'.
1510
 * @return string Original referer field.
1511
 */
1512
function wp_original_referer_field( $echo = true, $jump_back_to = 'current' ) {
1513
	if ( ! $ref = wp_get_original_referer() ) {
1514
		$ref = 'previous' == $jump_back_to ? wp_get_referer() : wp_unslash( $_SERVER['REQUEST_URI'] );
1515
	}
1516
	$orig_referer_field = '<input type="hidden" name="_wp_original_http_referer" value="' . esc_attr( $ref ) . '" />';
0 ignored issues
show
It seems like $ref can also be of type array; however, esc_attr() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1517
	if ( $echo )
1518
		echo $orig_referer_field;
1519
	return $orig_referer_field;
1520
}
1521
1522
/**
1523
 * Retrieve referer from '_wp_http_referer' or HTTP referer.
1524
 *
1525
 * If it's the same as the current request URL, will return false.
1526
 *
1527
 * @since 2.0.4
1528
 *
1529
 * @return false|string False on failure. Referer URL on success.
1530
 */
1531
function wp_get_referer() {
1532
	if ( ! function_exists( 'wp_validate_redirect' ) ) {
1533
		return false;
1534
	}
1535
1536
	$ref = wp_get_raw_referer();
1537
1538
	if ( $ref && $ref !== wp_unslash( $_SERVER['REQUEST_URI'] ) && $ref !== home_url() . wp_unslash( $_SERVER['REQUEST_URI'] ) ) {
1539
		return wp_validate_redirect( $ref, false );
0 ignored issues
show
It seems like $ref defined by wp_get_raw_referer() on line 1536 can also be of type array; however, wp_validate_redirect() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1540
	}
1541
1542
	return false;
1543
}
1544
1545
/**
1546
 * Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer.
1547
 *
1548
 * Do not use for redirects, use wp_get_referer() instead.
1549
 *
1550
 * @since 4.5.0
1551
 *
1552
 * @return string|false Referer URL on success, false on failure.
1553
 */
1554
function wp_get_raw_referer() {
1555
	if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
1556
		return wp_unslash( $_REQUEST['_wp_http_referer'] );
1557
	} else if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
1558
		return wp_unslash( $_SERVER['HTTP_REFERER'] );
1559
	}
1560
1561
	return false;
1562
}
1563
1564
/**
1565
 * Retrieve original referer that was posted, if it exists.
1566
 *
1567
 * @since 2.0.4
1568
 *
1569
 * @return string|false False if no original referer or original referer if set.
1570
 */
1571
function wp_get_original_referer() {
1572
	if ( ! empty( $_REQUEST['_wp_original_http_referer'] ) && function_exists( 'wp_validate_redirect' ) )
1573
		return wp_validate_redirect( wp_unslash( $_REQUEST['_wp_original_http_referer'] ), false );
0 ignored issues
show
It seems like wp_unslash($_REQUEST['_w...riginal_http_referer']) targeting wp_unslash() can also be of type array; however, wp_validate_redirect() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1574
	return false;
1575
}
1576
1577
/**
1578
 * Recursive directory creation based on full path.
1579
 *
1580
 * Will attempt to set permissions on folders.
1581
 *
1582
 * @since 2.0.1
1583
 *
1584
 * @param string $target Full path to attempt to create.
1585
 * @return bool Whether the path was created. True if path already exists.
1586
 */
1587
function wp_mkdir_p( $target ) {
1588
	$wrapper = null;
1589
1590
	// Strip the protocol.
1591
	if ( wp_is_stream( $target ) ) {
1592
		list( $wrapper, $target ) = explode( '://', $target, 2 );
1593
	}
1594
1595
	// From php.net/mkdir user contributed notes.
1596
	$target = str_replace( '//', '/', $target );
1597
1598
	// Put the wrapper back on the target.
1599
	if ( $wrapper !== null ) {
1600
		$target = $wrapper . '://' . $target;
1601
	}
1602
1603
	/*
1604
	 * Safe mode fails with a trailing slash under certain PHP versions.
1605
	 * Use rtrim() instead of untrailingslashit to avoid formatting.php dependency.
1606
	 */
1607
	$target = rtrim($target, '/');
1608
	if ( empty($target) )
1609
		$target = '/';
1610
1611
	if ( file_exists( $target ) )
1612
		return @is_dir( $target );
1613
1614
	// We need to find the permissions of the parent folder that exists and inherit that.
1615
	$target_parent = dirname( $target );
1616
	while ( '.' != $target_parent && ! is_dir( $target_parent ) ) {
1617
		$target_parent = dirname( $target_parent );
1618
	}
1619
1620
	// Get the permission bits.
1621
	if ( $stat = @stat( $target_parent ) ) {
1622
		$dir_perms = $stat['mode'] & 0007777;
1623
	} else {
1624
		$dir_perms = 0777;
1625
	}
1626
1627
	if ( @mkdir( $target, $dir_perms, true ) ) {
1628
1629
		/*
1630
		 * If a umask is set that modifies $dir_perms, we'll have to re-set
1631
		 * the $dir_perms correctly with chmod()
1632
		 */
1633
		if ( $dir_perms != ( $dir_perms & ~umask() ) ) {
1634
			$folder_parts = explode( '/', substr( $target, strlen( $target_parent ) + 1 ) );
1635
			for ( $i = 1, $c = count( $folder_parts ); $i <= $c; $i++ ) {
1636
				@chmod( $target_parent . '/' . implode( '/', array_slice( $folder_parts, 0, $i ) ), $dir_perms );
1637
			}
1638
		}
1639
1640
		return true;
1641
	}
1642
1643
	return false;
1644
}
1645
1646
/**
1647
 * Test if a give filesystem path is absolute.
1648
 *
1649
 * For example, '/foo/bar', or 'c:\windows'.
1650
 *
1651
 * @since 2.5.0
1652
 *
1653
 * @param string $path File path.
1654
 * @return bool True if path is absolute, false is not absolute.
1655
 */
1656
function path_is_absolute( $path ) {
1657
	/*
1658
	 * This is definitive if true but fails if $path does not exist or contains
1659
	 * a symbolic link.
1660
	 */
1661
	if ( realpath($path) == $path )
1662
		return true;
1663
1664
	if ( strlen($path) == 0 || $path[0] == '.' )
1665
		return false;
1666
1667
	// Windows allows absolute paths like this.
1668
	if ( preg_match('#^[a-zA-Z]:\\\\#', $path) )
1669
		return true;
1670
1671
	// A path starting with / or \ is absolute; anything else is relative.
1672
	return ( $path[0] == '/' || $path[0] == '\\' );
1673
}
1674
1675
/**
1676
 * Join two filesystem paths together.
1677
 *
1678
 * For example, 'give me $path relative to $base'. If the $path is absolute,
1679
 * then it the full path is returned.
1680
 *
1681
 * @since 2.5.0
1682
 *
1683
 * @param string $base Base path.
1684
 * @param string $path Path relative to $base.
1685
 * @return string The path with the base or absolute path.
1686
 */
1687
function path_join( $base, $path ) {
1688
	if ( path_is_absolute($path) )
1689
		return $path;
1690
1691
	return rtrim($base, '/') . '/' . ltrim($path, '/');
1692
}
1693
1694
/**
1695
 * Normalize a filesystem path.
1696
 *
1697
 * On windows systems, replaces backslashes with forward slashes
1698
 * and forces upper-case drive letters.
1699
 * Allows for two leading slashes for Windows network shares, but
1700
 * ensures that all other duplicate slashes are reduced to a single.
1701
 *
1702
 * @since 3.9.0
1703
 * @since 4.4.0 Ensures upper-case drive letters on Windows systems.
1704
 * @since 4.5.0 Allows for Windows network shares.
1705
 *
1706
 * @param string $path Path to normalize.
1707
 * @return string Normalized path.
1708
 */
1709
function wp_normalize_path( $path ) {
1710
	$path = str_replace( '\\', '/', $path );
1711
	$path = preg_replace( '|(?<=.)/+|', '/', $path );
1712
	if ( ':' === substr( $path, 1, 1 ) ) {
1713
		$path = ucfirst( $path );
1714
	}
1715
	return $path;
1716
}
1717
1718
/**
1719
 * Determine a writable directory for temporary files.
1720
 *
1721
 * Function's preference is the return value of sys_get_temp_dir(),
1722
 * followed by your PHP temporary upload directory, followed by WP_CONTENT_DIR,
1723
 * before finally defaulting to /tmp/
1724
 *
1725
 * In the event that this function does not find a writable location,
1726
 * It may be overridden by the WP_TEMP_DIR constant in your wp-config.php file.
1727
 *
1728
 * @since 2.5.0
1729
 *
1730
 * @staticvar string $temp
1731
 *
1732
 * @return string Writable temporary directory.
1733
 */
1734
function get_temp_dir() {
1735
	static $temp = '';
1736
	if ( defined('WP_TEMP_DIR') )
1737
		return trailingslashit(WP_TEMP_DIR);
1738
1739
	if ( $temp )
1740
		return trailingslashit( $temp );
1741
1742
	if ( function_exists('sys_get_temp_dir') ) {
1743
		$temp = sys_get_temp_dir();
1744
		if ( @is_dir( $temp ) && wp_is_writable( $temp ) )
1745
			return trailingslashit( $temp );
1746
	}
1747
1748
	$temp = ini_get('upload_tmp_dir');
1749
	if ( @is_dir( $temp ) && wp_is_writable( $temp ) )
1750
		return trailingslashit( $temp );
1751
1752
	$temp = WP_CONTENT_DIR . '/';
1753
	if ( is_dir( $temp ) && wp_is_writable( $temp ) )
1754
		return $temp;
1755
1756
	return '/tmp/';
1757
}
1758
1759
/**
1760
 * Determine if a directory is writable.
1761
 *
1762
 * This function is used to work around certain ACL issues in PHP primarily
1763
 * affecting Windows Servers.
1764
 *
1765
 * @since 3.6.0
1766
 *
1767
 * @see win_is_writable()
1768
 *
1769
 * @param string $path Path to check for write-ability.
1770
 * @return bool Whether the path is writable.
1771
 */
1772
function wp_is_writable( $path ) {
1773
	if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) )
1774
		return win_is_writable( $path );
1775
	else
1776
		return @is_writable( $path );
1777
}
1778
1779
/**
1780
 * Workaround for Windows bug in is_writable() function
1781
 *
1782
 * PHP has issues with Windows ACL's for determine if a
1783
 * directory is writable or not, this works around them by
1784
 * checking the ability to open files rather than relying
1785
 * upon PHP to interprate the OS ACL.
1786
 *
1787
 * @since 2.8.0
1788
 *
1789
 * @see https://bugs.php.net/bug.php?id=27609
1790
 * @see https://bugs.php.net/bug.php?id=30931
1791
 *
1792
 * @param string $path Windows path to check for write-ability.
1793
 * @return bool Whether the path is writable.
1794
 */
1795
function win_is_writable( $path ) {
1796
1797
	if ( $path[strlen( $path ) - 1] == '/' ) { // if it looks like a directory, check a random file within the directory
1798
		return win_is_writable( $path . uniqid( mt_rand() ) . '.tmp');
1799
	} elseif ( is_dir( $path ) ) { // If it's a directory (and not a file) check a random file within the directory
1800
		return win_is_writable( $path . '/' . uniqid( mt_rand() ) . '.tmp' );
1801
	}
1802
	// check tmp file for read/write capabilities
1803
	$should_delete_tmp_file = !file_exists( $path );
1804
	$f = @fopen( $path, 'a' );
1805
	if ( $f === false )
1806
		return false;
1807
	fclose( $f );
1808
	if ( $should_delete_tmp_file )
1809
		unlink( $path );
1810
	return true;
1811
}
1812
1813
/**
1814
 * Retrieves uploads directory information.
1815
 *
1816
 * Same as wp_upload_dir() but "light weight" as it doesn't attempt to create the uploads directory.
1817
 * Intended for use in themes, when only 'basedir' and 'baseurl' are needed, generally in all cases
1818
 * when not uploading files.
1819
 *
1820
 * @since 4.5.0
1821
 *
1822
 * @see wp_upload_dir()
1823
 *
1824
 * @return array See wp_upload_dir() for description.
1825
 */
1826
function wp_get_upload_dir() {
1827
	return wp_upload_dir( null, false );
1828
}
1829
1830
/**
1831
 * Get an array containing the current upload directory's path and url.
1832
 *
1833
 * Checks the 'upload_path' option, which should be from the web root folder,
1834
 * and if it isn't empty it will be used. If it is empty, then the path will be
1835
 * 'WP_CONTENT_DIR/uploads'. If the 'UPLOADS' constant is defined, then it will
1836
 * override the 'upload_path' option and 'WP_CONTENT_DIR/uploads' path.
1837
 *
1838
 * The upload URL path is set either by the 'upload_url_path' option or by using
1839
 * the 'WP_CONTENT_URL' constant and appending '/uploads' to the path.
1840
 *
1841
 * If the 'uploads_use_yearmonth_folders' is set to true (checkbox if checked in
1842
 * the administration settings panel), then the time will be used. The format
1843
 * will be year first and then month.
1844
 *
1845
 * If the path couldn't be created, then an error will be returned with the key
1846
 * 'error' containing the error message. The error suggests that the parent
1847
 * directory is not writable by the server.
1848
 *
1849
 * On success, the returned array will have many indices:
1850
 * 'path' - base directory and sub directory or full path to upload directory.
1851
 * 'url' - base url and sub directory or absolute URL to upload directory.
1852
 * 'subdir' - sub directory if uploads use year/month folders option is on.
1853
 * 'basedir' - path without subdir.
1854
 * 'baseurl' - URL path without subdir.
1855
 * 'error' - false or error message.
1856
 *
1857
 * @since 2.0.0
1858
 * @uses _wp_upload_dir()
1859
 *
1860
 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null.
1861
 * @param bool   $create_dir Optional. Whether to check and create the uploads directory.
1862
 *                           Default true for backward compatibility.
1863
 * @param bool   $refresh_cache Optional. Whether to refresh the cache. Default false.
1864
 * @return array See above for description.
1865
 */
1866
function wp_upload_dir( $time = null, $create_dir = true, $refresh_cache = false ) {
1867
	static $cache = array(), $tested_paths = array();
1868
1869
	$key = sprintf( '%d-%s', get_current_blog_id(), (string) $time );
1870
1871
	if ( $refresh_cache || empty( $cache[ $key ] ) ) {
1872
		$cache[ $key ] = _wp_upload_dir( $time );
1873
	}
1874
1875
	/**
1876
	 * Filters the uploads directory data.
1877
	 *
1878
	 * @since 2.0.0
1879
	 *
1880
	 * @param array $uploads Array of upload directory data with keys of 'path',
1881
	 *                       'url', 'subdir, 'basedir', and 'error'.
1882
	 */
1883
	$uploads = apply_filters( 'upload_dir', $cache[ $key ] );
1884
1885
	if ( $create_dir ) {
1886
		$path = $uploads['path'];
1887
1888
		if ( array_key_exists( $path, $tested_paths ) ) {
1889
			$uploads['error'] = $tested_paths[ $path ];
1890
		} else {
1891 View Code Duplication
			if ( ! wp_mkdir_p( $path ) ) {
1892
				if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) {
1893
					$error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
1894
				} else {
1895
					$error_path = basename( $uploads['basedir'] ) . $uploads['subdir'];
1896
				}
1897
1898
				$uploads['error'] = sprintf( __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), esc_html( $error_path ) );
1899
			}
1900
1901
			$tested_paths[ $path ] = $uploads['error'];
1902
		}
1903
	}
1904
1905
	return $uploads;
1906
}
1907
1908
/**
1909
 * A non-filtered, non-cached version of wp_upload_dir() that doesn't check the path.
1910
 *
1911
 * @access private
1912
 *
1913
 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null.
1914
 * @return array See wp_upload_dir()
1915
 */
1916
function _wp_upload_dir( $time = null ) {
1917
	$siteurl = get_option( 'siteurl' );
1918
	$upload_path = trim( get_option( 'upload_path' ) );
1919
1920
	if ( empty( $upload_path ) || 'wp-content/uploads' == $upload_path ) {
1921
		$dir = WP_CONTENT_DIR . '/uploads';
1922
	} elseif ( 0 !== strpos( $upload_path, ABSPATH ) ) {
1923
		// $dir is absolute, $upload_path is (maybe) relative to ABSPATH
1924
		$dir = path_join( ABSPATH, $upload_path );
1925
	} else {
1926
		$dir = $upload_path;
1927
	}
1928
1929
	if ( !$url = get_option( 'upload_url_path' ) ) {
1930
		if ( empty($upload_path) || ( 'wp-content/uploads' == $upload_path ) || ( $upload_path == $dir ) )
1931
			$url = WP_CONTENT_URL . '/uploads';
1932
		else
1933
			$url = trailingslashit( $siteurl ) . $upload_path;
1934
	}
1935
1936
	/*
1937
	 * Honor the value of UPLOADS. This happens as long as ms-files rewriting is disabled.
1938
	 * We also sometimes obey UPLOADS when rewriting is enabled -- see the next block.
1939
	 */
1940 View Code Duplication
	if ( defined( 'UPLOADS' ) && ! ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) ) {
1941
		$dir = ABSPATH . UPLOADS;
1942
		$url = trailingslashit( $siteurl ) . UPLOADS;
1943
	}
1944
1945
	// If multisite (and if not the main site in a post-MU network)
1946
	if ( is_multisite() && ! ( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
1947
1948
		if ( ! get_site_option( 'ms_files_rewriting' ) ) {
1949
			/*
1950
			 * If ms-files rewriting is disabled (networks created post-3.5), it is fairly
1951
			 * straightforward: Append sites/%d if we're not on the main site (for post-MU
1952
			 * networks). (The extra directory prevents a four-digit ID from conflicting with
1953
			 * a year-based directory for the main site. But if a MU-era network has disabled
1954
			 * ms-files rewriting manually, they don't need the extra directory, as they never
1955
			 * had wp-content/uploads for the main site.)
1956
			 */
1957
1958
			if ( defined( 'MULTISITE' ) )
1959
				$ms_dir = '/sites/' . get_current_blog_id();
1960
			else
1961
				$ms_dir = '/' . get_current_blog_id();
1962
1963
			$dir .= $ms_dir;
1964
			$url .= $ms_dir;
1965
1966 View Code Duplication
		} elseif ( defined( 'UPLOADS' ) && ! ms_is_switched() ) {
1967
			/*
1968
			 * Handle the old-form ms-files.php rewriting if the network still has that enabled.
1969
			 * When ms-files rewriting is enabled, then we only listen to UPLOADS when:
1970
			 * 1) We are not on the main site in a post-MU network, as wp-content/uploads is used
1971
			 *    there, and
1972
			 * 2) We are not switched, as ms_upload_constants() hardcodes these constants to reflect
1973
			 *    the original blog ID.
1974
			 *
1975
			 * Rather than UPLOADS, we actually use BLOGUPLOADDIR if it is set, as it is absolute.
1976
			 * (And it will be set, see ms_upload_constants().) Otherwise, UPLOADS can be used, as
1977
			 * as it is relative to ABSPATH. For the final piece: when UPLOADS is used with ms-files
1978
			 * rewriting in multisite, the resulting URL is /files. (#WP22702 for background.)
1979
			 */
1980
1981
			if ( defined( 'BLOGUPLOADDIR' ) )
1982
				$dir = untrailingslashit( BLOGUPLOADDIR );
1983
			else
1984
				$dir = ABSPATH . UPLOADS;
1985
			$url = trailingslashit( $siteurl ) . 'files';
1986
		}
1987
	}
1988
1989
	$basedir = $dir;
1990
	$baseurl = $url;
1991
1992
	$subdir = '';
1993
	if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
1994
		// Generate the yearly and monthly dirs
1995
		if ( !$time )
0 ignored issues
show
Bug Best Practice introduced by
The expression $time of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1996
			$time = current_time( 'mysql' );
1997
		$y = substr( $time, 0, 4 );
1998
		$m = substr( $time, 5, 2 );
1999
		$subdir = "/$y/$m";
2000
	}
2001
2002
	$dir .= $subdir;
2003
	$url .= $subdir;
2004
2005
	return array(
2006
		'path'    => $dir,
2007
		'url'     => $url,
2008
		'subdir'  => $subdir,
2009
		'basedir' => $basedir,
2010
		'baseurl' => $baseurl,
2011
		'error'   => false,
2012
	);
2013
}
2014
2015
/**
2016
 * Get a filename that is sanitized and unique for the given directory.
2017
 *
2018
 * If the filename is not unique, then a number will be added to the filename
2019
 * before the extension, and will continue adding numbers until the filename is
2020
 * unique.
2021
 *
2022
 * The callback is passed three parameters, the first one is the directory, the
2023
 * second is the filename, and the third is the extension.
2024
 *
2025
 * @since 2.5.0
2026
 *
2027
 * @param string   $dir                      Directory.
2028
 * @param string   $filename                 File name.
2029
 * @param callable $unique_filename_callback Callback. Default null.
2030
 * @return string New filename, if given wasn't unique.
2031
 */
2032
function wp_unique_filename( $dir, $filename, $unique_filename_callback = null ) {
2033
	// Sanitize the file name before we begin processing.
2034
	$filename = sanitize_file_name($filename);
2035
2036
	// Separate the filename into a name and extension.
2037
	$ext = pathinfo( $filename, PATHINFO_EXTENSION );
2038
	$name = pathinfo( $filename, PATHINFO_BASENAME );
2039
	if ( $ext ) {
2040
		$ext = '.' . $ext;
2041
	}
2042
2043
	// Edge case: if file is named '.ext', treat as an empty name.
2044
	if ( $name === $ext ) {
2045
		$name = '';
2046
	}
2047
2048
	/*
2049
	 * Increment the file number until we have a unique file to save in $dir.
2050
	 * Use callback if supplied.
2051
	 */
2052
	if ( $unique_filename_callback && is_callable( $unique_filename_callback ) ) {
2053
		$filename = call_user_func( $unique_filename_callback, $dir, $name, $ext );
2054
	} else {
2055
		$number = '';
2056
2057
		// Change '.ext' to lower case.
2058
		if ( $ext && strtolower($ext) != $ext ) {
2059
			$ext2 = strtolower($ext);
2060
			$filename2 = preg_replace( '|' . preg_quote($ext) . '$|', $ext2, $filename );
2061
2062
			// Check for both lower and upper case extension or image sub-sizes may be overwritten.
2063
			while ( file_exists($dir . "/$filename") || file_exists($dir . "/$filename2") ) {
2064
				$new_number = $number + 1;
2065
				$filename = str_replace( array( "-$number$ext", "$number$ext" ), "-$new_number$ext", $filename );
2066
				$filename2 = str_replace( array( "-$number$ext2", "$number$ext2" ), "-$new_number$ext2", $filename2 );
2067
				$number = $new_number;
2068
			}
2069
2070
			/**
2071
			 * Filters the result when generating a unique file name.
2072
			 *
2073
			 * @since 4.5.0
2074
			 *
2075
			 * @param string        $filename                 Unique file name.
2076
			 * @param string        $ext                      File extension, eg. ".png".
2077
			 * @param string        $dir                      Directory path.
2078
			 * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
2079
			 */
2080
			return apply_filters( 'wp_unique_filename', $filename2, $ext, $dir, $unique_filename_callback );
2081
		}
2082
2083
		while ( file_exists( $dir . "/$filename" ) ) {
2084
			if ( '' == "$number$ext" ) {
2085
				$filename = "$filename-" . ++$number;
2086
			} else {
2087
				$filename = str_replace( array( "-$number$ext", "$number$ext" ), "-" . ++$number . $ext, $filename );
2088
			}
2089
		}
2090
	}
2091
2092
	/** This filter is documented in wp-includes/functions.php */
2093
	return apply_filters( 'wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback );
2094
}
2095
2096
/**
2097
 * Create a file in the upload folder with given content.
2098
 *
2099
 * If there is an error, then the key 'error' will exist with the error message.
2100
 * If success, then the key 'file' will have the unique file path, the 'url' key
2101
 * will have the link to the new file. and the 'error' key will be set to false.
2102
 *
2103
 * This function will not move an uploaded file to the upload folder. It will
2104
 * create a new file with the content in $bits parameter. If you move the upload
2105
 * file, read the content of the uploaded file, and then you can give the
2106
 * filename and content to this function, which will add it to the upload
2107
 * folder.
2108
 *
2109
 * The permissions will be set on the new file automatically by this function.
2110
 *
2111
 * @since 2.0.0
2112
 *
2113
 * @param string       $name       Filename.
2114
 * @param null|string  $deprecated Never used. Set to null.
2115
 * @param mixed        $bits       File content
2116
 * @param string       $time       Optional. Time formatted in 'yyyy/mm'. Default null.
2117
 * @return array
2118
 */
2119
function wp_upload_bits( $name, $deprecated, $bits, $time = null ) {
2120
	if ( !empty( $deprecated ) )
2121
		_deprecated_argument( __FUNCTION__, '2.0.0' );
2122
2123
	if ( empty( $name ) )
2124
		return array( 'error' => __( 'Empty filename' ) );
2125
2126
	$wp_filetype = wp_check_filetype( $name );
2127
	if ( ! $wp_filetype['ext'] && ! current_user_can( 'unfiltered_upload' ) )
2128
		return array( 'error' => __( 'Invalid file type' ) );
2129
2130
	$upload = wp_upload_dir( $time );
2131
2132
	if ( $upload['error'] !== false )
2133
		return $upload;
2134
2135
	/**
2136
	 * Filters whether to treat the upload bits as an error.
2137
	 *
2138
	 * Passing a non-array to the filter will effectively short-circuit preparing
2139
	 * the upload bits, returning that value instead.
2140
	 *
2141
	 * @since 3.0.0
2142
	 *
2143
	 * @param mixed $upload_bits_error An array of upload bits data, or a non-array error to return.
2144
	 */
2145
	$upload_bits_error = apply_filters( 'wp_upload_bits', array( 'name' => $name, 'bits' => $bits, 'time' => $time ) );
2146
	if ( !is_array( $upload_bits_error ) ) {
2147
		$upload[ 'error' ] = $upload_bits_error;
2148
		return $upload;
2149
	}
2150
2151
	$filename = wp_unique_filename( $upload['path'], $name );
2152
2153
	$new_file = $upload['path'] . "/$filename";
2154 View Code Duplication
	if ( ! wp_mkdir_p( dirname( $new_file ) ) ) {
2155
		if ( 0 === strpos( $upload['basedir'], ABSPATH ) )
2156
			$error_path = str_replace( ABSPATH, '', $upload['basedir'] ) . $upload['subdir'];
2157
		else
2158
			$error_path = basename( $upload['basedir'] ) . $upload['subdir'];
2159
2160
		$message = sprintf( __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), $error_path );
2161
		return array( 'error' => $message );
2162
	}
2163
2164
	$ifp = @ fopen( $new_file, 'wb' );
2165
	if ( ! $ifp )
2166
		return array( 'error' => sprintf( __( 'Could not write file %s' ), $new_file ) );
2167
2168
	@fwrite( $ifp, $bits );
2169
	fclose( $ifp );
2170
	clearstatcache();
2171
2172
	// Set correct file permissions
2173
	$stat = @ stat( dirname( $new_file ) );
2174
	$perms = $stat['mode'] & 0007777;
2175
	$perms = $perms & 0000666;
2176
	@ chmod( $new_file, $perms );
2177
	clearstatcache();
2178
2179
	// Compute the URL
2180
	$url = $upload['url'] . "/$filename";
2181
2182
	/** This filter is documented in wp-admin/includes/file.php */
2183
	return apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $wp_filetype['type'], 'error' => false ), 'sideload' );
2184
}
2185
2186
/**
2187
 * Retrieve the file type based on the extension name.
2188
 *
2189
 * @since 2.5.0
2190
 *
2191
 * @param string $ext The extension to search.
2192
 * @return string|void The file type, example: audio, video, document, spreadsheet, etc.
2193
 */
2194
function wp_ext2type( $ext ) {
2195
	$ext = strtolower( $ext );
2196
2197
	$ext2type = wp_get_ext_types();
2198
	foreach ( $ext2type as $type => $exts )
2199
		if ( in_array( $ext, $exts ) )
2200
			return $type;
2201
}
2202
2203
/**
2204
 * Retrieve the file type from the file name.
2205
 *
2206
 * You can optionally define the mime array, if needed.
2207
 *
2208
 * @since 2.0.4
2209
 *
2210
 * @param string $filename File name or path.
2211
 * @param array  $mimes    Optional. Key is the file extension with value as the mime type.
2212
 * @return array Values with extension first and mime type.
2213
 */
2214
function wp_check_filetype( $filename, $mimes = null ) {
2215
	if ( empty($mimes) )
2216
		$mimes = get_allowed_mime_types();
2217
	$type = false;
2218
	$ext = false;
2219
2220
	foreach ( $mimes as $ext_preg => $mime_match ) {
2221
		$ext_preg = '!\.(' . $ext_preg . ')$!i';
2222
		if ( preg_match( $ext_preg, $filename, $ext_matches ) ) {
2223
			$type = $mime_match;
2224
			$ext = $ext_matches[1];
2225
			break;
2226
		}
2227
	}
2228
2229
	return compact( 'ext', 'type' );
2230
}
2231
2232
/**
2233
 * Attempt to determine the real file type of a file.
2234
 *
2235
 * If unable to, the file name extension will be used to determine type.
2236
 *
2237
 * If it's determined that the extension does not match the file's real type,
2238
 * then the "proper_filename" value will be set with a proper filename and extension.
2239
 *
2240
 * Currently this function only supports validating images known to getimagesize().
2241
 *
2242
 * @since 3.0.0
2243
 *
2244
 * @param string $file     Full path to the file.
2245
 * @param string $filename The name of the file (may differ from $file due to $file being
2246
 *                         in a tmp directory).
2247
 * @param array   $mimes   Optional. Key is the file extension with value as the mime type.
2248
 * @return array Values for the extension, MIME, and either a corrected filename or false
2249
 *               if original $filename is valid.
2250
 */
2251
function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) {
2252
	$proper_filename = false;
2253
2254
	// Do basic extension validation and MIME mapping
2255
	$wp_filetype = wp_check_filetype( $filename, $mimes );
2256
	$ext = $wp_filetype['ext'];
2257
	$type = $wp_filetype['type'];
2258
2259
	// We can't do any further validation without a file to work with
2260
	if ( ! file_exists( $file ) ) {
2261
		return compact( 'ext', 'type', 'proper_filename' );
2262
	}
2263
2264
	// We're able to validate images using GD
2265
	if ( $type && 0 === strpos( $type, 'image/' ) && function_exists('getimagesize') ) {
2266
2267
		// Attempt to figure out what type of image it actually is
2268
		$imgstats = @getimagesize( $file );
2269
2270
		// If getimagesize() knows what kind of image it really is and if the real MIME doesn't match the claimed MIME
2271
		if ( !empty($imgstats['mime']) && $imgstats['mime'] != $type ) {
2272
			/**
2273
			 * Filters the list mapping image mime types to their respective extensions.
2274
			 *
2275
			 * @since 3.0.0
2276
			 *
2277
			 * @param  array $mime_to_ext Array of image mime types and their matching extensions.
2278
			 */
2279
			$mime_to_ext = apply_filters( 'getimagesize_mimes_to_exts', array(
2280
				'image/jpeg' => 'jpg',
2281
				'image/png'  => 'png',
2282
				'image/gif'  => 'gif',
2283
				'image/bmp'  => 'bmp',
2284
				'image/tiff' => 'tif',
2285
			) );
2286
2287
			// Replace whatever is after the last period in the filename with the correct extension
2288
			if ( ! empty( $mime_to_ext[ $imgstats['mime'] ] ) ) {
2289
				$filename_parts = explode( '.', $filename );
2290
				array_pop( $filename_parts );
2291
				$filename_parts[] = $mime_to_ext[ $imgstats['mime'] ];
2292
				$new_filename = implode( '.', $filename_parts );
2293
2294
				if ( $new_filename != $filename ) {
2295
					$proper_filename = $new_filename; // Mark that it changed
2296
				}
2297
				// Redefine the extension / MIME
2298
				$wp_filetype = wp_check_filetype( $new_filename, $mimes );
2299
				$ext = $wp_filetype['ext'];
2300
				$type = $wp_filetype['type'];
2301
			}
2302
		}
2303
	}
2304
2305
	/**
2306
	 * Filters the "real" file type of the given file.
2307
	 *
2308
	 * @since 3.0.0
2309
	 *
2310
	 * @param array  $wp_check_filetype_and_ext File data array containing 'ext', 'type', and
2311
	 *                                          'proper_filename' keys.
2312
	 * @param string $file                      Full path to the file.
2313
	 * @param string $filename                  The name of the file (may differ from $file due to
2314
	 *                                          $file being in a tmp directory).
2315
	 * @param array  $mimes                     Key is the file extension with value as the mime type.
2316
	 */
2317
	return apply_filters( 'wp_check_filetype_and_ext', compact( 'ext', 'type', 'proper_filename' ), $file, $filename, $mimes );
2318
}
2319
2320
/**
2321
 * Retrieve list of mime types and file extensions.
2322
 *
2323
 * @since 3.5.0
2324
 * @since 4.2.0 Support was added for GIMP (xcf) files.
2325
 *
2326
 * @return array Array of mime types keyed by the file extension regex corresponding to those types.
2327
 */
2328
function wp_get_mime_types() {
2329
	/**
2330
	 * Filters the list of mime types and file extensions.
2331
	 *
2332
	 * This filter should be used to add, not remove, mime types. To remove
2333
	 * mime types, use the {@see 'upload_mimes'} filter.
2334
	 *
2335
	 * @since 3.5.0
2336
	 *
2337
	 * @param array $wp_get_mime_types Mime types keyed by the file extension regex
2338
	 *                                 corresponding to those types.
2339
	 */
2340
	return apply_filters( 'mime_types', array(
2341
	// Image formats.
2342
	'jpg|jpeg|jpe' => 'image/jpeg',
2343
	'gif' => 'image/gif',
2344
	'png' => 'image/png',
2345
	'bmp' => 'image/bmp',
2346
	'tiff|tif' => 'image/tiff',
2347
	'ico' => 'image/x-icon',
2348
	// Video formats.
2349
	'asf|asx' => 'video/x-ms-asf',
2350
	'wmv' => 'video/x-ms-wmv',
2351
	'wmx' => 'video/x-ms-wmx',
2352
	'wm' => 'video/x-ms-wm',
2353
	'avi' => 'video/avi',
2354
	'divx' => 'video/divx',
2355
	'flv' => 'video/x-flv',
2356
	'mov|qt' => 'video/quicktime',
2357
	'mpeg|mpg|mpe' => 'video/mpeg',
2358
	'mp4|m4v' => 'video/mp4',
2359
	'ogv' => 'video/ogg',
2360
	'webm' => 'video/webm',
2361
	'mkv' => 'video/x-matroska',
2362
	'3gp|3gpp' => 'video/3gpp', // Can also be audio
2363
	'3g2|3gp2' => 'video/3gpp2', // Can also be audio
2364
	// Text formats.
2365
	'txt|asc|c|cc|h|srt' => 'text/plain',
2366
	'csv' => 'text/csv',
2367
	'tsv' => 'text/tab-separated-values',
2368
	'ics' => 'text/calendar',
2369
	'rtx' => 'text/richtext',
2370
	'css' => 'text/css',
2371
	'htm|html' => 'text/html',
2372
	'vtt' => 'text/vtt',
2373
	'dfxp' => 'application/ttaf+xml',
2374
	// Audio formats.
2375
	'mp3|m4a|m4b' => 'audio/mpeg',
2376
	'ra|ram' => 'audio/x-realaudio',
2377
	'wav' => 'audio/wav',
2378
	'ogg|oga' => 'audio/ogg',
2379
	'mid|midi' => 'audio/midi',
2380
	'wma' => 'audio/x-ms-wma',
2381
	'wax' => 'audio/x-ms-wax',
2382
	'mka' => 'audio/x-matroska',
2383
	// Misc application formats.
2384
	'rtf' => 'application/rtf',
2385
	'js' => 'application/javascript',
2386
	'pdf' => 'application/pdf',
2387
	'swf' => 'application/x-shockwave-flash',
2388
	'class' => 'application/java',
2389
	'tar' => 'application/x-tar',
2390
	'zip' => 'application/zip',
2391
	'gz|gzip' => 'application/x-gzip',
2392
	'rar' => 'application/rar',
2393
	'7z' => 'application/x-7z-compressed',
2394
	'exe' => 'application/x-msdownload',
2395
	'psd' => 'application/octet-stream',
2396
	'xcf' => 'application/octet-stream',
2397
	// MS Office formats.
2398
	'doc' => 'application/msword',
2399
	'pot|pps|ppt' => 'application/vnd.ms-powerpoint',
2400
	'wri' => 'application/vnd.ms-write',
2401
	'xla|xls|xlt|xlw' => 'application/vnd.ms-excel',
2402
	'mdb' => 'application/vnd.ms-access',
2403
	'mpp' => 'application/vnd.ms-project',
2404
	'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
2405
	'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
2406
	'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
2407
	'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
2408
	'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
2409
	'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
2410
	'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
2411
	'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
2412
	'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
2413
	'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
2414
	'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
2415
	'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
2416
	'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
2417
	'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
2418
	'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
2419
	'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
2420
	'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
2421
	'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
2422
	'sldm' => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
2423
	'onetoc|onetoc2|onetmp|onepkg' => 'application/onenote',
2424
	'oxps' => 'application/oxps',
2425
	'xps' => 'application/vnd.ms-xpsdocument',
2426
	// OpenOffice formats.
2427
	'odt' => 'application/vnd.oasis.opendocument.text',
2428
	'odp' => 'application/vnd.oasis.opendocument.presentation',
2429
	'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
2430
	'odg' => 'application/vnd.oasis.opendocument.graphics',
2431
	'odc' => 'application/vnd.oasis.opendocument.chart',
2432
	'odb' => 'application/vnd.oasis.opendocument.database',
2433
	'odf' => 'application/vnd.oasis.opendocument.formula',
2434
	// WordPerfect formats.
2435
	'wp|wpd' => 'application/wordperfect',
2436
	// iWork formats.
2437
	'key' => 'application/vnd.apple.keynote',
2438
	'numbers' => 'application/vnd.apple.numbers',
2439
	'pages' => 'application/vnd.apple.pages',
2440
	) );
2441
}
2442
2443
/**
2444
 * Retrieves the list of common file extensions and their types.
2445
 *
2446
 * @since 4.6.0
2447
 *
2448
 * @return array Array of file extensions types keyed by the type of file.
2449
 */
2450
function wp_get_ext_types() {
2451
2452
	/**
2453
	 * Filters file type based on the extension name.
2454
	 *
2455
	 * @since 2.5.0
2456
	 *
2457
	 * @see wp_ext2type()
2458
	 *
2459
	 * @param array $ext2type Multi-dimensional array with extensions for a default set
2460
	 *                        of file types.
2461
	 */
2462
	return apply_filters( 'ext2type', array(
2463
		'image'       => array( 'jpg', 'jpeg', 'jpe',  'gif',  'png',  'bmp',   'tif',  'tiff', 'ico' ),
2464
		'audio'       => array( 'aac', 'ac3',  'aif',  'aiff', 'm3a',  'm4a',   'm4b',  'mka',  'mp1',  'mp2',  'mp3', 'ogg', 'oga', 'ram', 'wav', 'wma' ),
2465
		'video'       => array( '3g2',  '3gp', '3gpp', 'asf', 'avi',  'divx', 'dv',   'flv',  'm4v',   'mkv',  'mov',  'mp4',  'mpeg', 'mpg', 'mpv', 'ogm', 'ogv', 'qt',  'rm', 'vob', 'wmv' ),
2466
		'document'    => array( 'doc', 'docx', 'docm', 'dotm', 'odt',  'pages', 'pdf',  'xps',  'oxps', 'rtf',  'wp', 'wpd', 'psd', 'xcf' ),
2467
		'spreadsheet' => array( 'numbers',     'ods',  'xls',  'xlsx', 'xlsm',  'xlsb' ),
2468
		'interactive' => array( 'swf', 'key',  'ppt',  'pptx', 'pptm', 'pps',   'ppsx', 'ppsm', 'sldx', 'sldm', 'odp' ),
2469
		'text'        => array( 'asc', 'csv',  'tsv',  'txt' ),
2470
		'archive'     => array( 'bz2', 'cab',  'dmg',  'gz',   'rar',  'sea',   'sit',  'sqx',  'tar',  'tgz',  'zip', '7z' ),
2471
		'code'        => array( 'css', 'htm',  'html', 'php',  'js' ),
2472
	) );
2473
}
2474
2475
/**
2476
 * Retrieve list of allowed mime types and file extensions.
2477
 *
2478
 * @since 2.8.6
2479
 *
2480
 * @param int|WP_User $user Optional. User to check. Defaults to current user.
2481
 * @return array Array of mime types keyed by the file extension regex corresponding
2482
 *               to those types.
2483
 */
2484
function get_allowed_mime_types( $user = null ) {
2485
	$t = wp_get_mime_types();
2486
2487
	unset( $t['swf'], $t['exe'] );
2488
	if ( function_exists( 'current_user_can' ) )
2489
		$unfiltered = $user ? user_can( $user, 'unfiltered_html' ) : current_user_can( 'unfiltered_html' );
2490
2491
	if ( empty( $unfiltered ) )
2492
		unset( $t['htm|html'] );
2493
2494
	/**
2495
	 * Filters list of allowed mime types and file extensions.
2496
	 *
2497
	 * @since 2.0.0
2498
	 *
2499
	 * @param array            $t    Mime types keyed by the file extension regex corresponding to
2500
	 *                               those types. 'swf' and 'exe' removed from full list. 'htm|html' also
2501
	 *                               removed depending on '$user' capabilities.
2502
	 * @param int|WP_User|null $user User ID, User object or null if not provided (indicates current user).
2503
	 */
2504
	return apply_filters( 'upload_mimes', $t, $user );
2505
}
2506
2507
/**
2508
 * Display "Are You Sure" message to confirm the action being taken.
2509
 *
2510
 * If the action has the nonce explain message, then it will be displayed
2511
 * along with the "Are you sure?" message.
2512
 *
2513
 * @since 2.0.4
2514
 *
2515
 * @param string $action The nonce action.
2516
 */
2517
function wp_nonce_ays( $action ) {
2518
	if ( 'log-out' == $action ) {
2519
		$html = sprintf( __( 'You are attempting to log out of %s' ), get_bloginfo( 'name' ) ) . '</p><p>';
2520
		$redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '';
2521
		$html .= sprintf( __( "Do you really want to <a href='%s'>log out</a>?"), wp_logout_url( $redirect_to ) );
2522
	} else {
2523
		$html = __( 'Are you sure you want to do this?' );
2524
		if ( wp_get_referer() )
2525
			$html .= "</p><p><a href='" . esc_url( remove_query_arg( 'updated', wp_get_referer() ) ) . "'>" . __( 'Please try again.' ) . "</a>";
0 ignored issues
show
It seems like remove_query_arg('updated', wp_get_referer()) targeting remove_query_arg() can also be of type boolean; however, esc_url() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2526
	}
2527
2528
	wp_die( $html, __( 'WordPress Failure Notice' ), 403 );
2529
}
2530
2531
/**
2532
 * Kill WordPress execution and display HTML message with error message.
2533
 *
2534
 * This function complements the `die()` PHP function. The difference is that
2535
 * HTML will be displayed to the user. It is recommended to use this function
2536
 * only when the execution should not continue any further. It is not recommended
2537
 * to call this function very often, and try to handle as many errors as possible
2538
 * silently or more gracefully.
2539
 *
2540
 * As a shorthand, the desired HTTP response code may be passed as an integer to
2541
 * the `$title` parameter (the default title would apply) or the `$args` parameter.
2542
 *
2543
 * @since 2.0.4
2544
 * @since 4.1.0 The `$title` and `$args` parameters were changed to optionally accept
2545
 *              an integer to be used as the response code.
2546
 *
2547
 * @param string|WP_Error  $message Optional. Error message. If this is a WP_Error object,
2548
 *                                  the error's messages are used. Default empty.
2549
 * @param string|int       $title   Optional. Error title. If `$message` is a `WP_Error` object,
2550
 *                                  error data with the key 'title' may be used to specify the title.
2551
 *                                  If `$title` is an integer, then it is treated as the response
2552
 *                                  code. Default empty.
2553
 * @param string|array|int $args {
2554
 *     Optional. Arguments to control behavior. If `$args` is an integer, then it is treated
2555
 *     as the response code. Default empty array.
2556
 *
2557
 *     @type int    $response       The HTTP response code. Default 500.
2558
 *     @type bool   $back_link      Whether to include a link to go back. Default false.
2559
 *     @type string $text_direction The text direction. This is only useful internally, when WordPress
2560
 *                                  is still loading and the site's locale is not set up yet. Accepts 'rtl'.
2561
 *                                  Default is the value of is_rtl().
2562
 * }
2563
 */
2564
function wp_die( $message = '', $title = '', $args = array() ) {
2565
2566
	if ( is_int( $args ) ) {
2567
		$args = array( 'response' => $args );
2568
	} elseif ( is_int( $title ) ) {
2569
		$args  = array( 'response' => $title );
2570
		$title = '';
2571
	}
2572
2573
	if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
2574
		/**
2575
		 * Filters the callback for killing WordPress execution for Ajax requests.
2576
		 *
2577
		 * @since 3.4.0
2578
		 *
2579
		 * @param callable $function Callback function name.
2580
		 */
2581
		$function = apply_filters( 'wp_die_ajax_handler', '_ajax_wp_die_handler' );
2582
	} elseif ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
2583
		/**
2584
		 * Filters the callback for killing WordPress execution for XML-RPC requests.
2585
		 *
2586
		 * @since 3.4.0
2587
		 *
2588
		 * @param callable $function Callback function name.
2589
		 */
2590
		$function = apply_filters( 'wp_die_xmlrpc_handler', '_xmlrpc_wp_die_handler' );
2591
	} else {
2592
		/**
2593
		 * Filters the callback for killing WordPress execution for all non-Ajax, non-XML-RPC requests.
2594
		 *
2595
		 * @since 3.0.0
2596
		 *
2597
		 * @param callable $function Callback function name.
2598
		 */
2599
		$function = apply_filters( 'wp_die_handler', '_default_wp_die_handler' );
2600
	}
2601
2602
	call_user_func( $function, $message, $title, $args );
2603
}
2604
2605
/**
2606
 * Kills WordPress execution and display HTML message with error message.
2607
 *
2608
 * This is the default handler for wp_die if you want a custom one for your
2609
 * site then you can overload using the {@see 'wp_die_handler'} filter in wp_die().
2610
 *
2611
 * @since 3.0.0
2612
 * @access private
2613
 *
2614
 * @param string       $message Error message.
2615
 * @param string       $title   Optional. Error title. Default empty.
2616
 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
2617
 */
2618
function _default_wp_die_handler( $message, $title = '', $args = array() ) {
2619
	$defaults = array( 'response' => 500 );
2620
	$r = wp_parse_args($args, $defaults);
2621
2622
	$have_gettext = function_exists('__');
2623
2624
	if ( function_exists( 'is_wp_error' ) && is_wp_error( $message ) ) {
2625
		if ( empty( $title ) ) {
2626
			$error_data = $message->get_error_data();
0 ignored issues
show
The method get_error_data cannot be called on $message (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
2627
			if ( is_array( $error_data ) && isset( $error_data['title'] ) )
2628
				$title = $error_data['title'];
2629
		}
2630
		$errors = $message->get_error_messages();
0 ignored issues
show
The method get_error_messages cannot be called on $message (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
2631
		switch ( count( $errors ) ) {
2632
		case 0 :
2633
			$message = '';
2634
			break;
2635
		case 1 :
2636
			$message = "<p>{$errors[0]}</p>";
2637
			break;
2638
		default :
2639
			$message = "<ul>\n\t\t<li>" . join( "</li>\n\t\t<li>", $errors ) . "</li>\n\t</ul>";
2640
			break;
2641
		}
2642
	} elseif ( is_string( $message ) ) {
2643
		$message = "<p>$message</p>";
2644
	}
2645
2646
	if ( isset( $r['back_link'] ) && $r['back_link'] ) {
2647
		$back_text = $have_gettext? __('&laquo; Back') : '&laquo; Back';
2648
		$message .= "\n<p><a href='javascript:history.back()'>$back_text</a></p>";
2649
	}
2650
2651
	if ( ! did_action( 'admin_head' ) ) :
2652
		if ( !headers_sent() ) {
2653
			status_header( $r['response'] );
2654
			nocache_headers();
2655
			header( 'Content-Type: text/html; charset=utf-8' );
2656
		}
2657
2658
		if ( empty($title) )
2659
			$title = $have_gettext ? __('WordPress &rsaquo; Error') : 'WordPress &rsaquo; Error';
2660
2661
		$text_direction = 'ltr';
2662
		if ( isset($r['text_direction']) && 'rtl' == $r['text_direction'] )
2663
			$text_direction = 'rtl';
2664
		elseif ( function_exists( 'is_rtl' ) && is_rtl() )
2665
			$text_direction = 'rtl';
2666
?>
2667
<!DOCTYPE html>
2668
<!-- Ticket #11289, IE bug fix: always pad the error page with enough characters such that it is greater than 512 bytes, even after gzip compression abcdefghijklmnopqrstuvwxyz1234567890aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz11223344556677889900abacbcbdcdcededfefegfgfhghgihihjijikjkjlklkmlmlnmnmononpopoqpqprqrqsrsrtstsubcbcdcdedefefgfabcadefbghicjkldmnoepqrfstugvwxhyz1i234j567k890laabmbccnddeoeffpgghqhiirjjksklltmmnunoovppqwqrrxsstytuuzvvw0wxx1yyz2z113223434455666777889890091abc2def3ghi4jkl5mno6pqr7stu8vwx9yz11aab2bcc3dd4ee5ff6gg7hh8ii9j0jk1kl2lmm3nnoo4p5pq6qrr7ss8tt9uuvv0wwx1x2yyzz13aba4cbcb5dcdc6dedfef8egf9gfh0ghg1ihi2hji3jik4jkj5lkl6kml7mln8mnm9ono
2669
-->
2670
<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'"; ?>>
2671
<head>
2672
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
2673
	<meta name="viewport" content="width=device-width">
2674
	<?php
2675
	if ( function_exists( 'wp_no_robots' ) ) {
2676
		wp_no_robots();
2677
	}
2678
	?>
2679
	<title><?php echo $title ?></title>
2680
	<style type="text/css">
2681
		html {
2682
			background: #f1f1f1;
2683
		}
2684
		body {
2685
			background: #fff;
2686
			color: #444;
2687
			font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
2688
			margin: 2em auto;
2689
			padding: 1em 2em;
2690
			max-width: 700px;
2691
			-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.13);
2692
			box-shadow: 0 1px 3px rgba(0,0,0,0.13);
2693
		}
2694
		h1 {
2695
			border-bottom: 1px solid #dadada;
2696
			clear: both;
2697
			color: #666;
2698
			font-size: 24px;
2699
			margin: 30px 0 0 0;
2700
			padding: 0;
2701
			padding-bottom: 7px;
2702
		}
2703
		#error-page {
2704
			margin-top: 50px;
2705
		}
2706
		#error-page p {
2707
			font-size: 14px;
2708
			line-height: 1.5;
2709
			margin: 25px 0 20px;
2710
		}
2711
		#error-page code {
2712
			font-family: Consolas, Monaco, monospace;
2713
		}
2714
		ul li {
2715
			margin-bottom: 10px;
2716
			font-size: 14px ;
2717
		}
2718
		a {
2719
			color: #0073aa;
2720
		}
2721
		a:hover,
2722
		a:active {
2723
			color: #00a0d2;
2724
		}
2725
		a:focus {
2726
			color: #124964;
2727
		    -webkit-box-shadow:
2728
		    	0 0 0 1px #5b9dd9,
2729
				0 0 2px 1px rgba(30, 140, 190, .8);
2730
		    box-shadow:
2731
		    	0 0 0 1px #5b9dd9,
2732
				0 0 2px 1px rgba(30, 140, 190, .8);
2733
			outline: none;
2734
		}
2735
		.button {
2736
			background: #f7f7f7;
2737
			border: 1px solid #ccc;
2738
			color: #555;
2739
			display: inline-block;
2740
			text-decoration: none;
2741
			font-size: 13px;
2742
			line-height: 26px;
2743
			height: 28px;
2744
			margin: 0;
2745
			padding: 0 10px 1px;
2746
			cursor: pointer;
2747
			-webkit-border-radius: 3px;
2748
			-webkit-appearance: none;
2749
			border-radius: 3px;
2750
			white-space: nowrap;
2751
			-webkit-box-sizing: border-box;
2752
			-moz-box-sizing:    border-box;
2753
			box-sizing:         border-box;
2754
2755
			-webkit-box-shadow: 0 1px 0 #ccc;
2756
			box-shadow: 0 1px 0 #ccc;
2757
		 	vertical-align: top;
2758
		}
2759
2760
		.button.button-large {
2761
			height: 30px;
2762
			line-height: 28px;
2763
			padding: 0 12px 2px;
2764
		}
2765
2766
		.button:hover,
2767
		.button:focus {
2768
			background: #fafafa;
2769
			border-color: #999;
2770
			color: #23282d;
2771
		}
2772
2773
		.button:focus  {
2774
			border-color: #5b9dd9;
2775
			-webkit-box-shadow: 0 0 3px rgba( 0, 115, 170, .8 );
2776
			box-shadow: 0 0 3px rgba( 0, 115, 170, .8 );
2777
			outline: none;
2778
		}
2779
2780
		.button:active {
2781
			background: #eee;
2782
			border-color: #999;
2783
		 	-webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 );
2784
		 	box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 );
2785
		 	-webkit-transform: translateY(1px);
2786
		 	-ms-transform: translateY(1px);
2787
		 	transform: translateY(1px);
2788
		}
2789
2790
		<?php
2791
		if ( 'rtl' == $text_direction ) {
2792
			echo 'body { font-family: Tahoma, Arial; }';
2793
		}
2794
		?>
2795
	</style>
2796
</head>
2797
<body id="error-page">
2798
<?php endif; // ! did_action( 'admin_head' ) ?>
2799
	<?php echo $message; ?>
2800
</body>
2801
</html>
2802
<?php
2803
	die();
2804
}
2805
2806
/**
2807
 * Kill WordPress execution and display XML message with error message.
2808
 *
2809
 * This is the handler for wp_die when processing XMLRPC requests.
2810
 *
2811
 * @since 3.2.0
2812
 * @access private
2813
 *
2814
 * @global wp_xmlrpc_server $wp_xmlrpc_server
2815
 *
2816
 * @param string       $message Error message.
2817
 * @param string       $title   Optional. Error title. Default empty.
2818
 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
2819
 */
2820
function _xmlrpc_wp_die_handler( $message, $title = '', $args = array() ) {
2821
	global $wp_xmlrpc_server;
2822
	$defaults = array( 'response' => 500 );
2823
2824
	$r = wp_parse_args($args, $defaults);
2825
2826
	if ( $wp_xmlrpc_server ) {
2827
		$error = new IXR_Error( $r['response'] , $message);
2828
		$wp_xmlrpc_server->output( $error->getXml() );
2829
	}
2830
	die();
2831
}
2832
2833
/**
2834
 * Kill WordPress ajax execution.
2835
 *
2836
 * This is the handler for wp_die when processing Ajax requests.
2837
 *
2838
 * @since 3.4.0
2839
 * @access private
2840
 *
2841
 * @param string $message Optional. Response to print. Default empty.
2842
 */
2843
function _ajax_wp_die_handler( $message = '' ) {
2844
	if ( is_scalar( $message ) )
2845
		die( (string) $message );
2846
	die( '0' );
2847
}
2848
2849
/**
2850
 * Kill WordPress execution.
2851
 *
2852
 * This is the handler for wp_die when processing APP requests.
2853
 *
2854
 * @since 3.4.0
2855
 * @access private
2856
 *
2857
 * @param string $message Optional. Response to print. Default empty.
2858
 */
2859
function _scalar_wp_die_handler( $message = '' ) {
2860
	if ( is_scalar( $message ) )
2861
		die( (string) $message );
2862
	die();
2863
}
2864
2865
/**
2866
 * Encode a variable into JSON, with some sanity checks.
2867
 *
2868
 * @since 4.1.0
2869
 *
2870
 * @param mixed $data    Variable (usually an array or object) to encode as JSON.
2871
 * @param int   $options Optional. Options to be passed to json_encode(). Default 0.
2872
 * @param int   $depth   Optional. Maximum depth to walk through $data. Must be
2873
 *                       greater than 0. Default 512.
2874
 * @return string|false The JSON encoded string, or false if it cannot be encoded.
2875
 */
2876
function wp_json_encode( $data, $options = 0, $depth = 512 ) {
2877
	/*
2878
	 * json_encode() has had extra params added over the years.
2879
	 * $options was added in 5.3, and $depth in 5.5.
2880
	 * We need to make sure we call it with the correct arguments.
2881
	 */
2882
	if ( version_compare( PHP_VERSION, '5.5', '>=' ) ) {
2883
		$args = array( $data, $options, $depth );
2884
	} elseif ( version_compare( PHP_VERSION, '5.3', '>=' ) ) {
2885
		$args = array( $data, $options );
2886
	} else {
2887
		$args = array( $data );
2888
	}
2889
2890
	// Prepare the data for JSON serialization.
2891
	$args[0] = _wp_json_prepare_data( $data );
2892
2893
	$json = @call_user_func_array( 'json_encode', $args );
2894
2895
	// If json_encode() was successful, no need to do more sanity checking.
2896
	// ... unless we're in an old version of PHP, and json_encode() returned
2897
	// a string containing 'null'. Then we need to do more sanity checking.
2898
	if ( false !== $json && ( version_compare( PHP_VERSION, '5.5', '>=' ) || false === strpos( $json, 'null' ) ) )  {
2899
		return $json;
2900
	}
2901
2902
	try {
2903
		$args[0] = _wp_json_sanity_check( $data, $depth );
2904
	} catch ( Exception $e ) {
2905
		return false;
2906
	}
2907
2908
	return call_user_func_array( 'json_encode', $args );
2909
}
2910
2911
/**
2912
 * Perform sanity checks on data that shall be encoded to JSON.
2913
 *
2914
 * @ignore
2915
 * @since 4.1.0
2916
 * @access private
2917
 *
2918
 * @see wp_json_encode()
2919
 *
2920
 * @param mixed $data  Variable (usually an array or object) to encode as JSON.
2921
 * @param int   $depth Maximum depth to walk through $data. Must be greater than 0.
2922
 * @return mixed The sanitized data that shall be encoded to JSON.
2923
 */
2924
function _wp_json_sanity_check( $data, $depth ) {
2925
	if ( $depth < 0 ) {
2926
		throw new Exception( 'Reached depth limit' );
2927
	}
2928
2929
	if ( is_array( $data ) ) {
2930
		$output = array();
2931
		foreach ( $data as $id => $el ) {
2932
			// Don't forget to sanitize the ID!
2933
			if ( is_string( $id ) ) {
2934
				$clean_id = _wp_json_convert_string( $id );
2935
			} else {
2936
				$clean_id = $id;
2937
			}
2938
2939
			// Check the element type, so that we're only recursing if we really have to.
2940
			if ( is_array( $el ) || is_object( $el ) ) {
2941
				$output[ $clean_id ] = _wp_json_sanity_check( $el, $depth - 1 );
2942
			} elseif ( is_string( $el ) ) {
2943
				$output[ $clean_id ] = _wp_json_convert_string( $el );
2944
			} else {
2945
				$output[ $clean_id ] = $el;
2946
			}
2947
		}
2948
	} elseif ( is_object( $data ) ) {
2949
		$output = new stdClass;
2950
		foreach ( $data as $id => $el ) {
2951
			if ( is_string( $id ) ) {
2952
				$clean_id = _wp_json_convert_string( $id );
2953
			} else {
2954
				$clean_id = $id;
2955
			}
2956
2957
			if ( is_array( $el ) || is_object( $el ) ) {
2958
				$output->$clean_id = _wp_json_sanity_check( $el, $depth - 1 );
2959
			} elseif ( is_string( $el ) ) {
2960
				$output->$clean_id = _wp_json_convert_string( $el );
2961
			} else {
2962
				$output->$clean_id = $el;
2963
			}
2964
		}
2965
	} elseif ( is_string( $data ) ) {
2966
		return _wp_json_convert_string( $data );
2967
	} else {
2968
		return $data;
2969
	}
2970
2971
	return $output;
2972
}
2973
2974
/**
2975
 * Convert a string to UTF-8, so that it can be safely encoded to JSON.
2976
 *
2977
 * @ignore
2978
 * @since 4.1.0
2979
 * @access private
2980
 *
2981
 * @see _wp_json_sanity_check()
2982
 *
2983
 * @staticvar bool $use_mb
2984
 *
2985
 * @param string $string The string which is to be converted.
2986
 * @return string The checked string.
2987
 */
2988
function _wp_json_convert_string( $string ) {
2989
	static $use_mb = null;
2990
	if ( is_null( $use_mb ) ) {
2991
		$use_mb = function_exists( 'mb_convert_encoding' );
2992
	}
2993
2994
	if ( $use_mb ) {
2995
		$encoding = mb_detect_encoding( $string, mb_detect_order(), true );
2996
		if ( $encoding ) {
2997
			return mb_convert_encoding( $string, 'UTF-8', $encoding );
2998
		} else {
2999
			return mb_convert_encoding( $string, 'UTF-8', 'UTF-8' );
3000
		}
3001
	} else {
3002
		return wp_check_invalid_utf8( $string, true );
3003
	}
3004
}
3005
3006
/**
3007
 * Prepares response data to be serialized to JSON.
3008
 *
3009
 * This supports the JsonSerializable interface for PHP 5.2-5.3 as well.
3010
 *
3011
 * @ignore
3012
 * @since 4.4.0
3013
 * @access private
3014
 *
3015
 * @param mixed $data Native representation.
3016
 * @return bool|int|float|null|string|array Data ready for `json_encode()`.
3017
 */
3018
function _wp_json_prepare_data( $data ) {
3019
	if ( ! defined( 'WP_JSON_SERIALIZE_COMPATIBLE' ) || WP_JSON_SERIALIZE_COMPATIBLE === false ) {
3020
		return $data;
3021
	}
3022
3023
	switch ( gettype( $data ) ) {
3024
		case 'boolean':
3025
		case 'integer':
3026
		case 'double':
3027
		case 'string':
3028
		case 'NULL':
3029
			// These values can be passed through.
3030
			return $data;
3031
3032
		case 'array':
3033
			// Arrays must be mapped in case they also return objects.
3034
			return array_map( '_wp_json_prepare_data', $data );
3035
3036
		case 'object':
3037
			// If this is an incomplete object (__PHP_Incomplete_Class), bail.
3038
			if ( ! is_object( $data ) ) {
3039
				return null;
3040
			}
3041
3042
			if ( $data instanceof JsonSerializable ) {
3043
				$data = $data->jsonSerialize();
3044
			} else {
3045
				$data = get_object_vars( $data );
3046
			}
3047
3048
			// Now, pass the array (or whatever was returned from jsonSerialize through).
3049
			return _wp_json_prepare_data( $data );
3050
3051
		default:
3052
			return null;
3053
	}
3054
}
3055
3056
/**
3057
 * Send a JSON response back to an Ajax request.
3058
 *
3059
 * @since 3.5.0
3060
 *
3061
 * @param mixed $response Variable (usually an array or object) to encode as JSON,
3062
 *                        then print and die.
3063
 */
3064
function wp_send_json( $response ) {
3065
	@header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
3066
	echo wp_json_encode( $response );
3067
	if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
3068
		wp_die();
3069
	else
3070
		die;
3071
}
3072
3073
/**
3074
 * Send a JSON response back to an Ajax request, indicating success.
3075
 *
3076
 * @since 3.5.0
3077
 *
3078
 * @param mixed $data Data to encode as JSON, then print and die.
3079
 */
3080
function wp_send_json_success( $data = null ) {
3081
	$response = array( 'success' => true );
3082
3083
	if ( isset( $data ) )
3084
		$response['data'] = $data;
3085
3086
	wp_send_json( $response );
3087
}
3088
3089
/**
3090
 * Send a JSON response back to an Ajax request, indicating failure.
3091
 *
3092
 * If the `$data` parameter is a WP_Error object, the errors
3093
 * within the object are processed and output as an array of error
3094
 * codes and corresponding messages. All other types are output
3095
 * without further processing.
3096
 *
3097
 * @since 3.5.0
3098
 * @since 4.1.0 The `$data` parameter is now processed if a WP_Error object is passed in.
3099
 *
3100
 * @param mixed $data Data to encode as JSON, then print and die.
3101
 */
3102
function wp_send_json_error( $data = null ) {
3103
	$response = array( 'success' => false );
3104
3105
	if ( isset( $data ) ) {
3106
		if ( is_wp_error( $data ) ) {
3107
			$result = array();
3108
			foreach ( $data->errors as $code => $messages ) {
3109
				foreach ( $messages as $message ) {
3110
					$result[] = array( 'code' => $code, 'message' => $message );
3111
				}
3112
			}
3113
3114
			$response['data'] = $result;
3115
		} else {
3116
			$response['data'] = $data;
3117
		}
3118
	}
3119
3120
	wp_send_json( $response );
3121
}
3122
3123
/**
3124
 * Checks that a JSONP callback is a valid JavaScript callback.
3125
 *
3126
 * Only allows alphanumeric characters and the dot character in callback
3127
 * function names. This helps to mitigate XSS attacks caused by directly
3128
 * outputting user input.
3129
 *
3130
 * @since 4.6.0
3131
 *
3132
 * @param string $callback Supplied JSONP callback function.
3133
 * @return bool True if valid callback, otherwise false.
3134
 */
3135
function wp_check_jsonp_callback( $callback ) {
3136
	if ( ! is_string( $callback ) ) {
3137
		return false;
3138
	}
3139
3140
	preg_replace( '/[^\w\.]/', '', $callback, -1, $illegal_char_count );
3141
3142
	return 0 === $illegal_char_count;
3143
}
3144
3145
/**
3146
 * Retrieve the WordPress home page URL.
3147
 *
3148
 * If the constant named 'WP_HOME' exists, then it will be used and returned
3149
 * by the function. This can be used to counter the redirection on your local
3150
 * development environment.
3151
 *
3152
 * @since 2.2.0
3153
 * @access private
3154
 *
3155
 * @see WP_HOME
3156
 *
3157
 * @param string $url URL for the home location.
3158
 * @return string Homepage location.
3159
 */
3160
function _config_wp_home( $url = '' ) {
3161
	if ( defined( 'WP_HOME' ) )
3162
		return untrailingslashit( WP_HOME );
3163
	return $url;
3164
}
3165
3166
/**
3167
 * Retrieve the WordPress site URL.
3168
 *
3169
 * If the constant named 'WP_SITEURL' is defined, then the value in that
3170
 * constant will always be returned. This can be used for debugging a site
3171
 * on your localhost while not having to change the database to your URL.
3172
 *
3173
 * @since 2.2.0
3174
 * @access private
3175
 *
3176
 * @see WP_SITEURL
3177
 *
3178
 * @param string $url URL to set the WordPress site location.
3179
 * @return string The WordPress Site URL.
3180
 */
3181
function _config_wp_siteurl( $url = '' ) {
3182
	if ( defined( 'WP_SITEURL' ) )
3183
		return untrailingslashit( WP_SITEURL );
3184
	return $url;
3185
}
3186
3187
/**
3188
 * Set the localized direction for MCE plugin.
3189
 *
3190
 * Will only set the direction to 'rtl', if the WordPress locale has
3191
 * the text direction set to 'rtl'.
3192
 *
3193
 * Fills in the 'directionality' setting, enables the 'directionality'
3194
 * plugin, and adds the 'ltr' button to 'toolbar1', formerly
3195
 * 'theme_advanced_buttons1' array keys. These keys are then returned
3196
 * in the $input (TinyMCE settings) array.
3197
 *
3198
 * @since 2.1.0
3199
 * @access private
3200
 *
3201
 * @param array $input MCE settings array.
3202
 * @return array Direction set for 'rtl', if needed by locale.
3203
 */
3204
function _mce_set_direction( $input ) {
3205
	if ( is_rtl() ) {
3206
		$input['directionality'] = 'rtl';
3207
3208
		if ( ! empty( $input['plugins'] ) && strpos( $input['plugins'], 'directionality' ) === false ) {
3209
			$input['plugins'] .= ',directionality';
3210
		}
3211
3212
		if ( ! empty( $input['toolbar1'] ) && ! preg_match( '/\bltr\b/', $input['toolbar1'] ) ) {
3213
			$input['toolbar1'] .= ',ltr';
3214
		}
3215
	}
3216
3217
	return $input;
3218
}
3219
3220
3221
/**
3222
 * Convert smiley code to the icon graphic file equivalent.
3223
 *
3224
 * You can turn off smilies, by going to the write setting screen and unchecking
3225
 * the box, or by setting 'use_smilies' option to false or removing the option.
3226
 *
3227
 * Plugins may override the default smiley list by setting the $wpsmiliestrans
3228
 * to an array, with the key the code the blogger types in and the value the
3229
 * image file.
3230
 *
3231
 * The $wp_smiliessearch global is for the regular expression and is set each
3232
 * time the function is called.
3233
 *
3234
 * The full list of smilies can be found in the function and won't be listed in
3235
 * the description. Probably should create a Codex page for it, so that it is
3236
 * available.
3237
 *
3238
 * @global array $wpsmiliestrans
3239
 * @global array $wp_smiliessearch
3240
 *
3241
 * @since 2.2.0
3242
 */
3243
function smilies_init() {
3244
	global $wpsmiliestrans, $wp_smiliessearch;
3245
3246
	// don't bother setting up smilies if they are disabled
3247
	if ( !get_option( 'use_smilies' ) )
3248
		return;
3249
3250
	if ( !isset( $wpsmiliestrans ) ) {
3251
		$wpsmiliestrans = array(
3252
		':mrgreen:' => 'mrgreen.png',
3253
		':neutral:' => "\xf0\x9f\x98\x90",
3254
		':twisted:' => "\xf0\x9f\x98\x88",
3255
		  ':arrow:' => "\xe2\x9e\xa1",
3256
		  ':shock:' => "\xf0\x9f\x98\xaf",
3257
		  ':smile:' => "\xf0\x9f\x99\x82",
3258
		    ':???:' => "\xf0\x9f\x98\x95",
3259
		   ':cool:' => "\xf0\x9f\x98\x8e",
3260
		   ':evil:' => "\xf0\x9f\x91\xbf",
3261
		   ':grin:' => "\xf0\x9f\x98\x80",
3262
		   ':idea:' => "\xf0\x9f\x92\xa1",
3263
		   ':oops:' => "\xf0\x9f\x98\xb3",
3264
		   ':razz:' => "\xf0\x9f\x98\x9b",
3265
		   ':roll:' => "\xf0\x9f\x99\x84",
3266
		   ':wink:' => "\xf0\x9f\x98\x89",
3267
		    ':cry:' => "\xf0\x9f\x98\xa5",
3268
		    ':eek:' => "\xf0\x9f\x98\xae",
3269
		    ':lol:' => "\xf0\x9f\x98\x86",
3270
		    ':mad:' => "\xf0\x9f\x98\xa1",
3271
		    ':sad:' => "\xf0\x9f\x99\x81",
3272
		      '8-)' => "\xf0\x9f\x98\x8e",
3273
		      '8-O' => "\xf0\x9f\x98\xaf",
3274
		      ':-(' => "\xf0\x9f\x99\x81",
3275
		      ':-)' => "\xf0\x9f\x99\x82",
3276
		      ':-?' => "\xf0\x9f\x98\x95",
3277
		      ':-D' => "\xf0\x9f\x98\x80",
3278
		      ':-P' => "\xf0\x9f\x98\x9b",
3279
		      ':-o' => "\xf0\x9f\x98\xae",
3280
		      ':-x' => "\xf0\x9f\x98\xa1",
3281
		      ':-|' => "\xf0\x9f\x98\x90",
3282
		      ';-)' => "\xf0\x9f\x98\x89",
3283
		// This one transformation breaks regular text with frequency.
3284
		//     '8)' => "\xf0\x9f\x98\x8e",
3285
		       '8O' => "\xf0\x9f\x98\xaf",
3286
		       ':(' => "\xf0\x9f\x99\x81",
3287
		       ':)' => "\xf0\x9f\x99\x82",
3288
		       ':?' => "\xf0\x9f\x98\x95",
3289
		       ':D' => "\xf0\x9f\x98\x80",
3290
		       ':P' => "\xf0\x9f\x98\x9b",
3291
		       ':o' => "\xf0\x9f\x98\xae",
3292
		       ':x' => "\xf0\x9f\x98\xa1",
3293
		       ':|' => "\xf0\x9f\x98\x90",
3294
		       ';)' => "\xf0\x9f\x98\x89",
3295
		      ':!:' => "\xe2\x9d\x97",
3296
		      ':?:' => "\xe2\x9d\x93",
3297
		);
3298
	}
3299
3300
	if (count($wpsmiliestrans) == 0) {
3301
		return;
3302
	}
3303
3304
	/*
3305
	 * NOTE: we sort the smilies in reverse key order. This is to make sure
3306
	 * we match the longest possible smilie (:???: vs :?) as the regular
3307
	 * expression used below is first-match
3308
	 */
3309
	krsort($wpsmiliestrans);
3310
3311
	$spaces = wp_spaces_regexp();
3312
3313
	// Begin first "subpattern"
3314
	$wp_smiliessearch = '/(?<=' . $spaces . '|^)';
3315
3316
	$subchar = '';
3317
	foreach ( (array) $wpsmiliestrans as $smiley => $img ) {
3318
		$firstchar = substr($smiley, 0, 1);
3319
		$rest = substr($smiley, 1);
3320
3321
		// new subpattern?
3322
		if ($firstchar != $subchar) {
3323
			if ($subchar != '') {
3324
				$wp_smiliessearch .= ')(?=' . $spaces . '|$)';  // End previous "subpattern"
3325
				$wp_smiliessearch .= '|(?<=' . $spaces . '|^)'; // Begin another "subpattern"
3326
			}
3327
			$subchar = $firstchar;
3328
			$wp_smiliessearch .= preg_quote($firstchar, '/') . '(?:';
3329
		} else {
3330
			$wp_smiliessearch .= '|';
3331
		}
3332
		$wp_smiliessearch .= preg_quote($rest, '/');
3333
	}
3334
3335
	$wp_smiliessearch .= ')(?=' . $spaces . '|$)/m';
3336
3337
}
3338
3339
/**
3340
 * Merge user defined arguments into defaults array.
3341
 *
3342
 * This function is used throughout WordPress to allow for both string or array
3343
 * to be merged into another array.
3344
 *
3345
 * @since 2.2.0
3346
 *
3347
 * @param string|array $args     Value to merge with $defaults
3348
 * @param array        $defaults Optional. Array that serves as the defaults. Default empty.
3349
 * @return array Merged user defined values with defaults.
3350
 */
3351
function wp_parse_args( $args, $defaults = '' ) {
3352
	if ( is_object( $args ) )
3353
		$r = get_object_vars( $args );
3354
	elseif ( is_array( $args ) )
3355
		$r =& $args;
3356
	else
3357
		wp_parse_str( $args, $r );
3358
3359
	if ( is_array( $defaults ) )
3360
		return array_merge( $defaults, $r );
3361
	return $r;
3362
}
3363
3364
/**
3365
 * Clean up an array, comma- or space-separated list of IDs.
3366
 *
3367
 * @since 3.0.0
3368
 *
3369
 * @param array|string $list List of ids.
3370
 * @return array Sanitized array of IDs.
3371
 */
3372
function wp_parse_id_list( $list ) {
3373
	if ( !is_array($list) )
3374
		$list = preg_split('/[\s,]+/', $list);
3375
3376
	return array_unique(array_map('absint', $list));
3377
}
3378
3379
/**
3380
 * Extract a slice of an array, given a list of keys.
3381
 *
3382
 * @since 3.1.0
3383
 *
3384
 * @param array $array The original array.
3385
 * @param array $keys  The list of keys.
3386
 * @return array The array slice.
3387
 */
3388
function wp_array_slice_assoc( $array, $keys ) {
3389
	$slice = array();
3390
	foreach ( $keys as $key )
3391
		if ( isset( $array[ $key ] ) )
3392
			$slice[ $key ] = $array[ $key ];
3393
3394
	return $slice;
3395
}
3396
3397
/**
3398
 * Determines if the variable is a numeric-indexed array.
3399
 *
3400
 * @since 4.4.0
3401
 *
3402
 * @param mixed $data Variable to check.
3403
 * @return bool Whether the variable is a list.
3404
 */
3405
function wp_is_numeric_array( $data ) {
3406
	if ( ! is_array( $data ) ) {
3407
		return false;
3408
	}
3409
3410
	$keys = array_keys( $data );
3411
	$string_keys = array_filter( $keys, 'is_string' );
3412
	return count( $string_keys ) === 0;
3413
}
3414
3415
/**
3416
 * Filters a list of objects, based on a set of key => value arguments.
3417
 *
3418
 * @since 3.0.0
3419
 *
3420
 * @param array       $list     An array of objects to filter
3421
 * @param array       $args     Optional. An array of key => value arguments to match
3422
 *                              against each object. Default empty array.
3423
 * @param string      $operator Optional. The logical operation to perform. 'or' means
3424
 *                              only one element from the array needs to match; 'and'
3425
 *                              means all elements must match; 'not' means no elements may
3426
 *                              match. Default 'and'.
3427
 * @param bool|string $field    A field from the object to place instead of the entire object.
3428
 *                              Default false.
3429
 * @return array A list of objects or object fields.
3430
 */
3431
function wp_filter_object_list( $list, $args = array(), $operator = 'and', $field = false ) {
3432
	if ( ! is_array( $list ) )
3433
		return array();
3434
3435
	$list = wp_list_filter( $list, $args, $operator );
3436
3437
	if ( $field )
3438
		$list = wp_list_pluck( $list, $field );
0 ignored issues
show
It seems like $field defined by parameter $field on line 3431 can also be of type boolean; however, wp_list_pluck() does only seem to accept integer|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
3439
3440
	return $list;
3441
}
3442
3443
/**
3444
 * Filters a list of objects, based on a set of key => value arguments.
3445
 *
3446
 * @since 3.1.0
3447
 *
3448
 * @param array  $list     An array of objects to filter.
3449
 * @param array  $args     Optional. An array of key => value arguments to match
3450
 *                         against each object. Default empty array.
3451
 * @param string $operator Optional. The logical operation to perform. 'AND' means
3452
 *                         all elements from the array must match. 'OR' means only
3453
 *                         one element needs to match. 'NOT' means no elements may
3454
 *                         match. Default 'AND'.
3455
 * @return array Array of found values.
3456
 */
3457
function wp_list_filter( $list, $args = array(), $operator = 'AND' ) {
3458
	if ( ! is_array( $list ) )
3459
		return array();
3460
3461
	if ( empty( $args ) )
3462
		return $list;
3463
3464
	$operator = strtoupper( $operator );
3465
	$count = count( $args );
3466
	$filtered = array();
3467
3468
	foreach ( $list as $key => $obj ) {
3469
		$to_match = (array) $obj;
3470
3471
		$matched = 0;
3472
		foreach ( $args as $m_key => $m_value ) {
3473
			if ( array_key_exists( $m_key, $to_match ) && $m_value == $to_match[ $m_key ] )
3474
				$matched++;
3475
		}
3476
3477
		if ( ( 'AND' == $operator && $matched == $count )
3478
		  || ( 'OR' == $operator && $matched > 0 )
3479
		  || ( 'NOT' == $operator && 0 == $matched ) ) {
3480
			$filtered[$key] = $obj;
3481
		}
3482
	}
3483
3484
	return $filtered;
3485
}
3486
3487
/**
3488
 * Pluck a certain field out of each object in a list.
3489
 *
3490
 * This has the same functionality and prototype of
3491
 * array_column() (PHP 5.5) but also supports objects.
3492
 *
3493
 * @since 3.1.0
3494
 * @since 4.0.0 $index_key parameter added.
3495
 *
3496
 * @param array      $list      List of objects or arrays
3497
 * @param int|string $field     Field from the object to place instead of the entire object
3498
 * @param int|string $index_key Optional. Field from the object to use as keys for the new array.
3499
 *                              Default null.
3500
 * @return array Array of found values. If `$index_key` is set, an array of found values with keys
3501
 *               corresponding to `$index_key`. If `$index_key` is null, array keys from the original
3502
 *               `$list` will be preserved in the results.
3503
 */
3504
function wp_list_pluck( $list, $field, $index_key = null ) {
3505
	if ( ! $index_key ) {
3506
		/*
3507
		 * This is simple. Could at some point wrap array_column()
3508
		 * if we knew we had an array of arrays.
3509
		 */
3510
		foreach ( $list as $key => $value ) {
3511
			if ( is_object( $value ) ) {
3512
				$list[ $key ] = $value->$field;
3513
			} else {
3514
				$list[ $key ] = $value[ $field ];
3515
			}
3516
		}
3517
		return $list;
3518
	}
3519
3520
	/*
3521
	 * When index_key is not set for a particular item, push the value
3522
	 * to the end of the stack. This is how array_column() behaves.
3523
	 */
3524
	$newlist = array();
3525
	foreach ( $list as $value ) {
3526
		if ( is_object( $value ) ) {
3527
			if ( isset( $value->$index_key ) ) {
3528
				$newlist[ $value->$index_key ] = $value->$field;
3529
			} else {
3530
				$newlist[] = $value->$field;
3531
			}
3532
		} else {
3533
			if ( isset( $value[ $index_key ] ) ) {
3534
				$newlist[ $value[ $index_key ] ] = $value[ $field ];
3535
			} else {
3536
				$newlist[] = $value[ $field ];
3537
			}
3538
		}
3539
	}
3540
3541
	return $newlist;
3542
}
3543
3544
/**
3545
 * Determines if Widgets library should be loaded.
3546
 *
3547
 * Checks to make sure that the widgets library hasn't already been loaded.
3548
 * If it hasn't, then it will load the widgets library and run an action hook.
3549
 *
3550
 * @since 2.2.0
3551
 */
3552
function wp_maybe_load_widgets() {
3553
	/**
3554
	 * Filters whether to load the Widgets library.
3555
	 *
3556
	 * Passing a falsey value to the filter will effectively short-circuit
3557
	 * the Widgets library from loading.
3558
	 *
3559
	 * @since 2.8.0
3560
	 *
3561
	 * @param bool $wp_maybe_load_widgets Whether to load the Widgets library.
3562
	 *                                    Default true.
3563
	 */
3564
	if ( ! apply_filters( 'load_default_widgets', true ) ) {
3565
		return;
3566
	}
3567
3568
	require_once( ABSPATH . WPINC . '/default-widgets.php' );
3569
3570
	add_action( '_admin_menu', 'wp_widgets_add_menu' );
3571
}
3572
3573
/**
3574
 * Append the Widgets menu to the themes main menu.
3575
 *
3576
 * @since 2.2.0
3577
 *
3578
 * @global array $submenu
3579
 */
3580
function wp_widgets_add_menu() {
3581
	global $submenu;
3582
3583
	if ( ! current_theme_supports( 'widgets' ) )
3584
		return;
3585
3586
	$submenu['themes.php'][7] = array( __( 'Widgets' ), 'edit_theme_options', 'widgets.php' );
3587
	ksort( $submenu['themes.php'], SORT_NUMERIC );
3588
}
3589
3590
/**
3591
 * Flush all output buffers for PHP 5.2.
3592
 *
3593
 * Make sure all output buffers are flushed before our singletons are destroyed.
3594
 *
3595
 * @since 2.2.0
3596
 */
3597
function wp_ob_end_flush_all() {
3598
	$levels = ob_get_level();
3599
	for ($i=0; $i<$levels; $i++)
3600
		ob_end_flush();
3601
}
3602
3603
/**
3604
 * Load custom DB error or display WordPress DB error.
3605
 *
3606
 * If a file exists in the wp-content directory named db-error.php, then it will
3607
 * be loaded instead of displaying the WordPress DB error. If it is not found,
3608
 * then the WordPress DB error will be displayed instead.
3609
 *
3610
 * The WordPress DB error sets the HTTP status header to 500 to try to prevent
3611
 * search engines from caching the message. Custom DB messages should do the
3612
 * same.
3613
 *
3614
 * This function was backported to WordPress 2.3.2, but originally was added
3615
 * in WordPress 2.5.0.
3616
 *
3617
 * @since 2.3.2
3618
 *
3619
 * @global wpdb $wpdb WordPress database abstraction object.
3620
 */
3621
function dead_db() {
3622
	global $wpdb;
3623
3624
	wp_load_translations_early();
3625
3626
	// Load custom DB error template, if present.
3627
	if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
3628
		require_once( WP_CONTENT_DIR . '/db-error.php' );
3629
		die();
3630
	}
3631
3632
	// If installing or in the admin, provide the verbose message.
3633
	if ( wp_installing() || defined( 'WP_ADMIN' ) )
3634
		wp_die($wpdb->error);
3635
3636
	// Otherwise, be terse.
3637
	status_header( 500 );
3638
	nocache_headers();
3639
	header( 'Content-Type: text/html; charset=utf-8' );
3640
?>
3641
<!DOCTYPE html>
3642
<html xmlns="http://www.w3.org/1999/xhtml"<?php if ( is_rtl() ) echo ' dir="rtl"'; ?>>
3643
<head>
3644
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
3645
	<title><?php _e( 'Database Error' ); ?></title>
3646
3647
</head>
3648
<body>
3649
	<h1><?php _e( 'Error establishing a database connection' ); ?></h1>
3650
</body>
3651
</html>
3652
<?php
3653
	die();
3654
}
3655
3656
/**
3657
 * Convert a value to non-negative integer.
3658
 *
3659
 * @since 2.5.0
3660
 *
3661
 * @param mixed $maybeint Data you wish to have converted to a non-negative integer.
3662
 * @return int A non-negative integer.
3663
 */
3664
function absint( $maybeint ) {
3665
	return abs( intval( $maybeint ) );
3666
}
3667
3668
/**
3669
 * Mark a function as deprecated and inform when it has been used.
3670
 *
3671
 * There is a {@see 'hook deprecated_function_run'} that will be called that can be used
3672
 * to get the backtrace up to what file and function called the deprecated
3673
 * function.
3674
 *
3675
 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
3676
 *
3677
 * This function is to be used in every function that is deprecated.
3678
 *
3679
 * @since 2.5.0
3680
 * @access private
3681
 *
3682
 * @param string $function    The function that was called.
3683
 * @param string $version     The version of WordPress that deprecated the function.
3684
 * @param string $replacement Optional. The function that should have been called. Default null.
3685
 */
3686 View Code Duplication
function _deprecated_function( $function, $version, $replacement = null ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3687
3688
	/**
3689
	 * Fires when a deprecated function is called.
3690
	 *
3691
	 * @since 2.5.0
3692
	 *
3693
	 * @param string $function    The function that was called.
3694
	 * @param string $replacement The function that should have been called.
3695
	 * @param string $version     The version of WordPress that deprecated the function.
3696
	 */
3697
	do_action( 'deprecated_function_run', $function, $replacement, $version );
3698
3699
	/**
3700
	 * Filters whether to trigger an error for deprecated functions.
3701
	 *
3702
	 * @since 2.5.0
3703
	 *
3704
	 * @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
3705
	 */
3706
	if ( WP_DEBUG && apply_filters( 'deprecated_function_trigger_error', true ) ) {
3707
		if ( function_exists( '__' ) ) {
3708
			if ( ! is_null( $replacement ) )
3709
				trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.'), $function, $version, $replacement ) );
3710
			else
3711
				trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.'), $function, $version ) );
3712
		} else {
3713
			if ( ! is_null( $replacement ) )
3714
				trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.', $function, $version, $replacement ) );
3715
			else
3716
				trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.', $function, $version ) );
3717
		}
3718
	}
3719
}
3720
3721
/**
3722
 * Marks a constructor as deprecated and informs when it has been used.
3723
 *
3724
 * Similar to _deprecated_function(), but with different strings. Used to
3725
 * remove PHP4 style constructors.
3726
 *
3727
 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
3728
 *
3729
 * This function is to be used in every PHP4 style constructor method that is deprecated.
3730
 *
3731
 * @since 4.3.0
3732
 * @since 4.5.0 Added the `$parent_class` parameter.
3733
 *
3734
 * @access private
3735
 *
3736
 * @param string $class        The class containing the deprecated constructor.
3737
 * @param string $version      The version of WordPress that deprecated the function.
3738
 * @param string $parent_class Optional. The parent class calling the deprecated constructor.
3739
 *                             Default empty string.
3740
 */
3741
function _deprecated_constructor( $class, $version, $parent_class = '' ) {
3742
3743
	/**
3744
	 * Fires when a deprecated constructor is called.
3745
	 *
3746
	 * @since 4.3.0
3747
	 * @since 4.5.0 Added the `$parent_class` parameter.
3748
	 *
3749
	 * @param string $class        The class containing the deprecated constructor.
3750
	 * @param string $version      The version of WordPress that deprecated the function.
3751
	 * @param string $parent_class The parent class calling the deprecated constructor.
3752
	 */
3753
	do_action( 'deprecated_constructor_run', $class, $version, $parent_class );
3754
3755
	/**
3756
	 * Filters whether to trigger an error for deprecated functions.
3757
	 *
3758
	 * `WP_DEBUG` must be true in addition to the filter evaluating to true.
3759
	 *
3760
	 * @since 4.3.0
3761
	 *
3762
	 * @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
3763
	 */
3764
	if ( WP_DEBUG && apply_filters( 'deprecated_constructor_trigger_error', true ) ) {
3765
		if ( function_exists( '__' ) ) {
3766 View Code Duplication
			if ( ! empty( $parent_class ) ) {
3767
				/* translators: 1: PHP class name, 2: PHP parent class name, 3: version number, 4: __construct() method */
3768
				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.' ),
3769
					$class, $parent_class, $version, '<pre>__construct()</pre>' ) );
3770
			} else {
3771
				/* translators: 1: PHP class name, 2: version number, 3: __construct() method */
3772
				trigger_error( sprintf( __( 'The called constructor method for %1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ),
3773
					$class, $version, '<pre>__construct()</pre>' ) );
3774
			}
3775 View Code Duplication
		} else {
3776
			if ( ! empty( $parent_class ) ) {
3777
				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.',
3778
					$class, $parent_class, $version, '<pre>__construct()</pre>' ) );
3779
			} else {
3780
				trigger_error( sprintf( 'The called constructor method for %1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.',
3781
					$class, $version, '<pre>__construct()</pre>' ) );
3782
			}
3783
		}
3784
	}
3785
3786
}
3787
3788
/**
3789
 * Mark a file as deprecated and inform when it has been used.
3790
 *
3791
 * There is a hook {@see 'deprecated_file_included'} that will be called that can be used
3792
 * to get the backtrace up to what file and function included the deprecated
3793
 * file.
3794
 *
3795
 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
3796
 *
3797
 * This function is to be used in every file that is deprecated.
3798
 *
3799
 * @since 2.5.0
3800
 * @access private
3801
 *
3802
 * @param string $file        The file that was included.
3803
 * @param string $version     The version of WordPress that deprecated the file.
3804
 * @param string $replacement Optional. The file that should have been included based on ABSPATH.
3805
 *                            Default null.
3806
 * @param string $message     Optional. A message regarding the change. Default empty.
3807
 */
3808
function _deprecated_file( $file, $version, $replacement = null, $message = '' ) {
3809
3810
	/**
3811
	 * Fires when a deprecated file is called.
3812
	 *
3813
	 * @since 2.5.0
3814
	 *
3815
	 * @param string $file        The file that was called.
3816
	 * @param string $replacement The file that should have been included based on ABSPATH.
3817
	 * @param string $version     The version of WordPress that deprecated the file.
3818
	 * @param string $message     A message regarding the change.
3819
	 */
3820
	do_action( 'deprecated_file_included', $file, $replacement, $version, $message );
3821
3822
	/**
3823
	 * Filters whether to trigger an error for deprecated files.
3824
	 *
3825
	 * @since 2.5.0
3826
	 *
3827
	 * @param bool $trigger Whether to trigger the error for deprecated files. Default true.
3828
	 */
3829
	if ( WP_DEBUG && apply_filters( 'deprecated_file_trigger_error', true ) ) {
3830
		$message = empty( $message ) ? '' : ' ' . $message;
3831
		if ( function_exists( '__' ) ) {
3832
			if ( ! is_null( $replacement ) )
3833
				trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.'), $file, $version, $replacement ) . $message );
3834
			else
3835
				trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.'), $file, $version ) . $message );
3836
		} else {
3837
			if ( ! is_null( $replacement ) )
3838
				trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.', $file, $version, $replacement ) . $message );
3839
			else
3840
				trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.', $file, $version ) . $message );
3841
		}
3842
	}
3843
}
3844
/**
3845
 * Mark a function argument as deprecated and inform when it has been used.
3846
 *
3847
 * This function is to be used whenever a deprecated function argument is used.
3848
 * Before this function is called, the argument must be checked for whether it was
3849
 * used by comparing it to its default value or evaluating whether it is empty.
3850
 * For example:
3851
 *
3852
 *     if ( ! empty( $deprecated ) ) {
3853
 *         _deprecated_argument( __FUNCTION__, '3.0.0' );
3854
 *     }
3855
 *
3856
 *
3857
 * There is a hook deprecated_argument_run that will be called that can be used
3858
 * to get the backtrace up to what file and function used the deprecated
3859
 * argument.
3860
 *
3861
 * The current behavior is to trigger a user error if WP_DEBUG is true.
3862
 *
3863
 * @since 3.0.0
3864
 * @access private
3865
 *
3866
 * @param string $function The function that was called.
3867
 * @param string $version  The version of WordPress that deprecated the argument used.
3868
 * @param string $message  Optional. A message regarding the change. Default null.
3869
 */
3870 View Code Duplication
function _deprecated_argument( $function, $version, $message = null ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3871
3872
	/**
3873
	 * Fires when a deprecated argument is called.
3874
	 *
3875
	 * @since 3.0.0
3876
	 *
3877
	 * @param string $function The function that was called.
3878
	 * @param string $message  A message regarding the change.
3879
	 * @param string $version  The version of WordPress that deprecated the argument used.
3880
	 */
3881
	do_action( 'deprecated_argument_run', $function, $message, $version );
3882
3883
	/**
3884
	 * Filters whether to trigger an error for deprecated arguments.
3885
	 *
3886
	 * @since 3.0.0
3887
	 *
3888
	 * @param bool $trigger Whether to trigger the error for deprecated arguments. Default true.
3889
	 */
3890
	if ( WP_DEBUG && apply_filters( 'deprecated_argument_trigger_error', true ) ) {
3891
		if ( function_exists( '__' ) ) {
3892
			if ( ! is_null( $message ) )
3893
				trigger_error( sprintf( __('%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s'), $function, $version, $message ) );
3894
			else
3895
				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 ) );
3896
		} else {
3897
			if ( ! is_null( $message ) )
3898
				trigger_error( sprintf( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s', $function, $version, $message ) );
3899
			else
3900
				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 ) );
3901
		}
3902
	}
3903
}
3904
3905
/**
3906
 * Marks a deprecated action or filter hook as deprecated and throws a notice.
3907
 *
3908
 * Use the {@see 'deprecated_hook_run'} action to get the backtrace describing where
3909
 * the deprecated hook was called.
3910
 *
3911
 * Default behavior is to trigger a user error if `WP_DEBUG` is true.
3912
 *
3913
 * This function is called by the do_action_deprecated() and apply_filters_deprecated()
3914
 * functions, and so generally does not need to be called directly.
3915
 *
3916
 * @since 4.6.0
3917
 * @access private
3918
 *
3919
 * @param string $hook        The hook that was used.
3920
 * @param string $version     The version of WordPress that deprecated the hook.
3921
 * @param string $replacement Optional. The hook that should have been used.
3922
 * @param string $message     Optional. A message regarding the change.
3923
 */
3924
function _deprecated_hook( $hook, $version, $replacement = null, $message = null ) {
3925
	/**
3926
	 * Fires when a deprecated hook is called.
3927
	 *
3928
	 * @since 4.6.0
3929
	 *
3930
	 * @param string $hook        The hook that was called.
3931
	 * @param string $replacement The hook that should be used as a replacement.
3932
	 * @param string $version     The version of WordPress that deprecated the argument used.
3933
	 * @param string $message     A message regarding the change.
3934
	 */
3935
	do_action( 'deprecated_hook_run', $hook, $replacement, $version, $message );
3936
3937
	/**
3938
	 * Filters whether to trigger deprecated hook errors.
3939
	 *
3940
	 * @since 4.6.0
3941
	 *
3942
	 * @param bool $trigger Whether to trigger deprecated hook errors. Requires
3943
	 *                      `WP_DEBUG` to be defined true.
3944
	 */
3945
	if ( WP_DEBUG && apply_filters( 'deprecated_hook_trigger_error', true ) ) {
3946
		$message = empty( $message ) ? '' : ' ' . $message;
3947
		if ( ! is_null( $replacement ) ) {
3948
			trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ), $hook, $version, $replacement ) . $message );
3949
		} else {
3950
			trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.' ), $hook, $version ) . $message );
3951
		}
3952
	}
3953
}
3954
3955
/**
3956
 * Mark something as being incorrectly called.
3957
 *
3958
 * There is a hook {@see 'doing_it_wrong_run'} that will be called that can be used
3959
 * to get the backtrace up to what file and function called the deprecated
3960
 * function.
3961
 *
3962
 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
3963
 *
3964
 * @since 3.1.0
3965
 * @access private
3966
 *
3967
 * @param string $function The function that was called.
3968
 * @param string $message  A message explaining what has been done incorrectly.
3969
 * @param string $version  The version of WordPress where the message was added.
3970
 */
3971
function _doing_it_wrong( $function, $message, $version ) {
3972
3973
	/**
3974
	 * Fires when the given function is being used incorrectly.
3975
	 *
3976
	 * @since 3.1.0
3977
	 *
3978
	 * @param string $function The function that was called.
3979
	 * @param string $message  A message explaining what has been done incorrectly.
3980
	 * @param string $version  The version of WordPress where the message was added.
3981
	 */
3982
	do_action( 'doing_it_wrong_run', $function, $message, $version );
3983
3984
	/**
3985
	 * Filters whether to trigger an error for _doing_it_wrong() calls.
3986
	 *
3987
	 * @since 3.1.0
3988
	 *
3989
	 * @param bool $trigger Whether to trigger the error for _doing_it_wrong() calls. Default true.
3990
	 */
3991
	if ( WP_DEBUG && apply_filters( 'doing_it_wrong_trigger_error', true ) ) {
3992
		if ( function_exists( '__' ) ) {
3993
			$version = is_null( $version ) ? '' : sprintf( __( '(This message was added in version %s.)' ), $version );
3994
			/* translators: %s: Codex URL */
3995
			$message .= ' ' . sprintf( __( 'Please see <a href="%s">Debugging in WordPress</a> for more information.' ),
3996
				__( 'https://codex.wordpress.org/Debugging_in_WordPress' )
3997
			);
3998
			trigger_error( sprintf( __( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s' ), $function, $message, $version ) );
3999
		} else {
4000
			$version = is_null( $version ) ? '' : sprintf( '(This message was added in version %s.)', $version );
4001
			$message .= sprintf( ' Please see <a href="%s">Debugging in WordPress</a> for more information.',
4002
				'https://codex.wordpress.org/Debugging_in_WordPress'
4003
			);
4004
			trigger_error( sprintf( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s', $function, $message, $version ) );
4005
		}
4006
	}
4007
}
4008
4009
/**
4010
 * Is the server running earlier than 1.5.0 version of lighttpd?
4011
 *
4012
 * @since 2.5.0
4013
 *
4014
 * @return bool Whether the server is running lighttpd < 1.5.0.
4015
 */
4016
function is_lighttpd_before_150() {
4017
	$server_parts = explode( '/', isset( $_SERVER['SERVER_SOFTWARE'] )? $_SERVER['SERVER_SOFTWARE'] : '' );
4018
	$server_parts[1] = isset( $server_parts[1] )? $server_parts[1] : '';
4019
	return  'lighttpd' == $server_parts[0] && -1 == version_compare( $server_parts[1], '1.5.0' );
4020
}
4021
4022
/**
4023
 * Does the specified module exist in the Apache config?
4024
 *
4025
 * @since 2.5.0
4026
 *
4027
 * @global bool $is_apache
4028
 *
4029
 * @param string $mod     The module, e.g. mod_rewrite.
4030
 * @param bool   $default Optional. The default return value if the module is not found. Default false.
4031
 * @return bool Whether the specified module is loaded.
4032
 */
4033
function apache_mod_loaded($mod, $default = false) {
4034
	global $is_apache;
4035
4036
	if ( !$is_apache )
4037
		return false;
4038
4039
	if ( function_exists( 'apache_get_modules' ) ) {
4040
		$mods = apache_get_modules();
4041
		if ( in_array($mod, $mods) )
4042
			return true;
4043
	} elseif ( function_exists( 'phpinfo' ) && false === strpos( ini_get( 'disable_functions' ), 'phpinfo' ) ) {
4044
			ob_start();
4045
			phpinfo(8);
4046
			$phpinfo = ob_get_clean();
4047
			if ( false !== strpos($phpinfo, $mod) )
4048
				return true;
4049
	}
4050
	return $default;
4051
}
4052
4053
/**
4054
 * Check if IIS 7+ supports pretty permalinks.
4055
 *
4056
 * @since 2.8.0
4057
 *
4058
 * @global bool $is_iis7
4059
 *
4060
 * @return bool Whether IIS7 supports permalinks.
4061
 */
4062
function iis7_supports_permalinks() {
4063
	global $is_iis7;
4064
4065
	$supports_permalinks = false;
4066
	if ( $is_iis7 ) {
4067
		/* First we check if the DOMDocument class exists. If it does not exist, then we cannot
4068
		 * easily update the xml configuration file, hence we just bail out and tell user that
4069
		 * pretty permalinks cannot be used.
4070
		 *
4071
		 * Next we check if the URL Rewrite Module 1.1 is loaded and enabled for the web site. When
4072
		 * URL Rewrite 1.1 is loaded it always sets a server variable called 'IIS_UrlRewriteModule'.
4073
		 * Lastly we make sure that PHP is running via FastCGI. This is important because if it runs
4074
		 * via ISAPI then pretty permalinks will not work.
4075
		 */
4076
		$supports_permalinks = class_exists( 'DOMDocument', false ) && isset($_SERVER['IIS_UrlRewriteModule']) && ( PHP_SAPI == 'cgi-fcgi' );
4077
	}
4078
4079
	/**
4080
	 * Filters whether IIS 7+ supports pretty permalinks.
4081
	 *
4082
	 * @since 2.8.0
4083
	 *
4084
	 * @param bool $supports_permalinks Whether IIS7 supports permalinks. Default false.
4085
	 */
4086
	return apply_filters( 'iis7_supports_permalinks', $supports_permalinks );
4087
}
4088
4089
/**
4090
 * File validates against allowed set of defined rules.
4091
 *
4092
 * A return value of '1' means that the $file contains either '..' or './'. A
4093
 * return value of '2' means that the $file contains ':' after the first
4094
 * character. A return value of '3' means that the file is not in the allowed
4095
 * files list.
4096
 *
4097
 * @since 1.2.0
4098
 *
4099
 * @param string $file File path.
4100
 * @param array  $allowed_files List of allowed files.
4101
 * @return int 0 means nothing is wrong, greater than 0 means something was wrong.
4102
 */
4103
function validate_file( $file, $allowed_files = '' ) {
4104
	if ( false !== strpos( $file, '..' ) )
4105
		return 1;
4106
4107
	if ( false !== strpos( $file, './' ) )
4108
		return 1;
4109
4110
	if ( ! empty( $allowed_files ) && ! in_array( $file, $allowed_files ) )
4111
		return 3;
4112
4113
	if (':' == substr( $file, 1, 1 ) )
4114
		return 2;
4115
4116
	return 0;
4117
}
4118
4119
/**
4120
 * Whether to force SSL used for the Administration Screens.
4121
 *
4122
 * @since 2.6.0
4123
 *
4124
 * @staticvar bool $forced
4125
 *
4126
 * @param string|bool $force Optional. Whether to force SSL in admin screens. Default null.
4127
 * @return bool True if forced, false if not forced.
4128
 */
4129
function force_ssl_admin( $force = null ) {
4130
	static $forced = false;
4131
4132
	if ( !is_null( $force ) ) {
4133
		$old_forced = $forced;
4134
		$forced = $force;
4135
		return $old_forced;
4136
	}
4137
4138
	return $forced;
4139
}
4140
4141
/**
4142
 * Guess the URL for the site.
4143
 *
4144
 * Will remove wp-admin links to retrieve only return URLs not in the wp-admin
4145
 * directory.
4146
 *
4147
 * @since 2.6.0
4148
 *
4149
 * @return string The guessed URL.
4150
 */
4151
function wp_guess_url() {
4152
	if ( defined('WP_SITEURL') && '' != WP_SITEURL ) {
4153
		$url = WP_SITEURL;
4154
	} else {
4155
		$abspath_fix = str_replace( '\\', '/', ABSPATH );
4156
		$script_filename_dir = dirname( $_SERVER['SCRIPT_FILENAME'] );
4157
4158
		// The request is for the admin
4159
		if ( strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) !== false || strpos( $_SERVER['REQUEST_URI'], 'wp-login.php' ) !== false ) {
4160
			$path = preg_replace( '#/(wp-admin/.*|wp-login.php)#i', '', $_SERVER['REQUEST_URI'] );
4161
4162
		// The request is for a file in ABSPATH
4163
		} elseif ( $script_filename_dir . '/' == $abspath_fix ) {
4164
			// Strip off any file/query params in the path
4165
			$path = preg_replace( '#/[^/]*$#i', '', $_SERVER['PHP_SELF'] );
4166
4167
		} else {
4168
			if ( false !== strpos( $_SERVER['SCRIPT_FILENAME'], $abspath_fix ) ) {
4169
				// Request is hitting a file inside ABSPATH
4170
				$directory = str_replace( ABSPATH, '', $script_filename_dir );
4171
				// Strip off the sub directory, and any file/query params
4172
				$path = preg_replace( '#/' . preg_quote( $directory, '#' ) . '/[^/]*$#i', '' , $_SERVER['REQUEST_URI'] );
4173
			} elseif ( false !== strpos( $abspath_fix, $script_filename_dir ) ) {
4174
				// Request is hitting a file above ABSPATH
4175
				$subdirectory = substr( $abspath_fix, strpos( $abspath_fix, $script_filename_dir ) + strlen( $script_filename_dir ) );
4176
				// Strip off any file/query params from the path, appending the sub directory to the install
4177
				$path = preg_replace( '#/[^/]*$#i', '' , $_SERVER['REQUEST_URI'] ) . $subdirectory;
4178
			} else {
4179
				$path = $_SERVER['REQUEST_URI'];
4180
			}
4181
		}
4182
4183
		$schema = is_ssl() ? 'https://' : 'http://'; // set_url_scheme() is not defined yet
4184
		$url = $schema . $_SERVER['HTTP_HOST'] . $path;
4185
	}
4186
4187
	return rtrim($url, '/');
4188
}
4189
4190
/**
4191
 * Temporarily suspend cache additions.
4192
 *
4193
 * Stops more data being added to the cache, but still allows cache retrieval.
4194
 * This is useful for actions, such as imports, when a lot of data would otherwise
4195
 * be almost uselessly added to the cache.
4196
 *
4197
 * Suspension lasts for a single page load at most. Remember to call this
4198
 * function again if you wish to re-enable cache adds earlier.
4199
 *
4200
 * @since 3.3.0
4201
 *
4202
 * @staticvar bool $_suspend
4203
 *
4204
 * @param bool $suspend Optional. Suspends additions if true, re-enables them if false.
4205
 * @return bool The current suspend setting
4206
 */
4207
function wp_suspend_cache_addition( $suspend = null ) {
4208
	static $_suspend = false;
4209
4210
	if ( is_bool( $suspend ) )
4211
		$_suspend = $suspend;
4212
4213
	return $_suspend;
4214
}
4215
4216
/**
4217
 * Suspend cache invalidation.
4218
 *
4219
 * Turns cache invalidation on and off. Useful during imports where you don't wont to do
4220
 * invalidations every time a post is inserted. Callers must be sure that what they are
4221
 * doing won't lead to an inconsistent cache when invalidation is suspended.
4222
 *
4223
 * @since 2.7.0
4224
 *
4225
 * @global bool $_wp_suspend_cache_invalidation
4226
 *
4227
 * @param bool $suspend Optional. Whether to suspend or enable cache invalidation. Default true.
4228
 * @return bool The current suspend setting.
4229
 */
4230
function wp_suspend_cache_invalidation( $suspend = true ) {
4231
	global $_wp_suspend_cache_invalidation;
4232
4233
	$current_suspend = $_wp_suspend_cache_invalidation;
4234
	$_wp_suspend_cache_invalidation = $suspend;
4235
	return $current_suspend;
4236
}
4237
4238
/**
4239
 * Determine whether a site is the main site of the current network.
4240
 *
4241
 * @since 3.0.0
4242
 *
4243
 * @global object $current_site
4244
 *
4245
 * @param int $site_id Optional. Site ID to test. Defaults to current site.
4246
 * @return bool True if $site_id is the main site of the network, or if not
4247
 *              running Multisite.
4248
 */
4249
function is_main_site( $site_id = null ) {
4250
	// This is the current network's information; 'site' is old terminology.
4251
	global $current_site;
4252
4253
	if ( ! is_multisite() )
4254
		return true;
4255
4256
	if ( ! $site_id )
0 ignored issues
show
Bug Best Practice introduced by
The expression $site_id of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
4257
		$site_id = get_current_blog_id();
4258
4259
	return (int) $site_id === (int) $current_site->blog_id;
4260
}
4261
4262
/**
4263
 * Determine whether a network is the main network of the Multisite install.
4264
 *
4265
 * @since 3.7.0
4266
 *
4267
 * @param int $network_id Optional. Network ID to test. Defaults to current network.
4268
 * @return bool True if $network_id is the main network, or if not running Multisite.
4269
 */
4270
function is_main_network( $network_id = null ) {
4271
	if ( ! is_multisite() ) {
4272
		return true;
4273
	}
4274
4275
	$current_network_id = (int) get_current_site()->id;
4276
4277
	if ( null === $network_id ) {
4278
		$network_id = $current_network_id;
4279
	}
4280
4281
	$network_id = (int) $network_id;
4282
4283
	return ( $network_id === get_main_network_id() );
4284
}
4285
4286
/**
4287
 * Get the main network ID.
4288
 *
4289
 * @since 4.3.0
4290
 *
4291
 * @global wpdb $wpdb WordPress database abstraction object.
4292
 *
4293
 * @return int The ID of the main network.
4294
 */
4295
function get_main_network_id() {
4296
	global $wpdb;
4297
4298
	if ( ! is_multisite() ) {
4299
		return 1;
4300
	}
4301
4302
	$current_site = get_current_site();
4303
4304
	if ( defined( 'PRIMARY_NETWORK_ID' ) ) {
4305
		$main_network_id = PRIMARY_NETWORK_ID;
4306
	} elseif ( isset( $current_site->id ) && 1 === (int) $current_site->id ) {
4307
		// If the current network has an ID of 1, assume it is the main network.
4308
		$main_network_id = 1;
4309 View Code Duplication
	} else {
4310
		$main_network_id = wp_cache_get( 'primary_network_id', 'site-options' );
4311
4312
		if ( false === $main_network_id ) {
4313
			$main_network_id = (int) $wpdb->get_var( "SELECT id FROM {$wpdb->site} ORDER BY id LIMIT 1" );
4314
			wp_cache_add( 'primary_network_id', $main_network_id, 'site-options' );
4315
		}
4316
	}
4317
4318
	/**
4319
	 * Filters the main network ID.
4320
	 *
4321
	 * @since 4.3.0
4322
	 *
4323
	 * @param int $main_network_id The ID of the main network.
4324
	 */
4325
	return (int) apply_filters( 'get_main_network_id', $main_network_id );
4326
}
4327
4328
/**
4329
 * Determine whether global terms are enabled.
4330
 *
4331
 * @since 3.0.0
4332
 *
4333
 * @staticvar bool $global_terms
4334
 *
4335
 * @return bool True if multisite and global terms enabled.
4336
 */
4337
function global_terms_enabled() {
4338
	if ( ! is_multisite() )
4339
		return false;
4340
4341
	static $global_terms = null;
4342
	if ( is_null( $global_terms ) ) {
4343
4344
		/**
4345
		 * Filters whether global terms are enabled.
4346
		 *
4347
		 * Passing a non-null value to the filter will effectively short-circuit the function,
4348
		 * returning the value of the 'global_terms_enabled' site option instead.
4349
		 *
4350
		 * @since 3.0.0
4351
		 *
4352
		 * @param null $enabled Whether global terms are enabled.
4353
		 */
4354
		$filter = apply_filters( 'global_terms_enabled', null );
4355
		if ( ! is_null( $filter ) )
4356
			$global_terms = (bool) $filter;
4357
		else
4358
			$global_terms = (bool) get_site_option( 'global_terms_enabled', false );
4359
	}
4360
	return $global_terms;
4361
}
4362
4363
/**
4364
 * gmt_offset modification for smart timezone handling.
4365
 *
4366
 * Overrides the gmt_offset option if we have a timezone_string available.
4367
 *
4368
 * @since 2.8.0
4369
 *
4370
 * @return float|false Timezone GMT offset, false otherwise.
4371
 */
4372
function wp_timezone_override_offset() {
4373
	if ( !$timezone_string = get_option( 'timezone_string' ) ) {
4374
		return false;
4375
	}
4376
4377
	$timezone_object = timezone_open( $timezone_string );
4378
	$datetime_object = date_create();
4379
	if ( false === $timezone_object || false === $datetime_object ) {
4380
		return false;
4381
	}
4382
	return round( timezone_offset_get( $timezone_object, $datetime_object ) / HOUR_IN_SECONDS, 2 );
4383
}
4384
4385
/**
4386
 * Sort-helper for timezones.
4387
 *
4388
 * @since 2.9.0
4389
 * @access private
4390
 *
4391
 * @param array $a
4392
 * @param array $b
4393
 * @return int
4394
 */
4395
function _wp_timezone_choice_usort_callback( $a, $b ) {
4396
	// Don't use translated versions of Etc
4397
	if ( 'Etc' === $a['continent'] && 'Etc' === $b['continent'] ) {
4398
		// Make the order of these more like the old dropdown
4399
		if ( 'GMT+' === substr( $a['city'], 0, 4 ) && 'GMT+' === substr( $b['city'], 0, 4 ) ) {
4400
			return -1 * ( strnatcasecmp( $a['city'], $b['city'] ) );
4401
		}
4402 View Code Duplication
		if ( 'UTC' === $a['city'] ) {
4403
			if ( 'GMT+' === substr( $b['city'], 0, 4 ) ) {
4404
				return 1;
4405
			}
4406
			return -1;
4407
		}
4408 View Code Duplication
		if ( 'UTC' === $b['city'] ) {
4409
			if ( 'GMT+' === substr( $a['city'], 0, 4 ) ) {
4410
				return -1;
4411
			}
4412
			return 1;
4413
		}
4414
		return strnatcasecmp( $a['city'], $b['city'] );
4415
	}
4416
	if ( $a['t_continent'] == $b['t_continent'] ) {
4417
		if ( $a['t_city'] == $b['t_city'] ) {
4418
			return strnatcasecmp( $a['t_subcity'], $b['t_subcity'] );
4419
		}
4420
		return strnatcasecmp( $a['t_city'], $b['t_city'] );
4421
	} else {
4422
		// Force Etc to the bottom of the list
4423
		if ( 'Etc' === $a['continent'] ) {
4424
			return 1;
4425
		}
4426
		if ( 'Etc' === $b['continent'] ) {
4427
			return -1;
4428
		}
4429
		return strnatcasecmp( $a['t_continent'], $b['t_continent'] );
4430
	}
4431
}
4432
4433
/**
4434
 * Gives a nicely-formatted list of timezone strings.
4435
 *
4436
 * @since 2.9.0
4437
 *
4438
 * @staticvar bool $mo_loaded
4439
 *
4440
 * @param string $selected_zone Selected timezone.
4441
 * @return string
4442
 */
4443
function wp_timezone_choice( $selected_zone ) {
4444
	static $mo_loaded = false;
4445
4446
	$continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific');
4447
4448
	// Load translations for continents and cities
4449
	if ( !$mo_loaded ) {
4450
		$locale = get_locale();
4451
		$mofile = WP_LANG_DIR . '/continents-cities-' . $locale . '.mo';
4452
		load_textdomain( 'continents-cities', $mofile );
4453
		$mo_loaded = true;
4454
	}
4455
4456
	$zonen = array();
4457
	foreach ( timezone_identifiers_list() as $zone ) {
4458
		$zone = explode( '/', $zone );
4459
		if ( !in_array( $zone[0], $continents ) ) {
4460
			continue;
4461
		}
4462
4463
		// This determines what gets set and translated - we don't translate Etc/* strings here, they are done later
4464
		$exists = array(
4465
			0 => ( isset( $zone[0] ) && $zone[0] ),
4466
			1 => ( isset( $zone[1] ) && $zone[1] ),
4467
			2 => ( isset( $zone[2] ) && $zone[2] ),
4468
		);
4469
		$exists[3] = ( $exists[0] && 'Etc' !== $zone[0] );
4470
		$exists[4] = ( $exists[1] && $exists[3] );
4471
		$exists[5] = ( $exists[2] && $exists[3] );
4472
4473
		$zonen[] = array(
4474
			'continent'   => ( $exists[0] ? $zone[0] : '' ),
4475
			'city'        => ( $exists[1] ? $zone[1] : '' ),
4476
			'subcity'     => ( $exists[2] ? $zone[2] : '' ),
4477
			't_continent' => ( $exists[3] ? translate( str_replace( '_', ' ', $zone[0] ), 'continents-cities' ) : '' ),
4478
			't_city'      => ( $exists[4] ? translate( str_replace( '_', ' ', $zone[1] ), 'continents-cities' ) : '' ),
4479
			't_subcity'   => ( $exists[5] ? translate( str_replace( '_', ' ', $zone[2] ), 'continents-cities' ) : '' )
4480
		);
4481
	}
4482
	usort( $zonen, '_wp_timezone_choice_usort_callback' );
4483
4484
	$structure = array();
4485
4486
	if ( empty( $selected_zone ) ) {
4487
		$structure[] = '<option selected="selected" value="">' . __( 'Select a city' ) . '</option>';
4488
	}
4489
4490
	foreach ( $zonen as $key => $zone ) {
4491
		// Build value in an array to join later
4492
		$value = array( $zone['continent'] );
4493
4494
		if ( empty( $zone['city'] ) ) {
4495
			// It's at the continent level (generally won't happen)
4496
			$display = $zone['t_continent'];
4497
		} else {
4498
			// It's inside a continent group
4499
4500
			// Continent optgroup
4501
			if ( !isset( $zonen[$key - 1] ) || $zonen[$key - 1]['continent'] !== $zone['continent'] ) {
4502
				$label = $zone['t_continent'];
4503
				$structure[] = '<optgroup label="'. esc_attr( $label ) .'">';
4504
			}
4505
4506
			// Add the city to the value
4507
			$value[] = $zone['city'];
4508
4509
			$display = $zone['t_city'];
4510
			if ( !empty( $zone['subcity'] ) ) {
4511
				// Add the subcity to the value
4512
				$value[] = $zone['subcity'];
4513
				$display .= ' - ' . $zone['t_subcity'];
4514
			}
4515
		}
4516
4517
		// Build the value
4518
		$value = join( '/', $value );
4519
		$selected = '';
4520
		if ( $value === $selected_zone ) {
4521
			$selected = 'selected="selected" ';
4522
		}
4523
		$structure[] = '<option ' . $selected . 'value="' . esc_attr( $value ) . '">' . esc_html( $display ) . "</option>";
4524
4525
		// Close continent optgroup
4526
		if ( !empty( $zone['city'] ) && ( !isset($zonen[$key + 1]) || (isset( $zonen[$key + 1] ) && $zonen[$key + 1]['continent'] !== $zone['continent']) ) ) {
4527
			$structure[] = '</optgroup>';
4528
		}
4529
	}
4530
4531
	// Do UTC
4532
	$structure[] = '<optgroup label="'. esc_attr__( 'UTC' ) .'">';
4533
	$selected = '';
4534
	if ( 'UTC' === $selected_zone )
4535
		$selected = 'selected="selected" ';
4536
	$structure[] = '<option ' . $selected . 'value="' . esc_attr( 'UTC' ) . '">' . __('UTC') . '</option>';
4537
	$structure[] = '</optgroup>';
4538
4539
	// Do manual UTC offsets
4540
	$structure[] = '<optgroup label="'. esc_attr__( 'Manual Offsets' ) .'">';
4541
	$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,
4542
		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);
4543
	foreach ( $offset_range as $offset ) {
4544
		if ( 0 <= $offset )
4545
			$offset_name = '+' . $offset;
4546
		else
4547
			$offset_name = (string) $offset;
4548
4549
		$offset_value = $offset_name;
4550
		$offset_name = str_replace(array('.25','.5','.75'), array(':15',':30',':45'), $offset_name);
4551
		$offset_name = 'UTC' . $offset_name;
4552
		$offset_value = 'UTC' . $offset_value;
4553
		$selected = '';
4554
		if ( $offset_value === $selected_zone )
4555
			$selected = 'selected="selected" ';
4556
		$structure[] = '<option ' . $selected . 'value="' . esc_attr( $offset_value ) . '">' . esc_html( $offset_name ) . "</option>";
4557
4558
	}
4559
	$structure[] = '</optgroup>';
4560
4561
	return join( "\n", $structure );
4562
}
4563
4564
/**
4565
 * Strip close comment and close php tags from file headers used by WP.
4566
 *
4567
 * @since 2.8.0
4568
 * @access private
4569
 *
4570
 * @see https://core.trac.wordpress.org/ticket/8497
4571
 *
4572
 * @param string $str Header comment to clean up.
4573
 * @return string
4574
 */
4575
function _cleanup_header_comment( $str ) {
4576
	return trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $str));
4577
}
4578
4579
/**
4580
 * Permanently delete comments or posts of any type that have held a status
4581
 * of 'trash' for the number of days defined in EMPTY_TRASH_DAYS.
4582
 *
4583
 * The default value of `EMPTY_TRASH_DAYS` is 30 (days).
4584
 *
4585
 * @since 2.9.0
4586
 *
4587
 * @global wpdb $wpdb WordPress database abstraction object.
4588
 */
4589
function wp_scheduled_delete() {
4590
	global $wpdb;
4591
4592
	$delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
4593
4594
	$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);
4595
4596
	foreach ( (array) $posts_to_delete as $post ) {
4597
		$post_id = (int) $post['post_id'];
4598
		if ( !$post_id )
4599
			continue;
4600
4601
		$del_post = get_post($post_id);
4602
4603
		if ( !$del_post || 'trash' != $del_post->post_status ) {
4604
			delete_post_meta($post_id, '_wp_trash_meta_status');
4605
			delete_post_meta($post_id, '_wp_trash_meta_time');
4606
		} else {
4607
			wp_delete_post($post_id);
4608
		}
4609
	}
4610
4611
	$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);
4612
4613
	foreach ( (array) $comments_to_delete as $comment ) {
4614
		$comment_id = (int) $comment['comment_id'];
4615
		if ( !$comment_id )
4616
			continue;
4617
4618
		$del_comment = get_comment($comment_id);
4619
4620
		if ( !$del_comment || 'trash' != $del_comment->comment_approved ) {
4621
			delete_comment_meta($comment_id, '_wp_trash_meta_time');
4622
			delete_comment_meta($comment_id, '_wp_trash_meta_status');
4623
		} else {
4624
			wp_delete_comment( $del_comment );
0 ignored issues
show
It seems like $del_comment defined by get_comment($comment_id) on line 4618 can also be of type array; however, wp_delete_comment() does only seem to accept integer|object<WP_Comment>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
4625
		}
4626
	}
4627
}
4628
4629
/**
4630
 * Retrieve metadata from a file.
4631
 *
4632
 * Searches for metadata in the first 8kiB of a file, such as a plugin or theme.
4633
 * Each piece of metadata must be on its own line. Fields can not span multiple
4634
 * lines, the value will get cut at the end of the first line.
4635
 *
4636
 * If the file data is not within that first 8kiB, then the author should correct
4637
 * their plugin file and move the data headers to the top.
4638
 *
4639
 * @link https://codex.wordpress.org/File_Header
4640
 *
4641
 * @since 2.9.0
4642
 *
4643
 * @param string $file            Path to the file.
4644
 * @param array  $default_headers List of headers, in the format array('HeaderKey' => 'Header Name').
4645
 * @param string $context         Optional. If specified adds filter hook {@see 'extra_$context_headers'}.
4646
 *                                Default empty.
4647
 * @return array Array of file headers in `HeaderKey => Header Value` format.
4648
 */
4649
function get_file_data( $file, $default_headers, $context = '' ) {
4650
	// We don't need to write to the file, so just open for reading.
4651
	$fp = fopen( $file, 'r' );
4652
4653
	// Pull only the first 8kiB of the file in.
4654
	$file_data = fread( $fp, 8192 );
4655
4656
	// PHP will close file handle, but we are good citizens.
4657
	fclose( $fp );
4658
4659
	// Make sure we catch CR-only line endings.
4660
	$file_data = str_replace( "\r", "\n", $file_data );
4661
4662
	/**
4663
	 * Filters extra file headers by context.
4664
	 *
4665
	 * The dynamic portion of the hook name, `$context`, refers to
4666
	 * the context where extra headers might be loaded.
4667
	 *
4668
	 * @since 2.9.0
4669
	 *
4670
	 * @param array $extra_context_headers Empty array by default.
4671
	 */
4672
	if ( $context && $extra_headers = apply_filters( "extra_{$context}_headers", array() ) ) {
4673
		$extra_headers = array_combine( $extra_headers, $extra_headers ); // keys equal values
4674
		$all_headers = array_merge( $extra_headers, (array) $default_headers );
4675
	} else {
4676
		$all_headers = $default_headers;
4677
	}
4678
4679
	foreach ( $all_headers as $field => $regex ) {
4680
		if ( preg_match( '/^[ \t\/*#@]*' . preg_quote( $regex, '/' ) . ':(.*)$/mi', $file_data, $match ) && $match[1] )
4681
			$all_headers[ $field ] = _cleanup_header_comment( $match[1] );
4682
		else
4683
			$all_headers[ $field ] = '';
4684
	}
4685
4686
	return $all_headers;
4687
}
4688
4689
/**
4690
 * Returns true.
4691
 *
4692
 * Useful for returning true to filters easily.
4693
 *
4694
 * @since 3.0.0
4695
 *
4696
 * @see __return_false()
4697
 *
4698
 * @return true True.
4699
 */
4700
function __return_true() {
4701
	return true;
4702
}
4703
4704
/**
4705
 * Returns false.
4706
 *
4707
 * Useful for returning false to filters easily.
4708
 *
4709
 * @since 3.0.0
4710
 *
4711
 * @see __return_true()
4712
 *
4713
 * @return false False.
4714
 */
4715
function __return_false() {
4716
	return false;
4717
}
4718
4719
/**
4720
 * Returns 0.
4721
 *
4722
 * Useful for returning 0 to filters easily.
4723
 *
4724
 * @since 3.0.0
4725
 *
4726
 * @return int 0.
4727
 */
4728
function __return_zero() {
4729
	return 0;
4730
}
4731
4732
/**
4733
 * Returns an empty array.
4734
 *
4735
 * Useful for returning an empty array to filters easily.
4736
 *
4737
 * @since 3.0.0
4738
 *
4739
 * @return array Empty array.
4740
 */
4741
function __return_empty_array() {
4742
	return array();
4743
}
4744
4745
/**
4746
 * Returns null.
4747
 *
4748
 * Useful for returning null to filters easily.
4749
 *
4750
 * @since 3.4.0
4751
 *
4752
 * @return null Null value.
4753
 */
4754
function __return_null() {
4755
	return null;
4756
}
4757
4758
/**
4759
 * Returns an empty string.
4760
 *
4761
 * Useful for returning an empty string to filters easily.
4762
 *
4763
 * @since 3.7.0
4764
 *
4765
 * @see __return_null()
4766
 *
4767
 * @return string Empty string.
4768
 */
4769
function __return_empty_string() {
4770
	return '';
4771
}
4772
4773
/**
4774
 * Send a HTTP header to disable content type sniffing in browsers which support it.
4775
 *
4776
 * @since 3.0.0
4777
 *
4778
 * @see https://blogs.msdn.com/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx
4779
 * @see https://src.chromium.org/viewvc/chrome?view=rev&revision=6985
4780
 */
4781
function send_nosniff_header() {
4782
	@header( 'X-Content-Type-Options: nosniff' );
4783
}
4784
4785
/**
4786
 * Return a MySQL expression for selecting the week number based on the start_of_week option.
4787
 *
4788
 * @ignore
4789
 * @since 3.0.0
4790
 *
4791
 * @param string $column Database column.
4792
 * @return string SQL clause.
4793
 */
4794
function _wp_mysql_week( $column ) {
4795
	switch ( $start_of_week = (int) get_option( 'start_of_week' ) ) {
4796
	case 1 :
4797
		return "WEEK( $column, 1 )";
4798
	case 2 :
4799
	case 3 :
4800
	case 4 :
4801
	case 5 :
4802
	case 6 :
4803
		return "WEEK( DATE_SUB( $column, INTERVAL $start_of_week DAY ), 0 )";
4804
	case 0 :
4805
	default :
4806
		return "WEEK( $column, 0 )";
4807
	}
4808
}
4809
4810
/**
4811
 * Find hierarchy loops using a callback function that maps object IDs to parent IDs.
4812
 *
4813
 * @since 3.1.0
4814
 * @access private
4815
 *
4816
 * @param callable $callback      Function that accepts ( ID, $callback_args ) and outputs parent_ID.
4817
 * @param int      $start         The ID to start the loop check at.
4818
 * @param int      $start_parent  The parent_ID of $start to use instead of calling $callback( $start ).
4819
 *                                Use null to always use $callback
4820
 * @param array    $callback_args Optional. Additional arguments to send to $callback.
4821
 * @return array IDs of all members of loop.
4822
 */
4823
function wp_find_hierarchy_loop( $callback, $start, $start_parent, $callback_args = array() ) {
4824
	$override = is_null( $start_parent ) ? array() : array( $start => $start_parent );
4825
4826
	if ( !$arbitrary_loop_member = wp_find_hierarchy_loop_tortoise_hare( $callback, $start, $override, $callback_args ) )
4827
		return array();
4828
4829
	return wp_find_hierarchy_loop_tortoise_hare( $callback, $arbitrary_loop_member, $override, $callback_args, true );
4830
}
4831
4832
/**
4833
 * Use the "The Tortoise and the Hare" algorithm to detect loops.
4834
 *
4835
 * For every step of the algorithm, the hare takes two steps and the tortoise one.
4836
 * If the hare ever laps the tortoise, there must be a loop.
4837
 *
4838
 * @since 3.1.0
4839
 * @access private
4840
 *
4841
 * @param callable $callback      Function that accepts ( ID, callback_arg, ... ) and outputs parent_ID.
4842
 * @param int      $start         The ID to start the loop check at.
4843
 * @param array    $override      Optional. An array of ( ID => parent_ID, ... ) to use instead of $callback.
4844
 *                                Default empty array.
4845
 * @param array    $callback_args Optional. Additional arguments to send to $callback. Default empty array.
4846
 * @param bool     $_return_loop  Optional. Return loop members or just detect presence of loop? Only set
4847
 *                                to true if you already know the given $start is part of a loop (otherwise
4848
 *                                the returned array might include branches). Default false.
4849
 * @return mixed Scalar ID of some arbitrary member of the loop, or array of IDs of all members of loop if
4850
 *               $_return_loop
4851
 */
4852
function wp_find_hierarchy_loop_tortoise_hare( $callback, $start, $override = array(), $callback_args = array(), $_return_loop = false ) {
4853
	$tortoise = $hare = $evanescent_hare = $start;
4854
	$return = array();
4855
4856
	// Set evanescent_hare to one past hare
4857
	// Increment hare two steps
4858
	while (
4859
		$tortoise
4860
	&&
4861
		( $evanescent_hare = isset( $override[$hare] ) ? $override[$hare] : call_user_func_array( $callback, array_merge( array( $hare ), $callback_args ) ) )
4862
	&&
4863
		( $hare = isset( $override[$evanescent_hare] ) ? $override[$evanescent_hare] : call_user_func_array( $callback, array_merge( array( $evanescent_hare ), $callback_args ) ) )
4864
	) {
4865
		if ( $_return_loop )
4866
			$return[$tortoise] = $return[$evanescent_hare] = $return[$hare] = true;
4867
4868
		// tortoise got lapped - must be a loop
4869
		if ( $tortoise == $evanescent_hare || $tortoise == $hare )
4870
			return $_return_loop ? $return : $tortoise;
4871
4872
		// Increment tortoise by one step
4873
		$tortoise = isset( $override[$tortoise] ) ? $override[$tortoise] : call_user_func_array( $callback, array_merge( array( $tortoise ), $callback_args ) );
4874
	}
4875
4876
	return false;
4877
}
4878
4879
/**
4880
 * Send a HTTP header to limit rendering of pages to same origin iframes.
4881
 *
4882
 * @since 3.1.3
4883
 *
4884
 * @see https://developer.mozilla.org/en/the_x-frame-options_response_header
4885
 */
4886
function send_frame_options_header() {
4887
	@header( 'X-Frame-Options: SAMEORIGIN' );
4888
}
4889
4890
/**
4891
 * Retrieve a list of protocols to allow in HTML attributes.
4892
 *
4893
 * @since 3.3.0
4894
 * @since 4.3.0 Added 'webcal' to the protocols array.
4895
 *
4896
 * @see wp_kses()
4897
 * @see esc_url()
4898
 *
4899
 * @staticvar array $protocols
4900
 *
4901
 * @return array Array of allowed protocols. Defaults to an array containing 'http', 'https',
4902
 *               'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet',
4903
 *               'mms', 'rtsp', 'svn', 'tel', 'fax', 'xmpp', and 'webcal'.
4904
 */
4905
function wp_allowed_protocols() {
4906
	static $protocols = array();
4907
4908
	if ( empty( $protocols ) ) {
4909
		$protocols = array( 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn', 'tel', 'fax', 'xmpp', 'webcal' );
4910
4911
		/**
4912
		 * Filters the list of protocols allowed in HTML attributes.
4913
		 *
4914
		 * @since 3.0.0
4915
		 *
4916
		 * @param array $protocols Array of allowed protocols e.g. 'http', 'ftp', 'tel', and more.
4917
		 */
4918
		$protocols = apply_filters( 'kses_allowed_protocols', $protocols );
4919
	}
4920
4921
	return $protocols;
4922
}
4923
4924
/**
4925
 * Return a comma-separated string of functions that have been called to get
4926
 * to the current point in code.
4927
 *
4928
 * @since 3.4.0
4929
 *
4930
 * @see https://core.trac.wordpress.org/ticket/19589
4931
 *
4932
 * @param string $ignore_class Optional. A class to ignore all function calls within - useful
4933
 *                             when you want to just give info about the callee. Default null.
4934
 * @param int    $skip_frames  Optional. A number of stack frames to skip - useful for unwinding
4935
 *                             back to the source of the issue. Default 0.
4936
 * @param bool   $pretty       Optional. Whether or not you want a comma separated string or raw
4937
 *                             array returned. Default true.
4938
 * @return string|array Either a string containing a reversed comma separated trace or an array
4939
 *                      of individual calls.
4940
 */
4941
function wp_debug_backtrace_summary( $ignore_class = null, $skip_frames = 0, $pretty = true ) {
4942
	if ( version_compare( PHP_VERSION, '5.2.5', '>=' ) )
4943
		$trace = debug_backtrace( false );
4944
	else
4945
		$trace = debug_backtrace();
4946
4947
	$caller = array();
4948
	$check_class = ! is_null( $ignore_class );
4949
	$skip_frames++; // skip this function
4950
4951
	foreach ( $trace as $call ) {
4952
		if ( $skip_frames > 0 ) {
4953
			$skip_frames--;
4954
		} elseif ( isset( $call['class'] ) ) {
4955
			if ( $check_class && $ignore_class == $call['class'] )
4956
				continue; // Filter out calls
4957
4958
			$caller[] = "{$call['class']}{$call['type']}{$call['function']}";
4959
		} else {
4960
			if ( in_array( $call['function'], array( 'do_action', 'apply_filters' ) ) ) {
4961
				$caller[] = "{$call['function']}('{$call['args'][0]}')";
4962
			} elseif ( in_array( $call['function'], array( 'include', 'include_once', 'require', 'require_once' ) ) ) {
4963
				$caller[] = $call['function'] . "('" . str_replace( array( WP_CONTENT_DIR, ABSPATH ) , '', $call['args'][0] ) . "')";
4964
			} else {
4965
				$caller[] = $call['function'];
4966
			}
4967
		}
4968
	}
4969
	if ( $pretty )
4970
		return join( ', ', array_reverse( $caller ) );
4971
	else
4972
		return $caller;
4973
}
4974
4975
/**
4976
 * Retrieve ids that are not already present in the cache.
4977
 *
4978
 * @since 3.4.0
4979
 * @access private
4980
 *
4981
 * @param array  $object_ids ID list.
4982
 * @param string $cache_key  The cache bucket to check against.
4983
 *
4984
 * @return array List of ids not present in the cache.
4985
 */
4986
function _get_non_cached_ids( $object_ids, $cache_key ) {
4987
	$clean = array();
4988
	foreach ( $object_ids as $id ) {
4989
		$id = (int) $id;
4990
		if ( !wp_cache_get( $id, $cache_key ) ) {
4991
			$clean[] = $id;
4992
		}
4993
	}
4994
4995
	return $clean;
4996
}
4997
4998
/**
4999
 * Test if the current device has the capability to upload files.
5000
 *
5001
 * @since 3.4.0
5002
 * @access private
5003
 *
5004
 * @return bool Whether the device is able to upload files.
5005
 */
5006
function _device_can_upload() {
5007
	if ( ! wp_is_mobile() )
5008
		return true;
5009
5010
	$ua = $_SERVER['HTTP_USER_AGENT'];
5011
5012
	if ( strpos($ua, 'iPhone') !== false
5013
		|| strpos($ua, 'iPad') !== false
5014
		|| strpos($ua, 'iPod') !== false ) {
5015
			return preg_match( '#OS ([\d_]+) like Mac OS X#', $ua, $version ) && version_compare( $version[1], '6', '>=' );
5016
	}
5017
5018
	return true;
5019
}
5020
5021
/**
5022
 * Test if a given path is a stream URL
5023
 *
5024
 * @param string $path The resource path or URL.
5025
 * @return bool True if the path is a stream URL.
5026
 */
5027
function wp_is_stream( $path ) {
5028
	$wrappers = stream_get_wrappers();
5029
	$wrappers_re = '(' . join('|', $wrappers) . ')';
5030
5031
	return preg_match( "!^$wrappers_re://!", $path ) === 1;
5032
}
5033
5034
/**
5035
 * Test if the supplied date is valid for the Gregorian calendar.
5036
 *
5037
 * @since 3.5.0
5038
 *
5039
 * @see checkdate()
5040
 *
5041
 * @param  int    $month       Month number.
5042
 * @param  int    $day         Day number.
5043
 * @param  int    $year        Year number.
5044
 * @param  string $source_date The date to filter.
5045
 * @return bool True if valid date, false if not valid date.
5046
 */
5047
function wp_checkdate( $month, $day, $year, $source_date ) {
5048
	/**
5049
	 * Filters whether the given date is valid for the Gregorian calendar.
5050
	 *
5051
	 * @since 3.5.0
5052
	 *
5053
	 * @param bool   $checkdate   Whether the given date is valid.
5054
	 * @param string $source_date Date to check.
5055
	 */
5056
	return apply_filters( 'wp_checkdate', checkdate( $month, $day, $year ), $source_date );
5057
}
5058
5059
/**
5060
 * Load the auth check for monitoring whether the user is still logged in.
5061
 *
5062
 * Can be disabled with remove_action( 'admin_enqueue_scripts', 'wp_auth_check_load' );
5063
 *
5064
 * This is disabled for certain screens where a login screen could cause an
5065
 * inconvenient interruption. A filter called {@see 'wp_auth_check_load'} can be used
5066
 * for fine-grained control.
5067
 *
5068
 * @since 3.6.0
5069
 */
5070
function wp_auth_check_load() {
5071
	if ( ! is_admin() && ! is_user_logged_in() )
5072
		return;
5073
5074
	if ( defined( 'IFRAME_REQUEST' ) )
5075
		return;
5076
5077
	$screen = get_current_screen();
5078
	$hidden = array( 'update', 'update-network', 'update-core', 'update-core-network', 'upgrade', 'upgrade-network', 'network' );
5079
	$show = ! in_array( $screen->id, $hidden );
5080
5081
	/**
5082
	 * Filters whether to load the authentication check.
5083
	 *
5084
	 * Passing a falsey value to the filter will effectively short-circuit
5085
	 * loading the authentication check.
5086
	 *
5087
	 * @since 3.6.0
5088
	 *
5089
	 * @param bool      $show   Whether to load the authentication check.
5090
	 * @param WP_Screen $screen The current screen object.
5091
	 */
5092
	if ( apply_filters( 'wp_auth_check_load', $show, $screen ) ) {
5093
		wp_enqueue_style( 'wp-auth-check' );
5094
		wp_enqueue_script( 'wp-auth-check' );
5095
5096
		add_action( 'admin_print_footer_scripts', 'wp_auth_check_html', 5 );
5097
		add_action( 'wp_print_footer_scripts', 'wp_auth_check_html', 5 );
5098
	}
5099
}
5100
5101
/**
5102
 * Output the HTML that shows the wp-login dialog when the user is no longer logged in.
5103
 *
5104
 * @since 3.6.0
5105
 */
5106
function wp_auth_check_html() {
5107
	$login_url = wp_login_url();
5108
	$current_domain = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'];
5109
	$same_domain = ( strpos( $login_url, $current_domain ) === 0 );
5110
5111
	/**
5112
	 * Filters whether the authentication check originated at the same domain.
5113
	 *
5114
	 * @since 3.6.0
5115
	 *
5116
	 * @param bool $same_domain Whether the authentication check originated at the same domain.
5117
	 */
5118
	$same_domain = apply_filters( 'wp_auth_check_same_domain', $same_domain );
5119
	$wrap_class = $same_domain ? 'hidden' : 'hidden fallback';
5120
5121
	?>
5122
	<div id="wp-auth-check-wrap" class="<?php echo $wrap_class; ?>">
5123
	<div id="wp-auth-check-bg"></div>
5124
	<div id="wp-auth-check">
5125
	<button type="button" class="wp-auth-check-close button-link"><span class="screen-reader-text"><?php _e( 'Close dialog' ); ?></span></button>
5126
	<?php
5127
5128
	if ( $same_domain ) {
5129
		?>
5130
		<div id="wp-auth-check-form" class="loading" data-src="<?php echo esc_url( add_query_arg( array( 'interim-login' => 1 ), $login_url ) ); ?>"></div>
5131
		<?php
5132
	}
5133
5134
	?>
5135
	<div class="wp-auth-fallback">
5136
		<p><b class="wp-auth-fallback-expired" tabindex="0"><?php _e('Session expired'); ?></b></p>
5137
		<p><a href="<?php echo esc_url( $login_url ); ?>" target="_blank"><?php _e('Please log in again.'); ?></a>
5138
		<?php _e('The login page will open in a new window. After logging in you can close it and return to this page.'); ?></p>
5139
	</div>
5140
	</div>
5141
	</div>
5142
	<?php
5143
}
5144
5145
/**
5146
 * Check whether a user is still logged in, for the heartbeat.
5147
 *
5148
 * Send a result that shows a log-in box if the user is no longer logged in,
5149
 * or if their cookie is within the grace period.
5150
 *
5151
 * @since 3.6.0
5152
 *
5153
 * @global int $login_grace_period
5154
 *
5155
 * @param array $response  The Heartbeat response.
5156
 * @return array $response The Heartbeat response with 'wp-auth-check' value set.
5157
 */
5158
function wp_auth_check( $response ) {
5159
	$response['wp-auth-check'] = is_user_logged_in() && empty( $GLOBALS['login_grace_period'] );
5160
	return $response;
5161
}
5162
5163
/**
5164
 * Return RegEx body to liberally match an opening HTML tag.
5165
 *
5166
 * Matches an opening HTML tag that:
5167
 * 1. Is self-closing or
5168
 * 2. Has no body but has a closing tag of the same name or
5169
 * 3. Contains a body and a closing tag of the same name
5170
 *
5171
 * Note: this RegEx does not balance inner tags and does not attempt
5172
 * to produce valid HTML
5173
 *
5174
 * @since 3.6.0
5175
 *
5176
 * @param string $tag An HTML tag name. Example: 'video'.
5177
 * @return string Tag RegEx.
5178
 */
5179
function get_tag_regex( $tag ) {
5180
	if ( empty( $tag ) )
5181
		return;
5182
	return sprintf( '<%1$s[^<]*(?:>[\s\S]*<\/%1$s>|\s*\/>)', tag_escape( $tag ) );
5183
}
5184
5185
/**
5186
 * Retrieve a canonical form of the provided charset appropriate for passing to PHP
5187
 * functions such as htmlspecialchars() and charset html attributes.
5188
 *
5189
 * @since 3.6.0
5190
 * @access private
5191
 *
5192
 * @see https://core.trac.wordpress.org/ticket/23688
5193
 *
5194
 * @param string $charset A charset name.
5195
 * @return string The canonical form of the charset.
5196
 */
5197
function _canonical_charset( $charset ) {
5198
	if ( 'UTF-8' === $charset || 'utf-8' === $charset || 'utf8' === $charset ||
5199
		'UTF8' === $charset )
5200
		return 'UTF-8';
5201
5202
	if ( 'ISO-8859-1' === $charset || 'iso-8859-1' === $charset ||
5203
		'iso8859-1' === $charset || 'ISO8859-1' === $charset )
5204
		return 'ISO-8859-1';
5205
5206
	return $charset;
5207
}
5208
5209
/**
5210
 * Set the mbstring internal encoding to a binary safe encoding when func_overload
5211
 * is enabled.
5212
 *
5213
 * When mbstring.func_overload is in use for multi-byte encodings, the results from
5214
 * strlen() and similar functions respect the utf8 characters, causing binary data
5215
 * to return incorrect lengths.
5216
 *
5217
 * This function overrides the mbstring encoding to a binary-safe encoding, and
5218
 * resets it to the users expected encoding afterwards through the
5219
 * `reset_mbstring_encoding` function.
5220
 *
5221
 * It is safe to recursively call this function, however each
5222
 * `mbstring_binary_safe_encoding()` call must be followed up with an equal number
5223
 * of `reset_mbstring_encoding()` calls.
5224
 *
5225
 * @since 3.7.0
5226
 *
5227
 * @see reset_mbstring_encoding()
5228
 *
5229
 * @staticvar array $encodings
5230
 * @staticvar bool  $overloaded
5231
 *
5232
 * @param bool $reset Optional. Whether to reset the encoding back to a previously-set encoding.
5233
 *                    Default false.
5234
 */
5235
function mbstring_binary_safe_encoding( $reset = false ) {
5236
	static $encodings = array();
5237
	static $overloaded = null;
5238
5239
	if ( is_null( $overloaded ) )
5240
		$overloaded = function_exists( 'mb_internal_encoding' ) && ( ini_get( 'mbstring.func_overload' ) & 2 );
5241
5242
	if ( false === $overloaded )
5243
		return;
5244
5245
	if ( ! $reset ) {
5246
		$encoding = mb_internal_encoding();
5247
		array_push( $encodings, $encoding );
5248
		mb_internal_encoding( 'ISO-8859-1' );
5249
	}
5250
5251
	if ( $reset && $encodings ) {
5252
		$encoding = array_pop( $encodings );
5253
		mb_internal_encoding( $encoding );
5254
	}
5255
}
5256
5257
/**
5258
 * Reset the mbstring internal encoding to a users previously set encoding.
5259
 *
5260
 * @see mbstring_binary_safe_encoding()
5261
 *
5262
 * @since 3.7.0
5263
 */
5264
function reset_mbstring_encoding() {
5265
	mbstring_binary_safe_encoding( true );
5266
}
5267
5268
/**
5269
 * Filter/validate a variable as a boolean.
5270
 *
5271
 * Alternative to `filter_var( $var, FILTER_VALIDATE_BOOLEAN )`.
5272
 *
5273
 * @since 4.0.0
5274
 *
5275
 * @param mixed $var Boolean value to validate.
5276
 * @return bool Whether the value is validated.
5277
 */
5278
function wp_validate_boolean( $var ) {
5279
	if ( is_bool( $var ) ) {
5280
		return $var;
5281
	}
5282
5283
	if ( is_string( $var ) && 'false' === strtolower( $var ) ) {
5284
		return false;
5285
	}
5286
5287
	return (bool) $var;
5288
}
5289
5290
/**
5291
 * Delete a file
5292
 *
5293
 * @since 4.2.0
5294
 *
5295
 * @param string $file The path to the file to delete.
5296
 */
5297
function wp_delete_file( $file ) {
5298
	/**
5299
	 * Filters the path of the file to delete.
5300
	 *
5301
	 * @since 2.1.0
5302
	 *
5303
	 * @param string $medium Path to the file to delete.
5304
	 */
5305
	$delete = apply_filters( 'wp_delete_file', $file );
5306
	if ( ! empty( $delete ) ) {
5307
		@unlink( $delete );
5308
	}
5309
}
5310
5311
/**
5312
 * Outputs a small JS snippet on preview tabs/windows to remove `window.name` on unload.
5313
 *
5314
 * This prevents reusing the same tab for a preview when the user has navigated away.
5315
 *
5316
 * @since 4.3.0
5317
 */
5318
function wp_post_preview_js() {
5319
	global $post;
5320
5321
	if ( ! is_preview() || empty( $post ) ) {
5322
		return;
5323
	}
5324
5325
	// Has to match the window name used in post_submit_meta_box()
5326
	$name = 'wp-preview-' . (int) $post->ID;
5327
5328
	?>
5329
	<script>
5330
	( function() {
5331
		var query = document.location.search;
5332
5333
		if ( query && query.indexOf( 'preview=true' ) !== -1 ) {
5334
			window.name = '<?php echo $name; ?>';
5335
		}
5336
5337
		if ( window.addEventListener ) {
5338
			window.addEventListener( 'unload', function() { window.name = ''; }, false );
5339
		}
5340
	}());
5341
	</script>
5342
	<?php
5343
}
5344
5345
/**
5346
 * Parses and formats a MySQL datetime (Y-m-d H:i:s) for ISO8601/RFC3339.
5347
 *
5348
 * Explicitly strips timezones, as datetimes are not saved with any timezone
5349
 * information. Including any information on the offset could be misleading.
5350
 *
5351
 * @since 4.4.0
5352
 *
5353
 * @param string $date_string Date string to parse and format.
5354
 * @return string Date formatted for ISO8601/RFC3339.
5355
 */
5356
function mysql_to_rfc3339( $date_string ) {
5357
	$formatted = mysql2date( 'c', $date_string, false );
5358
5359
	// Strip timezone information
5360
	return preg_replace( '/(?:Z|[+-]\d{2}(?::\d{2})?)$/', '', $formatted );
5361
}
5362
5363
/**
5364
 * Attempts to raise the PHP memory limit for memory intensive processes.
5365
 *
5366
 * Only allows raising the existing limit and prevents lowering it.
5367
 *
5368
 * @since 4.6.0
5369
 *
5370
 * @param string $context Optional. Context in which the function is called. Accepts either 'admin',
5371
 *                        'image', or an arbitrary other context. If an arbitrary context is passed,
5372
 *                        the similarly arbitrary {@see '{$context}_memory_limit'} filter will be
5373
 *                        invoked. Default 'admin'.
5374
 * @return bool|int|string The limit that was set or false on failure.
5375
 */
5376
function wp_raise_memory_limit( $context = 'admin' ) {
5377
	// Exit early if the limit cannot be changed.
5378
	if ( false === wp_is_ini_value_changeable( 'memory_limit' ) ) {
5379
		return false;
5380
	}
5381
5382
	$current_limit     = @ini_get( 'memory_limit' );
5383
	$current_limit_int = wp_convert_hr_to_bytes( $current_limit );
5384
5385
	if ( -1 === $current_limit_int ) {
5386
		return false;
5387
	}
5388
5389
	$wp_max_limit     = WP_MAX_MEMORY_LIMIT;
5390
	$wp_max_limit_int = wp_convert_hr_to_bytes( $wp_max_limit );
5391
	$filtered_limit   = $wp_max_limit;
5392
5393
	switch ( $context ) {
5394
		case 'admin':
5395
			/**
5396
			 * Filters the maximum memory limit available for administration screens.
5397
			 *
5398
			 * This only applies to administrators, who may require more memory for tasks
5399
			 * like updates. Memory limits when processing images (uploaded or edited by
5400
			 * users of any role) are handled separately.
5401
			 *
5402
			 * The `WP_MAX_MEMORY_LIMIT` constant specifically defines the maximum memory
5403
			 * limit available when in the administration back end. The default is 256M
5404
			 * (256 megabytes of memory) or the original `memory_limit` php.ini value if
5405
			 * this is higher.
5406
			 *
5407
			 * @since 3.0.0
5408
			 * @since 4.6.0 The default now takes the original `memory_limit` into account.
5409
			 *
5410
			 * @param int|string $filtered_limit The maximum WordPress memory limit. Accepts an integer
5411
			 *                                   (bytes), or a shorthand string notation, such as '256M'.
5412
			 */
5413
			$filtered_limit = apply_filters( 'admin_memory_limit', $filtered_limit );
5414
			break;
5415
5416
		case 'image':
5417
			/**
5418
			 * Filters the memory limit allocated for image manipulation.
5419
			 *
5420
			 * @since 3.5.0
5421
			 * @since 4.6.0 The default now takes the original `memory_limit` into account.
5422
			 *
5423
			 * @param int|string $filtered_limit Maximum memory limit to allocate for images.
5424
			 *                                   Default `WP_MAX_MEMORY_LIMIT` or the original
5425
			 *                                   php.ini `memory_limit`, whichever is higher.
5426
			 *                                   Accepts an integer (bytes), or a shorthand string
5427
			 *                                   notation, such as '256M'.
5428
			 */
5429
			$filtered_limit = apply_filters( 'image_memory_limit', $filtered_limit );
5430
			break;
5431
5432
		default:
5433
			/**
5434
			 * Filters the memory limit allocated for arbitrary contexts.
5435
			 *
5436
			 * The dynamic portion of the hook name, `$context`, refers to an arbitrary
5437
			 * context passed on calling the function. This allows for plugins to define
5438
			 * their own contexts for raising the memory limit.
5439
			 *
5440
			 * @since 4.6.0
5441
			 *
5442
			 * @param int|string $filtered_limit Maximum memory limit to allocate for images.
5443
			 *                                   Default '256M' or the original php.ini `memory_limit`,
5444
			 *                                   whichever is higher. Accepts an integer (bytes), or a
5445
			 *                                   shorthand string notation, such as '256M'.
5446
			 */
5447
			$filtered_limit = apply_filters( "{$context}_memory_limit", $filtered_limit );
5448
			break;
5449
	}
5450
5451
	$filtered_limit_int = wp_convert_hr_to_bytes( $filtered_limit );
5452
5453
	if ( -1 === $filtered_limit_int || ( $filtered_limit_int > $wp_max_limit_int && $filtered_limit_int > $current_limit_int ) ) {
5454
		if ( false !== @ini_set( 'memory_limit', $filtered_limit ) ) {
5455
			return $filtered_limit;
5456
		} else {
5457
			return false;
5458
		}
5459
	} elseif ( -1 === $wp_max_limit_int || $wp_max_limit_int > $current_limit_int ) {
5460
		if ( false !== @ini_set( 'memory_limit', $wp_max_limit ) ) {
5461
			return $wp_max_limit;
5462
		} else {
5463
			return false;
5464
		}
5465
	}
5466
5467
	return false;
5468
}
5469