Completed
Push — master ( fdb3a7...cde0c6 )
by Stephen
20:18
created

functions.php ➔ wp_raise_memory_limit()   C

Complexity

Conditions 12
Paths 17

Size

Total Lines 93
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 12
eloc 32
c 2
b 1
f 0
nc 17
nop 1
dl 0
loc 93
rs 5.034

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
Duplication introduced by
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
Bug introduced by
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 );
0 ignored issues
show
Bug introduced by
It seems like $response defined by wp_safe_remote_head($url) on line 651 can also be of type object<WP_Error>; however, wp_remote_retrieve_headers() does only seem to accept array, 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...
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
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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 );
0 ignored issues
show
Bug introduced by
It seems like $response defined by wp_safe_remote_get($uri, $options) on line 942 can also be of type object<WP_Error>; however, wp_remote_retrieve_body() does only seem to accept array, 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...
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 );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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' );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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}");
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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...
Documentation introduced by
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
Bug introduced by
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...
Documentation introduced by
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 );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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
	$info = pathinfo($filename);
2038
	$ext = !empty($info['extension']) ? '.' . $info['extension'] : '';
2039
	$name = basename($filename, $ext);
2040
2041
	// Edge case: if file is named '.ext', treat as an empty name.
2042
	if ( $name === $ext )
2043
		$name = '';
2044
2045
	/*
2046
	 * Increment the file number until we have a unique file to save in $dir.
2047
	 * Use callback if supplied.
2048
	 */
2049
	if ( $unique_filename_callback && is_callable( $unique_filename_callback ) ) {
2050
		$filename = call_user_func( $unique_filename_callback, $dir, $name, $ext );
2051
	} else {
2052
		$number = '';
2053
2054
		// Change '.ext' to lower case.
2055
		if ( $ext && strtolower($ext) != $ext ) {
2056
			$ext2 = strtolower($ext);
2057
			$filename2 = preg_replace( '|' . preg_quote($ext) . '$|', $ext2, $filename );
2058
2059
			// Check for both lower and upper case extension or image sub-sizes may be overwritten.
2060
			while ( file_exists($dir . "/$filename") || file_exists($dir . "/$filename2") ) {
2061
				$new_number = $number + 1;
2062
				$filename = str_replace( array( "-$number$ext", "$number$ext" ), "-$new_number$ext", $filename );
2063
				$filename2 = str_replace( array( "-$number$ext2", "$number$ext2" ), "-$new_number$ext2", $filename2 );
2064
				$number = $new_number;
2065
			}
2066
2067
			/**
2068
			 * Filters the result when generating a unique file name.
2069
			 *
2070
			 * @since 4.5.0
2071
			 *
2072
			 * @param string        $filename                 Unique file name.
2073
			 * @param string        $ext                      File extension, eg. ".png".
2074
			 * @param string        $dir                      Directory path.
2075
			 * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
2076
			 */
2077
			return apply_filters( 'wp_unique_filename', $filename2, $ext, $dir, $unique_filename_callback );
2078
		}
2079
2080
		while ( file_exists( $dir . "/$filename" ) ) {
2081
			if ( '' == "$number$ext" ) {
2082
				$filename = "$filename-" . ++$number;
2083
			} else {
2084
				$filename = str_replace( array( "-$number$ext", "$number$ext" ), "-" . ++$number . $ext, $filename );
2085
			}
2086
		}
2087
	}
2088
2089
	/** This filter is documented in wp-includes/functions.php */
2090
	return apply_filters( 'wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback );
2091
}
2092
2093
/**
2094
 * Create a file in the upload folder with given content.
2095
 *
2096
 * If there is an error, then the key 'error' will exist with the error message.
2097
 * If success, then the key 'file' will have the unique file path, the 'url' key
2098
 * will have the link to the new file. and the 'error' key will be set to false.
2099
 *
2100
 * This function will not move an uploaded file to the upload folder. It will
2101
 * create a new file with the content in $bits parameter. If you move the upload
2102
 * file, read the content of the uploaded file, and then you can give the
2103
 * filename and content to this function, which will add it to the upload
2104
 * folder.
2105
 *
2106
 * The permissions will be set on the new file automatically by this function.
2107
 *
2108
 * @since 2.0.0
2109
 *
2110
 * @param string       $name       Filename.
2111
 * @param null|string  $deprecated Never used. Set to null.
2112
 * @param mixed        $bits       File content
2113
 * @param string       $time       Optional. Time formatted in 'yyyy/mm'. Default null.
2114
 * @return array
2115
 */
2116
function wp_upload_bits( $name, $deprecated, $bits, $time = null ) {
2117
	if ( !empty( $deprecated ) )
2118
		_deprecated_argument( __FUNCTION__, '2.0.0' );
2119
2120
	if ( empty( $name ) )
2121
		return array( 'error' => __( 'Empty filename' ) );
2122
2123
	$wp_filetype = wp_check_filetype( $name );
2124
	if ( ! $wp_filetype['ext'] && ! current_user_can( 'unfiltered_upload' ) )
2125
		return array( 'error' => __( 'Invalid file type' ) );
2126
2127
	$upload = wp_upload_dir( $time );
2128
2129
	if ( $upload['error'] !== false )
2130
		return $upload;
2131
2132
	/**
2133
	 * Filters whether to treat the upload bits as an error.
2134
	 *
2135
	 * Passing a non-array to the filter will effectively short-circuit preparing
2136
	 * the upload bits, returning that value instead.
2137
	 *
2138
	 * @since 3.0.0
2139
	 *
2140
	 * @param mixed $upload_bits_error An array of upload bits data, or a non-array error to return.
2141
	 */
2142
	$upload_bits_error = apply_filters( 'wp_upload_bits', array( 'name' => $name, 'bits' => $bits, 'time' => $time ) );
2143
	if ( !is_array( $upload_bits_error ) ) {
2144
		$upload[ 'error' ] = $upload_bits_error;
2145
		return $upload;
2146
	}
2147
2148
	$filename = wp_unique_filename( $upload['path'], $name );
2149
2150
	$new_file = $upload['path'] . "/$filename";
2151 View Code Duplication
	if ( ! wp_mkdir_p( dirname( $new_file ) ) ) {
2152
		if ( 0 === strpos( $upload['basedir'], ABSPATH ) )
2153
			$error_path = str_replace( ABSPATH, '', $upload['basedir'] ) . $upload['subdir'];
2154
		else
2155
			$error_path = basename( $upload['basedir'] ) . $upload['subdir'];
2156
2157
		$message = sprintf( __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), $error_path );
2158
		return array( 'error' => $message );
2159
	}
2160
2161
	$ifp = @ fopen( $new_file, 'wb' );
2162
	if ( ! $ifp )
2163
		return array( 'error' => sprintf( __( 'Could not write file %s' ), $new_file ) );
2164
2165
	@fwrite( $ifp, $bits );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2166
	fclose( $ifp );
2167
	clearstatcache();
2168
2169
	// Set correct file permissions
2170
	$stat = @ stat( dirname( $new_file ) );
2171
	$perms = $stat['mode'] & 0007777;
2172
	$perms = $perms & 0000666;
2173
	@ chmod( $new_file, $perms );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2174
	clearstatcache();
2175
2176
	// Compute the URL
2177
	$url = $upload['url'] . "/$filename";
2178
2179
	/** This filter is documented in wp-admin/includes/file.php */
2180
	return apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $wp_filetype['type'], 'error' => false ), 'sideload' );
2181
}
2182
2183
/**
2184
 * Retrieve the file type based on the extension name.
2185
 *
2186
 * @since 2.5.0
2187
 *
2188
 * @param string $ext The extension to search.
2189
 * @return string|void The file type, example: audio, video, document, spreadsheet, etc.
2190
 */
2191
function wp_ext2type( $ext ) {
2192
	$ext = strtolower( $ext );
2193
2194
	$ext2type = wp_get_ext_types();
2195
	foreach ( $ext2type as $type => $exts )
2196
		if ( in_array( $ext, $exts ) )
2197
			return $type;
2198
}
2199
2200
/**
2201
 * Retrieve the file type from the file name.
2202
 *
2203
 * You can optionally define the mime array, if needed.
2204
 *
2205
 * @since 2.0.4
2206
 *
2207
 * @param string $filename File name or path.
2208
 * @param array  $mimes    Optional. Key is the file extension with value as the mime type.
2209
 * @return array Values with extension first and mime type.
2210
 */
2211
function wp_check_filetype( $filename, $mimes = null ) {
2212
	if ( empty($mimes) )
2213
		$mimes = get_allowed_mime_types();
2214
	$type = false;
2215
	$ext = false;
2216
2217
	foreach ( $mimes as $ext_preg => $mime_match ) {
2218
		$ext_preg = '!\.(' . $ext_preg . ')$!i';
2219
		if ( preg_match( $ext_preg, $filename, $ext_matches ) ) {
2220
			$type = $mime_match;
2221
			$ext = $ext_matches[1];
2222
			break;
2223
		}
2224
	}
2225
2226
	return compact( 'ext', 'type' );
2227
}
2228
2229
/**
2230
 * Attempt to determine the real file type of a file.
2231
 *
2232
 * If unable to, the file name extension will be used to determine type.
2233
 *
2234
 * If it's determined that the extension does not match the file's real type,
2235
 * then the "proper_filename" value will be set with a proper filename and extension.
2236
 *
2237
 * Currently this function only supports validating images known to getimagesize().
2238
 *
2239
 * @since 3.0.0
2240
 *
2241
 * @param string $file     Full path to the file.
2242
 * @param string $filename The name of the file (may differ from $file due to $file being
2243
 *                         in a tmp directory).
2244
 * @param array   $mimes   Optional. Key is the file extension with value as the mime type.
2245
 * @return array Values for the extension, MIME, and either a corrected filename or false
2246
 *               if original $filename is valid.
2247
 */
2248
function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) {
2249
	$proper_filename = false;
2250
2251
	// Do basic extension validation and MIME mapping
2252
	$wp_filetype = wp_check_filetype( $filename, $mimes );
2253
	$ext = $wp_filetype['ext'];
2254
	$type = $wp_filetype['type'];
2255
2256
	// We can't do any further validation without a file to work with
2257
	if ( ! file_exists( $file ) ) {
2258
		return compact( 'ext', 'type', 'proper_filename' );
2259
	}
2260
2261
	// We're able to validate images using GD
2262
	if ( $type && 0 === strpos( $type, 'image/' ) && function_exists('getimagesize') ) {
2263
2264
		// Attempt to figure out what type of image it actually is
2265
		$imgstats = @getimagesize( $file );
2266
2267
		// If getimagesize() knows what kind of image it really is and if the real MIME doesn't match the claimed MIME
2268
		if ( !empty($imgstats['mime']) && $imgstats['mime'] != $type ) {
2269
			/**
2270
			 * Filters the list mapping image mime types to their respective extensions.
2271
			 *
2272
			 * @since 3.0.0
2273
			 *
2274
			 * @param  array $mime_to_ext Array of image mime types and their matching extensions.
2275
			 */
2276
			$mime_to_ext = apply_filters( 'getimagesize_mimes_to_exts', array(
2277
				'image/jpeg' => 'jpg',
2278
				'image/png'  => 'png',
2279
				'image/gif'  => 'gif',
2280
				'image/bmp'  => 'bmp',
2281
				'image/tiff' => 'tif',
2282
			) );
2283
2284
			// Replace whatever is after the last period in the filename with the correct extension
2285
			if ( ! empty( $mime_to_ext[ $imgstats['mime'] ] ) ) {
2286
				$filename_parts = explode( '.', $filename );
2287
				array_pop( $filename_parts );
2288
				$filename_parts[] = $mime_to_ext[ $imgstats['mime'] ];
2289
				$new_filename = implode( '.', $filename_parts );
2290
2291
				if ( $new_filename != $filename ) {
2292
					$proper_filename = $new_filename; // Mark that it changed
2293
				}
2294
				// Redefine the extension / MIME
2295
				$wp_filetype = wp_check_filetype( $new_filename, $mimes );
2296
				$ext = $wp_filetype['ext'];
2297
				$type = $wp_filetype['type'];
2298
			}
2299
		}
2300
	}
2301
2302
	/**
2303
	 * Filters the "real" file type of the given file.
2304
	 *
2305
	 * @since 3.0.0
2306
	 *
2307
	 * @param array  $wp_check_filetype_and_ext File data array containing 'ext', 'type', and
2308
	 *                                          'proper_filename' keys.
2309
	 * @param string $file                      Full path to the file.
2310
	 * @param string $filename                  The name of the file (may differ from $file due to
2311
	 *                                          $file being in a tmp directory).
2312
	 * @param array  $mimes                     Key is the file extension with value as the mime type.
2313
	 */
2314
	return apply_filters( 'wp_check_filetype_and_ext', compact( 'ext', 'type', 'proper_filename' ), $file, $filename, $mimes );
2315
}
2316
2317
/**
2318
 * Retrieve list of mime types and file extensions.
2319
 *
2320
 * @since 3.5.0
2321
 * @since 4.2.0 Support was added for GIMP (xcf) files.
2322
 *
2323
 * @return array Array of mime types keyed by the file extension regex corresponding to those types.
2324
 */
2325
function wp_get_mime_types() {
2326
	/**
2327
	 * Filters the list of mime types and file extensions.
2328
	 *
2329
	 * This filter should be used to add, not remove, mime types. To remove
2330
	 * mime types, use the {@see 'upload_mimes'} filter.
2331
	 *
2332
	 * @since 3.5.0
2333
	 *
2334
	 * @param array $wp_get_mime_types Mime types keyed by the file extension regex
2335
	 *                                 corresponding to those types.
2336
	 */
2337
	return apply_filters( 'mime_types', array(
2338
	// Image formats.
2339
	'jpg|jpeg|jpe' => 'image/jpeg',
2340
	'gif' => 'image/gif',
2341
	'png' => 'image/png',
2342
	'bmp' => 'image/bmp',
2343
	'tiff|tif' => 'image/tiff',
2344
	'ico' => 'image/x-icon',
2345
	// Video formats.
2346
	'asf|asx' => 'video/x-ms-asf',
2347
	'wmv' => 'video/x-ms-wmv',
2348
	'wmx' => 'video/x-ms-wmx',
2349
	'wm' => 'video/x-ms-wm',
2350
	'avi' => 'video/avi',
2351
	'divx' => 'video/divx',
2352
	'flv' => 'video/x-flv',
2353
	'mov|qt' => 'video/quicktime',
2354
	'mpeg|mpg|mpe' => 'video/mpeg',
2355
	'mp4|m4v' => 'video/mp4',
2356
	'ogv' => 'video/ogg',
2357
	'webm' => 'video/webm',
2358
	'mkv' => 'video/x-matroska',
2359
	'3gp|3gpp' => 'video/3gpp', // Can also be audio
2360
	'3g2|3gp2' => 'video/3gpp2', // Can also be audio
2361
	// Text formats.
2362
	'txt|asc|c|cc|h|srt' => 'text/plain',
2363
	'csv' => 'text/csv',
2364
	'tsv' => 'text/tab-separated-values',
2365
	'ics' => 'text/calendar',
2366
	'rtx' => 'text/richtext',
2367
	'css' => 'text/css',
2368
	'htm|html' => 'text/html',
2369
	'vtt' => 'text/vtt',
2370
	'dfxp' => 'application/ttaf+xml',
2371
	// Audio formats.
2372
	'mp3|m4a|m4b' => 'audio/mpeg',
2373
	'ra|ram' => 'audio/x-realaudio',
2374
	'wav' => 'audio/wav',
2375
	'ogg|oga' => 'audio/ogg',
2376
	'mid|midi' => 'audio/midi',
2377
	'wma' => 'audio/x-ms-wma',
2378
	'wax' => 'audio/x-ms-wax',
2379
	'mka' => 'audio/x-matroska',
2380
	// Misc application formats.
2381
	'rtf' => 'application/rtf',
2382
	'js' => 'application/javascript',
2383
	'pdf' => 'application/pdf',
2384
	'swf' => 'application/x-shockwave-flash',
2385
	'class' => 'application/java',
2386
	'tar' => 'application/x-tar',
2387
	'zip' => 'application/zip',
2388
	'gz|gzip' => 'application/x-gzip',
2389
	'rar' => 'application/rar',
2390
	'7z' => 'application/x-7z-compressed',
2391
	'exe' => 'application/x-msdownload',
2392
	'psd' => 'application/octet-stream',
2393
	'xcf' => 'application/octet-stream',
2394
	// MS Office formats.
2395
	'doc' => 'application/msword',
2396
	'pot|pps|ppt' => 'application/vnd.ms-powerpoint',
2397
	'wri' => 'application/vnd.ms-write',
2398
	'xla|xls|xlt|xlw' => 'application/vnd.ms-excel',
2399
	'mdb' => 'application/vnd.ms-access',
2400
	'mpp' => 'application/vnd.ms-project',
2401
	'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
2402
	'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
2403
	'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
2404
	'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
2405
	'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
2406
	'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
2407
	'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
2408
	'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
2409
	'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
2410
	'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
2411
	'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
2412
	'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
2413
	'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
2414
	'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
2415
	'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
2416
	'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
2417
	'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
2418
	'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
2419
	'sldm' => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
2420
	'onetoc|onetoc2|onetmp|onepkg' => 'application/onenote',
2421
	'oxps' => 'application/oxps',
2422
	'xps' => 'application/vnd.ms-xpsdocument',
2423
	// OpenOffice formats.
2424
	'odt' => 'application/vnd.oasis.opendocument.text',
2425
	'odp' => 'application/vnd.oasis.opendocument.presentation',
2426
	'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
2427
	'odg' => 'application/vnd.oasis.opendocument.graphics',
2428
	'odc' => 'application/vnd.oasis.opendocument.chart',
2429
	'odb' => 'application/vnd.oasis.opendocument.database',
2430
	'odf' => 'application/vnd.oasis.opendocument.formula',
2431
	// WordPerfect formats.
2432
	'wp|wpd' => 'application/wordperfect',
2433
	// iWork formats.
2434
	'key' => 'application/vnd.apple.keynote',
2435
	'numbers' => 'application/vnd.apple.numbers',
2436
	'pages' => 'application/vnd.apple.pages',
2437
	) );
2438
}
2439
2440
/**
2441
 * Retrieves the list of common file extensions and their types.
2442
 *
2443
 * @since 4.6.0
2444
 *
2445
 * @return array Array of file extensions types keyed by the type of file.
2446
 */
2447
function wp_get_ext_types() {
2448
2449
	/**
2450
	 * Filters file type based on the extension name.
2451
	 *
2452
	 * @since 2.5.0
2453
	 *
2454
	 * @see wp_ext2type()
2455
	 *
2456
	 * @param array $ext2type Multi-dimensional array with extensions for a default set
2457
	 *                        of file types.
2458
	 */
2459
	return apply_filters( 'ext2type', array(
2460
		'image'       => array( 'jpg', 'jpeg', 'jpe',  'gif',  'png',  'bmp',   'tif',  'tiff', 'ico' ),
2461
		'audio'       => array( 'aac', 'ac3',  'aif',  'aiff', 'm3a',  'm4a',   'm4b',  'mka',  'mp1',  'mp2',  'mp3', 'ogg', 'oga', 'ram', 'wav', 'wma' ),
2462
		'video'       => array( '3g2',  '3gp', '3gpp', 'asf', 'avi',  'divx', 'dv',   'flv',  'm4v',   'mkv',  'mov',  'mp4',  'mpeg', 'mpg', 'mpv', 'ogm', 'ogv', 'qt',  'rm', 'vob', 'wmv' ),
2463
		'document'    => array( 'doc', 'docx', 'docm', 'dotm', 'odt',  'pages', 'pdf',  'xps',  'oxps', 'rtf',  'wp', 'wpd', 'psd', 'xcf' ),
2464
		'spreadsheet' => array( 'numbers',     'ods',  'xls',  'xlsx', 'xlsm',  'xlsb' ),
2465
		'interactive' => array( 'swf', 'key',  'ppt',  'pptx', 'pptm', 'pps',   'ppsx', 'ppsm', 'sldx', 'sldm', 'odp' ),
2466
		'text'        => array( 'asc', 'csv',  'tsv',  'txt' ),
2467
		'archive'     => array( 'bz2', 'cab',  'dmg',  'gz',   'rar',  'sea',   'sit',  'sqx',  'tar',  'tgz',  'zip', '7z' ),
2468
		'code'        => array( 'css', 'htm',  'html', 'php',  'js' ),
2469
	) );
2470
}
2471
2472
/**
2473
 * Retrieve list of allowed mime types and file extensions.
2474
 *
2475
 * @since 2.8.6
2476
 *
2477
 * @param int|WP_User $user Optional. User to check. Defaults to current user.
2478
 * @return array Array of mime types keyed by the file extension regex corresponding
2479
 *               to those types.
2480
 */
2481
function get_allowed_mime_types( $user = null ) {
2482
	$t = wp_get_mime_types();
2483
2484
	unset( $t['swf'], $t['exe'] );
2485
	if ( function_exists( 'current_user_can' ) )
2486
		$unfiltered = $user ? user_can( $user, 'unfiltered_html' ) : current_user_can( 'unfiltered_html' );
2487
2488
	if ( empty( $unfiltered ) )
2489
		unset( $t['htm|html'] );
2490
2491
	/**
2492
	 * Filters list of allowed mime types and file extensions.
2493
	 *
2494
	 * @since 2.0.0
2495
	 *
2496
	 * @param array            $t    Mime types keyed by the file extension regex corresponding to
2497
	 *                               those types. 'swf' and 'exe' removed from full list. 'htm|html' also
2498
	 *                               removed depending on '$user' capabilities.
2499
	 * @param int|WP_User|null $user User ID, User object or null if not provided (indicates current user).
2500
	 */
2501
	return apply_filters( 'upload_mimes', $t, $user );
2502
}
2503
2504
/**
2505
 * Display "Are You Sure" message to confirm the action being taken.
2506
 *
2507
 * If the action has the nonce explain message, then it will be displayed
2508
 * along with the "Are you sure?" message.
2509
 *
2510
 * @since 2.0.4
2511
 *
2512
 * @param string $action The nonce action.
2513
 */
2514
function wp_nonce_ays( $action ) {
2515
	if ( 'log-out' == $action ) {
2516
		$html = sprintf( __( 'You are attempting to log out of %s' ), get_bloginfo( 'name' ) ) . '</p><p>';
2517
		$redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '';
2518
		$html .= sprintf( __( "Do you really want to <a href='%s'>log out</a>?"), wp_logout_url( $redirect_to ) );
2519
	} else {
2520
		$html = __( 'Are you sure you want to do this?' );
2521
		if ( wp_get_referer() )
2522
			$html .= "</p><p><a href='" . esc_url( remove_query_arg( 'updated', wp_get_referer() ) ) . "'>" . __( 'Please try again.' ) . "</a>";
0 ignored issues
show
Bug introduced by
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...
2523
	}
2524
2525
	wp_die( $html, __( 'WordPress Failure Notice' ), 403 );
2526
}
2527
2528
/**
2529
 * Kill WordPress execution and display HTML message with error message.
2530
 *
2531
 * This function complements the `die()` PHP function. The difference is that
2532
 * HTML will be displayed to the user. It is recommended to use this function
2533
 * only when the execution should not continue any further. It is not recommended
2534
 * to call this function very often, and try to handle as many errors as possible
2535
 * silently or more gracefully.
2536
 *
2537
 * As a shorthand, the desired HTTP response code may be passed as an integer to
2538
 * the `$title` parameter (the default title would apply) or the `$args` parameter.
2539
 *
2540
 * @since 2.0.4
2541
 * @since 4.1.0 The `$title` and `$args` parameters were changed to optionally accept
2542
 *              an integer to be used as the response code.
2543
 *
2544
 * @param string|WP_Error  $message Optional. Error message. If this is a WP_Error object,
2545
 *                                  the error's messages are used. Default empty.
2546
 * @param string|int       $title   Optional. Error title. If `$message` is a `WP_Error` object,
2547
 *                                  error data with the key 'title' may be used to specify the title.
2548
 *                                  If `$title` is an integer, then it is treated as the response
2549
 *                                  code. Default empty.
2550
 * @param string|array|int $args {
2551
 *     Optional. Arguments to control behavior. If `$args` is an integer, then it is treated
2552
 *     as the response code. Default empty array.
2553
 *
2554
 *     @type int    $response       The HTTP response code. Default 500.
2555
 *     @type bool   $back_link      Whether to include a link to go back. Default false.
2556
 *     @type string $text_direction The text direction. This is only useful internally, when WordPress
2557
 *                                  is still loading and the site's locale is not set up yet. Accepts 'rtl'.
2558
 *                                  Default is the value of is_rtl().
2559
 * }
2560
 */
2561
function wp_die( $message = '', $title = '', $args = array() ) {
2562
2563
	if ( is_int( $args ) ) {
2564
		$args = array( 'response' => $args );
2565
	} elseif ( is_int( $title ) ) {
2566
		$args  = array( 'response' => $title );
2567
		$title = '';
2568
	}
2569
2570
	if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
2571
		/**
2572
		 * Filters the callback for killing WordPress execution for Ajax requests.
2573
		 *
2574
		 * @since 3.4.0
2575
		 *
2576
		 * @param callable $function Callback function name.
2577
		 */
2578
		$function = apply_filters( 'wp_die_ajax_handler', '_ajax_wp_die_handler' );
2579
	} elseif ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
2580
		/**
2581
		 * Filters the callback for killing WordPress execution for XML-RPC requests.
2582
		 *
2583
		 * @since 3.4.0
2584
		 *
2585
		 * @param callable $function Callback function name.
2586
		 */
2587
		$function = apply_filters( 'wp_die_xmlrpc_handler', '_xmlrpc_wp_die_handler' );
2588
	} else {
2589
		/**
2590
		 * Filters the callback for killing WordPress execution for all non-Ajax, non-XML-RPC requests.
2591
		 *
2592
		 * @since 3.0.0
2593
		 *
2594
		 * @param callable $function Callback function name.
2595
		 */
2596
		$function = apply_filters( 'wp_die_handler', '_default_wp_die_handler' );
2597
	}
2598
2599
	call_user_func( $function, $message, $title, $args );
2600
}
2601
2602
/**
2603
 * Kills WordPress execution and display HTML message with error message.
2604
 *
2605
 * This is the default handler for wp_die if you want a custom one for your
2606
 * site then you can overload using the {@see 'wp_die_handler'} filter in wp_die().
2607
 *
2608
 * @since 3.0.0
2609
 * @access private
2610
 *
2611
 * @param string       $message Error message.
2612
 * @param string       $title   Optional. Error title. Default empty.
2613
 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
2614
 */
2615
function _default_wp_die_handler( $message, $title = '', $args = array() ) {
2616
	$defaults = array( 'response' => 500 );
2617
	$r = wp_parse_args($args, $defaults);
2618
2619
	$have_gettext = function_exists('__');
2620
2621
	if ( function_exists( 'is_wp_error' ) && is_wp_error( $message ) ) {
2622
		if ( empty( $title ) ) {
2623
			$error_data = $message->get_error_data();
0 ignored issues
show
Bug introduced by
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...
2624
			if ( is_array( $error_data ) && isset( $error_data['title'] ) )
2625
				$title = $error_data['title'];
2626
		}
2627
		$errors = $message->get_error_messages();
0 ignored issues
show
Bug introduced by
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...
2628
		switch ( count( $errors ) ) {
2629
		case 0 :
2630
			$message = '';
2631
			break;
2632
		case 1 :
2633
			$message = "<p>{$errors[0]}</p>";
2634
			break;
2635
		default :
2636
			$message = "<ul>\n\t\t<li>" . join( "</li>\n\t\t<li>", $errors ) . "</li>\n\t</ul>";
2637
			break;
2638
		}
2639
	} elseif ( is_string( $message ) ) {
2640
		$message = "<p>$message</p>";
2641
	}
2642
2643
	if ( isset( $r['back_link'] ) && $r['back_link'] ) {
2644
		$back_text = $have_gettext? __('&laquo; Back') : '&laquo; Back';
2645
		$message .= "\n<p><a href='javascript:history.back()'>$back_text</a></p>";
2646
	}
2647
2648
	if ( ! did_action( 'admin_head' ) ) :
2649
		if ( !headers_sent() ) {
2650
			status_header( $r['response'] );
2651
			nocache_headers();
2652
			header( 'Content-Type: text/html; charset=utf-8' );
2653
		}
2654
2655
		if ( empty($title) )
2656
			$title = $have_gettext ? __('WordPress &rsaquo; Error') : 'WordPress &rsaquo; Error';
2657
2658
		$text_direction = 'ltr';
2659
		if ( isset($r['text_direction']) && 'rtl' == $r['text_direction'] )
2660
			$text_direction = 'rtl';
2661
		elseif ( function_exists( 'is_rtl' ) && is_rtl() )
2662
			$text_direction = 'rtl';
2663
?>
2664
<!DOCTYPE html>
2665
<!-- 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
2666
-->
2667
<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'"; ?>>
2668
<head>
2669
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
2670
	<meta name="viewport" content="width=device-width">
2671
	<?php
2672
	if ( function_exists( 'wp_no_robots' ) ) {
2673
		wp_no_robots();
2674
	}
2675
	?>
2676
	<title><?php echo $title ?></title>
2677
	<style type="text/css">
2678
		html {
2679
			background: #f1f1f1;
2680
		}
2681
		body {
2682
			background: #fff;
2683
			color: #444;
2684
			font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
2685
			margin: 2em auto;
2686
			padding: 1em 2em;
2687
			max-width: 700px;
2688
			-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.13);
2689
			box-shadow: 0 1px 3px rgba(0,0,0,0.13);
2690
		}
2691
		h1 {
2692
			border-bottom: 1px solid #dadada;
2693
			clear: both;
2694
			color: #666;
2695
			font-size: 24px;
2696
			margin: 30px 0 0 0;
2697
			padding: 0;
2698
			padding-bottom: 7px;
2699
		}
2700
		#error-page {
2701
			margin-top: 50px;
2702
		}
2703
		#error-page p {
2704
			font-size: 14px;
2705
			line-height: 1.5;
2706
			margin: 25px 0 20px;
2707
		}
2708
		#error-page code {
2709
			font-family: Consolas, Monaco, monospace;
2710
		}
2711
		ul li {
2712
			margin-bottom: 10px;
2713
			font-size: 14px ;
2714
		}
2715
		a {
2716
			color: #0073aa;
2717
		}
2718
		a:hover,
2719
		a:active {
2720
			color: #00a0d2;
2721
		}
2722
		a:focus {
2723
			color: #124964;
2724
		    -webkit-box-shadow:
2725
		    	0 0 0 1px #5b9dd9,
2726
				0 0 2px 1px rgba(30, 140, 190, .8);
2727
		    box-shadow:
2728
		    	0 0 0 1px #5b9dd9,
2729
				0 0 2px 1px rgba(30, 140, 190, .8);
2730
			outline: none;
2731
		}
2732
		.button {
2733
			background: #f7f7f7;
2734
			border: 1px solid #ccc;
2735
			color: #555;
2736
			display: inline-block;
2737
			text-decoration: none;
2738
			font-size: 13px;
2739
			line-height: 26px;
2740
			height: 28px;
2741
			margin: 0;
2742
			padding: 0 10px 1px;
2743
			cursor: pointer;
2744
			-webkit-border-radius: 3px;
2745
			-webkit-appearance: none;
2746
			border-radius: 3px;
2747
			white-space: nowrap;
2748
			-webkit-box-sizing: border-box;
2749
			-moz-box-sizing:    border-box;
2750
			box-sizing:         border-box;
2751
2752
			-webkit-box-shadow: 0 1px 0 #ccc;
2753
			box-shadow: 0 1px 0 #ccc;
2754
		 	vertical-align: top;
2755
		}
2756
2757
		.button.button-large {
2758
			height: 30px;
2759
			line-height: 28px;
2760
			padding: 0 12px 2px;
2761
		}
2762
2763
		.button:hover,
2764
		.button:focus {
2765
			background: #fafafa;
2766
			border-color: #999;
2767
			color: #23282d;
2768
		}
2769
2770
		.button:focus  {
2771
			border-color: #5b9dd9;
2772
			-webkit-box-shadow: 0 0 3px rgba( 0, 115, 170, .8 );
2773
			box-shadow: 0 0 3px rgba( 0, 115, 170, .8 );
2774
			outline: none;
2775
		}
2776
2777
		.button:active {
2778
			background: #eee;
2779
			border-color: #999;
2780
		 	-webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 );
2781
		 	box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 );
2782
		 	-webkit-transform: translateY(1px);
2783
		 	-ms-transform: translateY(1px);
2784
		 	transform: translateY(1px);
2785
		}
2786
2787
		<?php
2788
		if ( 'rtl' == $text_direction ) {
2789
			echo 'body { font-family: Tahoma, Arial; }';
2790
		}
2791
		?>
2792
	</style>
2793
</head>
2794
<body id="error-page">
2795
<?php endif; // ! did_action( 'admin_head' ) ?>
2796
	<?php echo $message; ?>
2797
</body>
2798
</html>
2799
<?php
2800
	die();
2801
}
2802
2803
/**
2804
 * Kill WordPress execution and display XML message with error message.
2805
 *
2806
 * This is the handler for wp_die when processing XMLRPC requests.
2807
 *
2808
 * @since 3.2.0
2809
 * @access private
2810
 *
2811
 * @global wp_xmlrpc_server $wp_xmlrpc_server
2812
 *
2813
 * @param string       $message Error message.
2814
 * @param string       $title   Optional. Error title. Default empty.
2815
 * @param string|array $args    Optional. Arguments to control behavior. Default empty array.
2816
 */
2817
function _xmlrpc_wp_die_handler( $message, $title = '', $args = array() ) {
0 ignored issues
show
Unused Code introduced by
The parameter $title is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2818
	global $wp_xmlrpc_server;
2819
	$defaults = array( 'response' => 500 );
2820
2821
	$r = wp_parse_args($args, $defaults);
2822
2823
	if ( $wp_xmlrpc_server ) {
2824
		$error = new IXR_Error( $r['response'] , $message);
2825
		$wp_xmlrpc_server->output( $error->getXml() );
2826
	}
2827
	die();
2828
}
2829
2830
/**
2831
 * Kill WordPress ajax execution.
2832
 *
2833
 * This is the handler for wp_die when processing Ajax requests.
2834
 *
2835
 * @since 3.4.0
2836
 * @access private
2837
 *
2838
 * @param string $message Optional. Response to print. Default empty.
2839
 */
2840
function _ajax_wp_die_handler( $message = '' ) {
2841
	if ( is_scalar( $message ) )
2842
		die( (string) $message );
2843
	die( '0' );
2844
}
2845
2846
/**
2847
 * Kill WordPress execution.
2848
 *
2849
 * This is the handler for wp_die when processing APP requests.
2850
 *
2851
 * @since 3.4.0
2852
 * @access private
2853
 *
2854
 * @param string $message Optional. Response to print. Default empty.
2855
 */
2856
function _scalar_wp_die_handler( $message = '' ) {
2857
	if ( is_scalar( $message ) )
2858
		die( (string) $message );
2859
	die();
2860
}
2861
2862
/**
2863
 * Encode a variable into JSON, with some sanity checks.
2864
 *
2865
 * @since 4.1.0
2866
 *
2867
 * @param mixed $data    Variable (usually an array or object) to encode as JSON.
2868
 * @param int   $options Optional. Options to be passed to json_encode(). Default 0.
2869
 * @param int   $depth   Optional. Maximum depth to walk through $data. Must be
2870
 *                       greater than 0. Default 512.
2871
 * @return string|false The JSON encoded string, or false if it cannot be encoded.
2872
 */
2873
function wp_json_encode( $data, $options = 0, $depth = 512 ) {
2874
	/*
2875
	 * json_encode() has had extra params added over the years.
2876
	 * $options was added in 5.3, and $depth in 5.5.
2877
	 * We need to make sure we call it with the correct arguments.
2878
	 */
2879
	if ( version_compare( PHP_VERSION, '5.5', '>=' ) ) {
2880
		$args = array( $data, $options, $depth );
2881
	} elseif ( version_compare( PHP_VERSION, '5.3', '>=' ) ) {
2882
		$args = array( $data, $options );
2883
	} else {
2884
		$args = array( $data );
2885
	}
2886
2887
	// Prepare the data for JSON serialization.
2888
	$args[0] = _wp_json_prepare_data( $data );
2889
2890
	$json = @call_user_func_array( 'json_encode', $args );
2891
2892
	// If json_encode() was successful, no need to do more sanity checking.
2893
	// ... unless we're in an old version of PHP, and json_encode() returned
2894
	// a string containing 'null'. Then we need to do more sanity checking.
2895
	if ( false !== $json && ( version_compare( PHP_VERSION, '5.5', '>=' ) || false === strpos( $json, 'null' ) ) )  {
2896
		return $json;
2897
	}
2898
2899
	try {
2900
		$args[0] = _wp_json_sanity_check( $data, $depth );
2901
	} catch ( Exception $e ) {
2902
		return false;
2903
	}
2904
2905
	return call_user_func_array( 'json_encode', $args );
2906
}
2907
2908
/**
2909
 * Perform sanity checks on data that shall be encoded to JSON.
2910
 *
2911
 * @ignore
2912
 * @since 4.1.0
2913
 * @access private
2914
 *
2915
 * @see wp_json_encode()
2916
 *
2917
 * @param mixed $data  Variable (usually an array or object) to encode as JSON.
2918
 * @param int   $depth Maximum depth to walk through $data. Must be greater than 0.
2919
 * @return mixed The sanitized data that shall be encoded to JSON.
2920
 */
2921
function _wp_json_sanity_check( $data, $depth ) {
2922
	if ( $depth < 0 ) {
2923
		throw new Exception( 'Reached depth limit' );
2924
	}
2925
2926
	if ( is_array( $data ) ) {
2927
		$output = array();
2928
		foreach ( $data as $id => $el ) {
2929
			// Don't forget to sanitize the ID!
2930
			if ( is_string( $id ) ) {
2931
				$clean_id = _wp_json_convert_string( $id );
2932
			} else {
2933
				$clean_id = $id;
2934
			}
2935
2936
			// Check the element type, so that we're only recursing if we really have to.
2937
			if ( is_array( $el ) || is_object( $el ) ) {
2938
				$output[ $clean_id ] = _wp_json_sanity_check( $el, $depth - 1 );
2939
			} elseif ( is_string( $el ) ) {
2940
				$output[ $clean_id ] = _wp_json_convert_string( $el );
2941
			} else {
2942
				$output[ $clean_id ] = $el;
2943
			}
2944
		}
2945
	} elseif ( is_object( $data ) ) {
2946
		$output = new stdClass;
2947
		foreach ( $data as $id => $el ) {
2948
			if ( is_string( $id ) ) {
2949
				$clean_id = _wp_json_convert_string( $id );
2950
			} else {
2951
				$clean_id = $id;
2952
			}
2953
2954
			if ( is_array( $el ) || is_object( $el ) ) {
2955
				$output->$clean_id = _wp_json_sanity_check( $el, $depth - 1 );
2956
			} elseif ( is_string( $el ) ) {
2957
				$output->$clean_id = _wp_json_convert_string( $el );
2958
			} else {
2959
				$output->$clean_id = $el;
2960
			}
2961
		}
2962
	} elseif ( is_string( $data ) ) {
2963
		return _wp_json_convert_string( $data );
2964
	} else {
2965
		return $data;
2966
	}
2967
2968
	return $output;
2969
}
2970
2971
/**
2972
 * Convert a string to UTF-8, so that it can be safely encoded to JSON.
2973
 *
2974
 * @ignore
2975
 * @since 4.1.0
2976
 * @access private
2977
 *
2978
 * @see _wp_json_sanity_check()
2979
 *
2980
 * @staticvar bool $use_mb
2981
 *
2982
 * @param string $string The string which is to be converted.
2983
 * @return string The checked string.
2984
 */
2985
function _wp_json_convert_string( $string ) {
2986
	static $use_mb = null;
2987
	if ( is_null( $use_mb ) ) {
2988
		$use_mb = function_exists( 'mb_convert_encoding' );
2989
	}
2990
2991
	if ( $use_mb ) {
2992
		$encoding = mb_detect_encoding( $string, mb_detect_order(), true );
2993
		if ( $encoding ) {
2994
			return mb_convert_encoding( $string, 'UTF-8', $encoding );
2995
		} else {
2996
			return mb_convert_encoding( $string, 'UTF-8', 'UTF-8' );
2997
		}
2998
	} else {
2999
		return wp_check_invalid_utf8( $string, true );
3000
	}
3001
}
3002
3003
/**
3004
 * Prepares response data to be serialized to JSON.
3005
 *
3006
 * This supports the JsonSerializable interface for PHP 5.2-5.3 as well.
3007
 *
3008
 * @ignore
3009
 * @since 4.4.0
3010
 * @access private
3011
 *
3012
 * @param mixed $data Native representation.
3013
 * @return bool|int|float|null|string|array Data ready for `json_encode()`.
3014
 */
3015
function _wp_json_prepare_data( $data ) {
3016
	if ( ! defined( 'WP_JSON_SERIALIZE_COMPATIBLE' ) || WP_JSON_SERIALIZE_COMPATIBLE === false ) {
3017
		return $data;
3018
	}
3019
3020
	switch ( gettype( $data ) ) {
3021
		case 'boolean':
3022
		case 'integer':
3023
		case 'double':
3024
		case 'string':
3025
		case 'NULL':
3026
			// These values can be passed through.
3027
			return $data;
3028
3029
		case 'array':
3030
			// Arrays must be mapped in case they also return objects.
3031
			return array_map( '_wp_json_prepare_data', $data );
3032
3033
		case 'object':
3034
			// If this is an incomplete object (__PHP_Incomplete_Class), bail.
3035
			if ( ! is_object( $data ) ) {
3036
				return null;
3037
			}
3038
3039
			if ( $data instanceof JsonSerializable ) {
3040
				$data = $data->jsonSerialize();
3041
			} else {
3042
				$data = get_object_vars( $data );
3043
			}
3044
3045
			// Now, pass the array (or whatever was returned from jsonSerialize through).
3046
			return _wp_json_prepare_data( $data );
3047
3048
		default:
3049
			return null;
3050
	}
3051
}
3052
3053
/**
3054
 * Send a JSON response back to an Ajax request.
3055
 *
3056
 * @since 3.5.0
3057
 *
3058
 * @param mixed $response Variable (usually an array or object) to encode as JSON,
3059
 *                        then print and die.
3060
 */
3061
function wp_send_json( $response ) {
3062
	@header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
3063
	echo wp_json_encode( $response );
3064
	if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
3065
		wp_die();
3066
	else
3067
		die;
3068
}
3069
3070
/**
3071
 * Send a JSON response back to an Ajax request, indicating success.
3072
 *
3073
 * @since 3.5.0
3074
 *
3075
 * @param mixed $data Data to encode as JSON, then print and die.
3076
 */
3077
function wp_send_json_success( $data = null ) {
3078
	$response = array( 'success' => true );
3079
3080
	if ( isset( $data ) )
3081
		$response['data'] = $data;
3082
3083
	wp_send_json( $response );
3084
}
3085
3086
/**
3087
 * Send a JSON response back to an Ajax request, indicating failure.
3088
 *
3089
 * If the `$data` parameter is a WP_Error object, the errors
3090
 * within the object are processed and output as an array of error
3091
 * codes and corresponding messages. All other types are output
3092
 * without further processing.
3093
 *
3094
 * @since 3.5.0
3095
 * @since 4.1.0 The `$data` parameter is now processed if a WP_Error object is passed in.
3096
 *
3097
 * @param mixed $data Data to encode as JSON, then print and die.
3098
 */
3099
function wp_send_json_error( $data = null ) {
3100
	$response = array( 'success' => false );
3101
3102
	if ( isset( $data ) ) {
3103
		if ( is_wp_error( $data ) ) {
3104
			$result = array();
3105
			foreach ( $data->errors as $code => $messages ) {
3106
				foreach ( $messages as $message ) {
3107
					$result[] = array( 'code' => $code, 'message' => $message );
3108
				}
3109
			}
3110
3111
			$response['data'] = $result;
3112
		} else {
3113
			$response['data'] = $data;
3114
		}
3115
	}
3116
3117
	wp_send_json( $response );
3118
}
3119
3120
/**
3121
 * Checks that a JSONP callback is a valid JavaScript callback.
3122
 *
3123
 * Only allows alphanumeric characters and the dot character in callback
3124
 * function names. This helps to mitigate XSS attacks caused by directly
3125
 * outputting user input.
3126
 *
3127
 * @since 4.6.0
3128
 *
3129
 * @param string $callback Supplied JSONP callback function.
3130
 * @return bool True if valid callback, otherwise false.
3131
 */
3132
function wp_check_jsonp_callback( $callback ) {
3133
	if ( ! is_string( $callback ) ) {
3134
		return false;
3135
	}
3136
3137
	$jsonp_callback = preg_replace( '/[^\w\.]/', '', $callback, -1, $illegal_char_count );
0 ignored issues
show
Unused Code introduced by
$jsonp_callback is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3138
3139
	return 0 === $illegal_char_count;
3140
}
3141
3142
/**
3143
 * Retrieve the WordPress home page URL.
3144
 *
3145
 * If the constant named 'WP_HOME' exists, then it will be used and returned
3146
 * by the function. This can be used to counter the redirection on your local
3147
 * development environment.
3148
 *
3149
 * @since 2.2.0
3150
 * @access private
3151
 *
3152
 * @see WP_HOME
3153
 *
3154
 * @param string $url URL for the home location.
3155
 * @return string Homepage location.
3156
 */
3157
function _config_wp_home( $url = '' ) {
3158
	if ( defined( 'WP_HOME' ) )
3159
		return untrailingslashit( WP_HOME );
3160
	return $url;
3161
}
3162
3163
/**
3164
 * Retrieve the WordPress site URL.
3165
 *
3166
 * If the constant named 'WP_SITEURL' is defined, then the value in that
3167
 * constant will always be returned. This can be used for debugging a site
3168
 * on your localhost while not having to change the database to your URL.
3169
 *
3170
 * @since 2.2.0
3171
 * @access private
3172
 *
3173
 * @see WP_SITEURL
3174
 *
3175
 * @param string $url URL to set the WordPress site location.
3176
 * @return string The WordPress Site URL.
3177
 */
3178
function _config_wp_siteurl( $url = '' ) {
3179
	if ( defined( 'WP_SITEURL' ) )
3180
		return untrailingslashit( WP_SITEURL );
3181
	return $url;
3182
}
3183
3184
/**
3185
 * Set the localized direction for MCE plugin.
3186
 *
3187
 * Will only set the direction to 'rtl', if the WordPress locale has
3188
 * the text direction set to 'rtl'.
3189
 *
3190
 * Fills in the 'directionality' setting, enables the 'directionality'
3191
 * plugin, and adds the 'ltr' button to 'toolbar1', formerly
3192
 * 'theme_advanced_buttons1' array keys. These keys are then returned
3193
 * in the $input (TinyMCE settings) array.
3194
 *
3195
 * @since 2.1.0
3196
 * @access private
3197
 *
3198
 * @param array $input MCE settings array.
3199
 * @return array Direction set for 'rtl', if needed by locale.
3200
 */
3201
function _mce_set_direction( $input ) {
3202
	if ( is_rtl() ) {
3203
		$input['directionality'] = 'rtl';
3204
3205
		if ( ! empty( $input['plugins'] ) && strpos( $input['plugins'], 'directionality' ) === false ) {
3206
			$input['plugins'] .= ',directionality';
3207
		}
3208
3209
		if ( ! empty( $input['toolbar1'] ) && ! preg_match( '/\bltr\b/', $input['toolbar1'] ) ) {
3210
			$input['toolbar1'] .= ',ltr';
3211
		}
3212
	}
3213
3214
	return $input;
3215
}
3216
3217
3218
/**
3219
 * Convert smiley code to the icon graphic file equivalent.
3220
 *
3221
 * You can turn off smilies, by going to the write setting screen and unchecking
3222
 * the box, or by setting 'use_smilies' option to false or removing the option.
3223
 *
3224
 * Plugins may override the default smiley list by setting the $wpsmiliestrans
3225
 * to an array, with the key the code the blogger types in and the value the
3226
 * image file.
3227
 *
3228
 * The $wp_smiliessearch global is for the regular expression and is set each
3229
 * time the function is called.
3230
 *
3231
 * The full list of smilies can be found in the function and won't be listed in
3232
 * the description. Probably should create a Codex page for it, so that it is
3233
 * available.
3234
 *
3235
 * @global array $wpsmiliestrans
3236
 * @global array $wp_smiliessearch
3237
 *
3238
 * @since 2.2.0
3239
 */
3240
function smilies_init() {
3241
	global $wpsmiliestrans, $wp_smiliessearch;
3242
3243
	// don't bother setting up smilies if they are disabled
3244
	if ( !get_option( 'use_smilies' ) )
3245
		return;
3246
3247
	if ( !isset( $wpsmiliestrans ) ) {
3248
		$wpsmiliestrans = array(
3249
		':mrgreen:' => 'mrgreen.png',
3250
		':neutral:' => "\xf0\x9f\x98\x90",
3251
		':twisted:' => "\xf0\x9f\x98\x88",
3252
		  ':arrow:' => "\xe2\x9e\xa1",
3253
		  ':shock:' => "\xf0\x9f\x98\xaf",
3254
		  ':smile:' => "\xf0\x9f\x99\x82",
3255
		    ':???:' => "\xf0\x9f\x98\x95",
3256
		   ':cool:' => "\xf0\x9f\x98\x8e",
3257
		   ':evil:' => "\xf0\x9f\x91\xbf",
3258
		   ':grin:' => "\xf0\x9f\x98\x80",
3259
		   ':idea:' => "\xf0\x9f\x92\xa1",
3260
		   ':oops:' => "\xf0\x9f\x98\xb3",
3261
		   ':razz:' => "\xf0\x9f\x98\x9b",
3262
		   ':roll:' => "\xf0\x9f\x99\x84",
3263
		   ':wink:' => "\xf0\x9f\x98\x89",
3264
		    ':cry:' => "\xf0\x9f\x98\xa5",
3265
		    ':eek:' => "\xf0\x9f\x98\xae",
3266
		    ':lol:' => "\xf0\x9f\x98\x86",
3267
		    ':mad:' => "\xf0\x9f\x98\xa1",
3268
		    ':sad:' => "\xf0\x9f\x99\x81",
3269
		      '8-)' => "\xf0\x9f\x98\x8e",
3270
		      '8-O' => "\xf0\x9f\x98\xaf",
3271
		      ':-(' => "\xf0\x9f\x99\x81",
3272
		      ':-)' => "\xf0\x9f\x99\x82",
3273
		      ':-?' => "\xf0\x9f\x98\x95",
3274
		      ':-D' => "\xf0\x9f\x98\x80",
3275
		      ':-P' => "\xf0\x9f\x98\x9b",
3276
		      ':-o' => "\xf0\x9f\x98\xae",
3277
		      ':-x' => "\xf0\x9f\x98\xa1",
3278
		      ':-|' => "\xf0\x9f\x98\x90",
3279
		      ';-)' => "\xf0\x9f\x98\x89",
3280
		// This one transformation breaks regular text with frequency.
3281
		//     '8)' => "\xf0\x9f\x98\x8e",
3282
		       '8O' => "\xf0\x9f\x98\xaf",
3283
		       ':(' => "\xf0\x9f\x99\x81",
3284
		       ':)' => "\xf0\x9f\x99\x82",
3285
		       ':?' => "\xf0\x9f\x98\x95",
3286
		       ':D' => "\xf0\x9f\x98\x80",
3287
		       ':P' => "\xf0\x9f\x98\x9b",
3288
		       ':o' => "\xf0\x9f\x98\xae",
3289
		       ':x' => "\xf0\x9f\x98\xa1",
3290
		       ':|' => "\xf0\x9f\x98\x90",
3291
		       ';)' => "\xf0\x9f\x98\x89",
3292
		      ':!:' => "\xe2\x9d\x97",
3293
		      ':?:' => "\xe2\x9d\x93",
3294
		);
3295
	}
3296
3297
	if (count($wpsmiliestrans) == 0) {
3298
		return;
3299
	}
3300
3301
	/*
3302
	 * NOTE: we sort the smilies in reverse key order. This is to make sure
3303
	 * we match the longest possible smilie (:???: vs :?) as the regular
3304
	 * expression used below is first-match
3305
	 */
3306
	krsort($wpsmiliestrans);
3307
3308
	$spaces = wp_spaces_regexp();
3309
3310
	// Begin first "subpattern"
3311
	$wp_smiliessearch = '/(?<=' . $spaces . '|^)';
3312
3313
	$subchar = '';
3314
	foreach ( (array) $wpsmiliestrans as $smiley => $img ) {
3315
		$firstchar = substr($smiley, 0, 1);
3316
		$rest = substr($smiley, 1);
3317
3318
		// new subpattern?
3319
		if ($firstchar != $subchar) {
3320
			if ($subchar != '') {
3321
				$wp_smiliessearch .= ')(?=' . $spaces . '|$)';  // End previous "subpattern"
3322
				$wp_smiliessearch .= '|(?<=' . $spaces . '|^)'; // Begin another "subpattern"
3323
			}
3324
			$subchar = $firstchar;
3325
			$wp_smiliessearch .= preg_quote($firstchar, '/') . '(?:';
3326
		} else {
3327
			$wp_smiliessearch .= '|';
3328
		}
3329
		$wp_smiliessearch .= preg_quote($rest, '/');
3330
	}
3331
3332
	$wp_smiliessearch .= ')(?=' . $spaces . '|$)/m';
3333
3334
}
3335
3336
/**
3337
 * Merge user defined arguments into defaults array.
3338
 *
3339
 * This function is used throughout WordPress to allow for both string or array
3340
 * to be merged into another array.
3341
 *
3342
 * @since 2.2.0
3343
 *
3344
 * @param string|array $args     Value to merge with $defaults
3345
 * @param array        $defaults Optional. Array that serves as the defaults. Default empty.
3346
 * @return array Merged user defined values with defaults.
3347
 */
3348
function wp_parse_args( $args, $defaults = '' ) {
3349
	if ( is_object( $args ) )
3350
		$r = get_object_vars( $args );
3351
	elseif ( is_array( $args ) )
3352
		$r =& $args;
3353
	else
3354
		wp_parse_str( $args, $r );
3355
3356
	if ( is_array( $defaults ) )
3357
		return array_merge( $defaults, $r );
3358
	return $r;
3359
}
3360
3361
/**
3362
 * Clean up an array, comma- or space-separated list of IDs.
3363
 *
3364
 * @since 3.0.0
3365
 *
3366
 * @param array|string $list List of ids.
3367
 * @return array Sanitized array of IDs.
3368
 */
3369
function wp_parse_id_list( $list ) {
3370
	if ( !is_array($list) )
3371
		$list = preg_split('/[\s,]+/', $list);
3372
3373
	return array_unique(array_map('absint', $list));
3374
}
3375
3376
/**
3377
 * Extract a slice of an array, given a list of keys.
3378
 *
3379
 * @since 3.1.0
3380
 *
3381
 * @param array $array The original array.
3382
 * @param array $keys  The list of keys.
3383
 * @return array The array slice.
3384
 */
3385
function wp_array_slice_assoc( $array, $keys ) {
3386
	$slice = array();
3387
	foreach ( $keys as $key )
3388
		if ( isset( $array[ $key ] ) )
3389
			$slice[ $key ] = $array[ $key ];
3390
3391
	return $slice;
3392
}
3393
3394
/**
3395
 * Determines if the variable is a numeric-indexed array.
3396
 *
3397
 * @since 4.4.0
3398
 *
3399
 * @param mixed $data Variable to check.
3400
 * @return bool Whether the variable is a list.
3401
 */
3402
function wp_is_numeric_array( $data ) {
3403
	if ( ! is_array( $data ) ) {
3404
		return false;
3405
	}
3406
3407
	$keys = array_keys( $data );
3408
	$string_keys = array_filter( $keys, 'is_string' );
3409
	return count( $string_keys ) === 0;
3410
}
3411
3412
/**
3413
 * Filters a list of objects, based on a set of key => value arguments.
3414
 *
3415
 * @since 3.0.0
3416
 *
3417
 * @param array       $list     An array of objects to filter
3418
 * @param array       $args     Optional. An array of key => value arguments to match
3419
 *                              against each object. Default empty array.
3420
 * @param string      $operator Optional. The logical operation to perform. 'or' means
3421
 *                              only one element from the array needs to match; 'and'
3422
 *                              means all elements must match; 'not' means no elements may
3423
 *                              match. Default 'and'.
3424
 * @param bool|string $field    A field from the object to place instead of the entire object.
3425
 *                              Default false.
3426
 * @return array A list of objects or object fields.
3427
 */
3428
function wp_filter_object_list( $list, $args = array(), $operator = 'and', $field = false ) {
3429
	if ( ! is_array( $list ) )
3430
		return array();
3431
3432
	$list = wp_list_filter( $list, $args, $operator );
3433
3434
	if ( $field )
3435
		$list = wp_list_pluck( $list, $field );
0 ignored issues
show
Bug introduced by
It seems like $field defined by parameter $field on line 3428 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...
3436
3437
	return $list;
3438
}
3439
3440
/**
3441
 * Filters a list of objects, based on a set of key => value arguments.
3442
 *
3443
 * @since 3.1.0
3444
 *
3445
 * @param array  $list     An array of objects to filter.
3446
 * @param array  $args     Optional. An array of key => value arguments to match
3447
 *                         against each object. Default empty array.
3448
 * @param string $operator Optional. The logical operation to perform. 'AND' means
3449
 *                         all elements from the array must match. 'OR' means only
3450
 *                         one element needs to match. 'NOT' means no elements may
3451
 *                         match. Default 'AND'.
3452
 * @return array Array of found values.
3453
 */
3454
function wp_list_filter( $list, $args = array(), $operator = 'AND' ) {
3455
	if ( ! is_array( $list ) )
3456
		return array();
3457
3458
	if ( empty( $args ) )
3459
		return $list;
3460
3461
	$operator = strtoupper( $operator );
3462
	$count = count( $args );
3463
	$filtered = array();
3464
3465
	foreach ( $list as $key => $obj ) {
3466
		$to_match = (array) $obj;
3467
3468
		$matched = 0;
3469
		foreach ( $args as $m_key => $m_value ) {
3470
			if ( array_key_exists( $m_key, $to_match ) && $m_value == $to_match[ $m_key ] )
3471
				$matched++;
3472
		}
3473
3474
		if ( ( 'AND' == $operator && $matched == $count )
3475
		  || ( 'OR' == $operator && $matched > 0 )
3476
		  || ( 'NOT' == $operator && 0 == $matched ) ) {
3477
			$filtered[$key] = $obj;
3478
		}
3479
	}
3480
3481
	return $filtered;
3482
}
3483
3484
/**
3485
 * Pluck a certain field out of each object in a list.
3486
 *
3487
 * This has the same functionality and prototype of
3488
 * array_column() (PHP 5.5) but also supports objects.
3489
 *
3490
 * @since 3.1.0
3491
 * @since 4.0.0 $index_key parameter added.
3492
 *
3493
 * @param array      $list      List of objects or arrays
3494
 * @param int|string $field     Field from the object to place instead of the entire object
3495
 * @param int|string $index_key Optional. Field from the object to use as keys for the new array.
3496
 *                              Default null.
3497
 * @return array Array of found values. If `$index_key` is set, an array of found values with keys
3498
 *               corresponding to `$index_key`. If `$index_key` is null, array keys from the original
3499
 *               `$list` will be preserved in the results.
3500
 */
3501
function wp_list_pluck( $list, $field, $index_key = null ) {
3502
	if ( ! $index_key ) {
3503
		/*
3504
		 * This is simple. Could at some point wrap array_column()
3505
		 * if we knew we had an array of arrays.
3506
		 */
3507
		foreach ( $list as $key => $value ) {
3508
			if ( is_object( $value ) ) {
3509
				$list[ $key ] = $value->$field;
3510
			} else {
3511
				$list[ $key ] = $value[ $field ];
3512
			}
3513
		}
3514
		return $list;
3515
	}
3516
3517
	/*
3518
	 * When index_key is not set for a particular item, push the value
3519
	 * to the end of the stack. This is how array_column() behaves.
3520
	 */
3521
	$newlist = array();
3522
	foreach ( $list as $value ) {
3523
		if ( is_object( $value ) ) {
3524
			if ( isset( $value->$index_key ) ) {
3525
				$newlist[ $value->$index_key ] = $value->$field;
3526
			} else {
3527
				$newlist[] = $value->$field;
3528
			}
3529
		} else {
3530
			if ( isset( $value[ $index_key ] ) ) {
3531
				$newlist[ $value[ $index_key ] ] = $value[ $field ];
3532
			} else {
3533
				$newlist[] = $value[ $field ];
3534
			}
3535
		}
3536
	}
3537
3538
	return $newlist;
3539
}
3540
3541
/**
3542
 * Determines if Widgets library should be loaded.
3543
 *
3544
 * Checks to make sure that the widgets library hasn't already been loaded.
3545
 * If it hasn't, then it will load the widgets library and run an action hook.
3546
 *
3547
 * @since 2.2.0
3548
 */
3549
function wp_maybe_load_widgets() {
3550
	/**
3551
	 * Filters whether to load the Widgets library.
3552
	 *
3553
	 * Passing a falsey value to the filter will effectively short-circuit
3554
	 * the Widgets library from loading.
3555
	 *
3556
	 * @since 2.8.0
3557
	 *
3558
	 * @param bool $wp_maybe_load_widgets Whether to load the Widgets library.
3559
	 *                                    Default true.
3560
	 */
3561
	if ( ! apply_filters( 'load_default_widgets', true ) ) {
3562
		return;
3563
	}
3564
3565
	require_once( ABSPATH . WPINC . '/default-widgets.php' );
3566
3567
	add_action( '_admin_menu', 'wp_widgets_add_menu' );
3568
}
3569
3570
/**
3571
 * Append the Widgets menu to the themes main menu.
3572
 *
3573
 * @since 2.2.0
3574
 *
3575
 * @global array $submenu
3576
 */
3577
function wp_widgets_add_menu() {
3578
	global $submenu;
3579
3580
	if ( ! current_theme_supports( 'widgets' ) )
3581
		return;
3582
3583
	$submenu['themes.php'][7] = array( __( 'Widgets' ), 'edit_theme_options', 'widgets.php' );
3584
	ksort( $submenu['themes.php'], SORT_NUMERIC );
3585
}
3586
3587
/**
3588
 * Flush all output buffers for PHP 5.2.
3589
 *
3590
 * Make sure all output buffers are flushed before our singletons are destroyed.
3591
 *
3592
 * @since 2.2.0
3593
 */
3594
function wp_ob_end_flush_all() {
3595
	$levels = ob_get_level();
3596
	for ($i=0; $i<$levels; $i++)
3597
		ob_end_flush();
3598
}
3599
3600
/**
3601
 * Load custom DB error or display WordPress DB error.
3602
 *
3603
 * If a file exists in the wp-content directory named db-error.php, then it will
3604
 * be loaded instead of displaying the WordPress DB error. If it is not found,
3605
 * then the WordPress DB error will be displayed instead.
3606
 *
3607
 * The WordPress DB error sets the HTTP status header to 500 to try to prevent
3608
 * search engines from caching the message. Custom DB messages should do the
3609
 * same.
3610
 *
3611
 * This function was backported to WordPress 2.3.2, but originally was added
3612
 * in WordPress 2.5.0.
3613
 *
3614
 * @since 2.3.2
3615
 *
3616
 * @global wpdb $wpdb WordPress database abstraction object.
3617
 */
3618
function dead_db() {
3619
	global $wpdb;
3620
3621
	wp_load_translations_early();
3622
3623
	// Load custom DB error template, if present.
3624
	if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
3625
		require_once( WP_CONTENT_DIR . '/db-error.php' );
3626
		die();
3627
	}
3628
3629
	// If installing or in the admin, provide the verbose message.
3630
	if ( wp_installing() || defined( 'WP_ADMIN' ) )
3631
		wp_die($wpdb->error);
3632
3633
	// Otherwise, be terse.
3634
	status_header( 500 );
3635
	nocache_headers();
3636
	header( 'Content-Type: text/html; charset=utf-8' );
3637
?>
3638
<!DOCTYPE html>
3639
<html xmlns="http://www.w3.org/1999/xhtml"<?php if ( is_rtl() ) echo ' dir="rtl"'; ?>>
3640
<head>
3641
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
3642
	<title><?php _e( 'Database Error' ); ?></title>
3643
3644
</head>
3645
<body>
3646
	<h1><?php _e( 'Error establishing a database connection' ); ?></h1>
3647
</body>
3648
</html>
3649
<?php
3650
	die();
3651
}
3652
3653
/**
3654
 * Convert a value to non-negative integer.
3655
 *
3656
 * @since 2.5.0
3657
 *
3658
 * @param mixed $maybeint Data you wish to have converted to a non-negative integer.
3659
 * @return int A non-negative integer.
3660
 */
3661
function absint( $maybeint ) {
3662
	return abs( intval( $maybeint ) );
3663
}
3664
3665
/**
3666
 * Mark a function as deprecated and inform when it has been used.
3667
 *
3668
 * There is a {@see 'hook deprecated_function_run'} that will be called that can be used
3669
 * to get the backtrace up to what file and function called the deprecated
3670
 * function.
3671
 *
3672
 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
3673
 *
3674
 * This function is to be used in every function that is deprecated.
3675
 *
3676
 * @since 2.5.0
3677
 * @access private
3678
 *
3679
 * @param string $function    The function that was called.
3680
 * @param string $version     The version of WordPress that deprecated the function.
3681
 * @param string $replacement Optional. The function that should have been called. Default null.
3682
 */
3683 View Code Duplication
function _deprecated_function( $function, $version, $replacement = null ) {
0 ignored issues
show
Duplication introduced by
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...
3684
3685
	/**
3686
	 * Fires when a deprecated function is called.
3687
	 *
3688
	 * @since 2.5.0
3689
	 *
3690
	 * @param string $function    The function that was called.
3691
	 * @param string $replacement The function that should have been called.
3692
	 * @param string $version     The version of WordPress that deprecated the function.
3693
	 */
3694
	do_action( 'deprecated_function_run', $function, $replacement, $version );
3695
3696
	/**
3697
	 * Filters whether to trigger an error for deprecated functions.
3698
	 *
3699
	 * @since 2.5.0
3700
	 *
3701
	 * @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
3702
	 */
3703
	if ( WP_DEBUG && apply_filters( 'deprecated_function_trigger_error', true ) ) {
3704
		if ( function_exists( '__' ) ) {
3705
			if ( ! is_null( $replacement ) )
3706
				trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.'), $function, $version, $replacement ) );
3707
			else
3708
				trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.'), $function, $version ) );
3709
		} else {
3710
			if ( ! is_null( $replacement ) )
3711
				trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.', $function, $version, $replacement ) );
3712
			else
3713
				trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.', $function, $version ) );
3714
		}
3715
	}
3716
}
3717
3718
/**
3719
 * Marks a constructor as deprecated and informs when it has been used.
3720
 *
3721
 * Similar to _deprecated_function(), but with different strings. Used to
3722
 * remove PHP4 style constructors.
3723
 *
3724
 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
3725
 *
3726
 * This function is to be used in every PHP4 style constructor method that is deprecated.
3727
 *
3728
 * @since 4.3.0
3729
 * @since 4.5.0 Added the `$parent_class` parameter.
3730
 *
3731
 * @access private
3732
 *
3733
 * @param string $class        The class containing the deprecated constructor.
3734
 * @param string $version      The version of WordPress that deprecated the function.
3735
 * @param string $parent_class Optional. The parent class calling the deprecated constructor.
3736
 *                             Default empty string.
3737
 */
3738
function _deprecated_constructor( $class, $version, $parent_class = '' ) {
3739
3740
	/**
3741
	 * Fires when a deprecated constructor is called.
3742
	 *
3743
	 * @since 4.3.0
3744
	 * @since 4.5.0 Added the `$parent_class` parameter.
3745
	 *
3746
	 * @param string $class        The class containing the deprecated constructor.
3747
	 * @param string $version      The version of WordPress that deprecated the function.
3748
	 * @param string $parent_class The parent class calling the deprecated constructor.
3749
	 */
3750
	do_action( 'deprecated_constructor_run', $class, $version, $parent_class );
3751
3752
	/**
3753
	 * Filters whether to trigger an error for deprecated functions.
3754
	 *
3755
	 * `WP_DEBUG` must be true in addition to the filter evaluating to true.
3756
	 *
3757
	 * @since 4.3.0
3758
	 *
3759
	 * @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
3760
	 */
3761
	if ( WP_DEBUG && apply_filters( 'deprecated_constructor_trigger_error', true ) ) {
3762
		if ( function_exists( '__' ) ) {
3763 View Code Duplication
			if ( ! empty( $parent_class ) ) {
3764
				/* translators: 1: PHP class name, 2: PHP parent class name, 3: version number, 4: __construct() method */
3765
				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.' ),
3766
					$class, $parent_class, $version, '<pre>__construct()</pre>' ) );
3767
			} else {
3768
				/* translators: 1: PHP class name, 2: version number, 3: __construct() method */
3769
				trigger_error( sprintf( __( 'The called constructor method for %1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ),
3770
					$class, $version, '<pre>__construct()</pre>' ) );
3771
			}
3772 View Code Duplication
		} else {
3773
			if ( ! empty( $parent_class ) ) {
3774
				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.',
3775
					$class, $parent_class, $version, '<pre>__construct()</pre>' ) );
3776
			} else {
3777
				trigger_error( sprintf( 'The called constructor method for %1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.',
3778
					$class, $version, '<pre>__construct()</pre>' ) );
3779
			}
3780
		}
3781
	}
3782
3783
}
3784
3785
/**
3786
 * Mark a file as deprecated and inform when it has been used.
3787
 *
3788
 * There is a hook {@see 'deprecated_file_included'} that will be called that can be used
3789
 * to get the backtrace up to what file and function included the deprecated
3790
 * file.
3791
 *
3792
 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
3793
 *
3794
 * This function is to be used in every file that is deprecated.
3795
 *
3796
 * @since 2.5.0
3797
 * @access private
3798
 *
3799
 * @param string $file        The file that was included.
3800
 * @param string $version     The version of WordPress that deprecated the file.
3801
 * @param string $replacement Optional. The file that should have been included based on ABSPATH.
3802
 *                            Default null.
3803
 * @param string $message     Optional. A message regarding the change. Default empty.
3804
 */
3805
function _deprecated_file( $file, $version, $replacement = null, $message = '' ) {
3806
3807
	/**
3808
	 * Fires when a deprecated file is called.
3809
	 *
3810
	 * @since 2.5.0
3811
	 *
3812
	 * @param string $file        The file that was called.
3813
	 * @param string $replacement The file that should have been included based on ABSPATH.
3814
	 * @param string $version     The version of WordPress that deprecated the file.
3815
	 * @param string $message     A message regarding the change.
3816
	 */
3817
	do_action( 'deprecated_file_included', $file, $replacement, $version, $message );
3818
3819
	/**
3820
	 * Filters whether to trigger an error for deprecated files.
3821
	 *
3822
	 * @since 2.5.0
3823
	 *
3824
	 * @param bool $trigger Whether to trigger the error for deprecated files. Default true.
3825
	 */
3826
	if ( WP_DEBUG && apply_filters( 'deprecated_file_trigger_error', true ) ) {
3827
		$message = empty( $message ) ? '' : ' ' . $message;
3828
		if ( function_exists( '__' ) ) {
3829
			if ( ! is_null( $replacement ) )
3830
				trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.'), $file, $version, $replacement ) . $message );
3831
			else
3832
				trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.'), $file, $version ) . $message );
3833
		} else {
3834
			if ( ! is_null( $replacement ) )
3835
				trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.', $file, $version, $replacement ) . $message );
3836
			else
3837
				trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.', $file, $version ) . $message );
3838
		}
3839
	}
3840
}
3841
/**
3842
 * Mark a function argument as deprecated and inform when it has been used.
3843
 *
3844
 * This function is to be used whenever a deprecated function argument is used.
3845
 * Before this function is called, the argument must be checked for whether it was
3846
 * used by comparing it to its default value or evaluating whether it is empty.
3847
 * For example:
3848
 *
3849
 *     if ( ! empty( $deprecated ) ) {
3850
 *         _deprecated_argument( __FUNCTION__, '3.0.0' );
3851
 *     }
3852
 *
3853
 *
3854
 * There is a hook deprecated_argument_run that will be called that can be used
3855
 * to get the backtrace up to what file and function used the deprecated
3856
 * argument.
3857
 *
3858
 * The current behavior is to trigger a user error if WP_DEBUG is true.
3859
 *
3860
 * @since 3.0.0
3861
 * @access private
3862
 *
3863
 * @param string $function The function that was called.
3864
 * @param string $version  The version of WordPress that deprecated the argument used.
3865
 * @param string $message  Optional. A message regarding the change. Default null.
3866
 */
3867 View Code Duplication
function _deprecated_argument( $function, $version, $message = null ) {
0 ignored issues
show
Duplication introduced by
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...
3868
3869
	/**
3870
	 * Fires when a deprecated argument is called.
3871
	 *
3872
	 * @since 3.0.0
3873
	 *
3874
	 * @param string $function The function that was called.
3875
	 * @param string $message  A message regarding the change.
3876
	 * @param string $version  The version of WordPress that deprecated the argument used.
3877
	 */
3878
	do_action( 'deprecated_argument_run', $function, $message, $version );
3879
3880
	/**
3881
	 * Filters whether to trigger an error for deprecated arguments.
3882
	 *
3883
	 * @since 3.0.0
3884
	 *
3885
	 * @param bool $trigger Whether to trigger the error for deprecated arguments. Default true.
3886
	 */
3887
	if ( WP_DEBUG && apply_filters( 'deprecated_argument_trigger_error', true ) ) {
3888
		if ( function_exists( '__' ) ) {
3889
			if ( ! is_null( $message ) )
3890
				trigger_error( sprintf( __('%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s'), $function, $version, $message ) );
3891
			else
3892
				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 ) );
3893
		} else {
3894
			if ( ! is_null( $message ) )
3895
				trigger_error( sprintf( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s', $function, $version, $message ) );
3896
			else
3897
				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 ) );
3898
		}
3899
	}
3900
}
3901
3902
/**
3903
 * Marks a deprecated action or filter hook as deprecated and throws a notice.
3904
 *
3905
 * Use the {@see 'deprecated_hook_run'} action to get the backtrace describing where
3906
 * the deprecated hook was called.
3907
 *
3908
 * Default behavior is to trigger a user error if `WP_DEBUG` is true.
3909
 *
3910
 * This function is called by the do_action_deprecated() and apply_filters_deprecated()
3911
 * functions, and so generally does not need to be called directly.
3912
 *
3913
 * @since 4.6.0
3914
 * @access private
3915
 *
3916
 * @param string $hook        The hook that was used.
3917
 * @param string $version     The version of WordPress that deprecated the hook.
3918
 * @param string $replacement Optional. The hook that should have been used.
3919
 * @param string $message     Optional. A message regarding the change.
3920
 */
3921
function _deprecated_hook( $hook, $version, $replacement = null, $message = null ) {
3922
	/**
3923
	 * Fires when a deprecated hook is called.
3924
	 *
3925
	 * @since 4.6.0
3926
	 *
3927
	 * @param string $hook        The hook that was called.
3928
	 * @param string $replacement The hook that should be used as a replacement.
3929
	 * @param string $version     The version of WordPress that deprecated the argument used.
3930
	 * @param string $message     A message regarding the change.
3931
	 */
3932
	do_action( 'deprecated_hook_run', $hook, $replacement, $version, $message );
3933
3934
	/**
3935
	 * Filters whether to trigger deprecated hook errors.
3936
	 *
3937
	 * @since 4.6.0
3938
	 *
3939
	 * @param bool $trigger Whether to trigger deprecated hook errors. Requires
3940
	 *                      `WP_DEBUG` to be defined true.
3941
	 */
3942
	if ( WP_DEBUG && apply_filters( 'deprecated_hook_trigger_error', true ) ) {
3943
		$message = empty( $message ) ? '' : ' ' . $message;
3944
		if ( ! is_null( $replacement ) ) {
3945
			trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.' ), $hook, $version, $replacement ) . $message );
3946
		} else {
3947
			trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.' ), $hook, $version ) . $message );
3948
		}
3949
	}
3950
}
3951
3952
/**
3953
 * Mark something as being incorrectly called.
3954
 *
3955
 * There is a hook {@see 'doing_it_wrong_run'} that will be called that can be used
3956
 * to get the backtrace up to what file and function called the deprecated
3957
 * function.
3958
 *
3959
 * The current behavior is to trigger a user error if `WP_DEBUG` is true.
3960
 *
3961
 * @since 3.1.0
3962
 * @access private
3963
 *
3964
 * @param string $function The function that was called.
3965
 * @param string $message  A message explaining what has been done incorrectly.
3966
 * @param string $version  The version of WordPress where the message was added.
3967
 */
3968
function _doing_it_wrong( $function, $message, $version ) {
3969
3970
	/**
3971
	 * Fires when the given function is being used incorrectly.
3972
	 *
3973
	 * @since 3.1.0
3974
	 *
3975
	 * @param string $function The function that was called.
3976
	 * @param string $message  A message explaining what has been done incorrectly.
3977
	 * @param string $version  The version of WordPress where the message was added.
3978
	 */
3979
	do_action( 'doing_it_wrong_run', $function, $message, $version );
3980
3981
	/**
3982
	 * Filters whether to trigger an error for _doing_it_wrong() calls.
3983
	 *
3984
	 * @since 3.1.0
3985
	 *
3986
	 * @param bool $trigger Whether to trigger the error for _doing_it_wrong() calls. Default true.
3987
	 */
3988
	if ( WP_DEBUG && apply_filters( 'doing_it_wrong_trigger_error', true ) ) {
3989
		if ( function_exists( '__' ) ) {
3990
			$version = is_null( $version ) ? '' : sprintf( __( '(This message was added in version %s.)' ), $version );
3991
			/* translators: %s: Codex URL */
3992
			$message .= ' ' . sprintf( __( 'Please see <a href="%s">Debugging in WordPress</a> for more information.' ),
3993
				__( 'https://codex.wordpress.org/Debugging_in_WordPress' )
3994
			);
3995
			trigger_error( sprintf( __( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s' ), $function, $message, $version ) );
3996
		} else {
3997
			$version = is_null( $version ) ? '' : sprintf( '(This message was added in version %s.)', $version );
3998
			$message .= sprintf( ' Please see <a href="%s">Debugging in WordPress</a> for more information.',
3999
				'https://codex.wordpress.org/Debugging_in_WordPress'
4000
			);
4001
			trigger_error( sprintf( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s', $function, $message, $version ) );
4002
		}
4003
	}
4004
}
4005
4006
/**
4007
 * Is the server running earlier than 1.5.0 version of lighttpd?
4008
 *
4009
 * @since 2.5.0
4010
 *
4011
 * @return bool Whether the server is running lighttpd < 1.5.0.
4012
 */
4013
function is_lighttpd_before_150() {
4014
	$server_parts = explode( '/', isset( $_SERVER['SERVER_SOFTWARE'] )? $_SERVER['SERVER_SOFTWARE'] : '' );
4015
	$server_parts[1] = isset( $server_parts[1] )? $server_parts[1] : '';
4016
	return  'lighttpd' == $server_parts[0] && -1 == version_compare( $server_parts[1], '1.5.0' );
4017
}
4018
4019
/**
4020
 * Does the specified module exist in the Apache config?
4021
 *
4022
 * @since 2.5.0
4023
 *
4024
 * @global bool $is_apache
4025
 *
4026
 * @param string $mod     The module, e.g. mod_rewrite.
4027
 * @param bool   $default Optional. The default return value if the module is not found. Default false.
4028
 * @return bool Whether the specified module is loaded.
4029
 */
4030
function apache_mod_loaded($mod, $default = false) {
4031
	global $is_apache;
4032
4033
	if ( !$is_apache )
4034
		return false;
4035
4036
	if ( function_exists( 'apache_get_modules' ) ) {
4037
		$mods = apache_get_modules();
4038
		if ( in_array($mod, $mods) )
4039
			return true;
4040
	} elseif ( function_exists( 'phpinfo' ) && false === strpos( ini_get( 'disable_functions' ), 'phpinfo' ) ) {
4041
			ob_start();
4042
			phpinfo(8);
4043
			$phpinfo = ob_get_clean();
4044
			if ( false !== strpos($phpinfo, $mod) )
4045
				return true;
4046
	}
4047
	return $default;
4048
}
4049
4050
/**
4051
 * Check if IIS 7+ supports pretty permalinks.
4052
 *
4053
 * @since 2.8.0
4054
 *
4055
 * @global bool $is_iis7
4056
 *
4057
 * @return bool Whether IIS7 supports permalinks.
4058
 */
4059
function iis7_supports_permalinks() {
4060
	global $is_iis7;
4061
4062
	$supports_permalinks = false;
4063
	if ( $is_iis7 ) {
4064
		/* First we check if the DOMDocument class exists. If it does not exist, then we cannot
4065
		 * easily update the xml configuration file, hence we just bail out and tell user that
4066
		 * pretty permalinks cannot be used.
4067
		 *
4068
		 * Next we check if the URL Rewrite Module 1.1 is loaded and enabled for the web site. When
4069
		 * URL Rewrite 1.1 is loaded it always sets a server variable called 'IIS_UrlRewriteModule'.
4070
		 * Lastly we make sure that PHP is running via FastCGI. This is important because if it runs
4071
		 * via ISAPI then pretty permalinks will not work.
4072
		 */
4073
		$supports_permalinks = class_exists( 'DOMDocument', false ) && isset($_SERVER['IIS_UrlRewriteModule']) && ( PHP_SAPI == 'cgi-fcgi' );
4074
	}
4075
4076
	/**
4077
	 * Filters whether IIS 7+ supports pretty permalinks.
4078
	 *
4079
	 * @since 2.8.0
4080
	 *
4081
	 * @param bool $supports_permalinks Whether IIS7 supports permalinks. Default false.
4082
	 */
4083
	return apply_filters( 'iis7_supports_permalinks', $supports_permalinks );
4084
}
4085
4086
/**
4087
 * File validates against allowed set of defined rules.
4088
 *
4089
 * A return value of '1' means that the $file contains either '..' or './'. A
4090
 * return value of '2' means that the $file contains ':' after the first
4091
 * character. A return value of '3' means that the file is not in the allowed
4092
 * files list.
4093
 *
4094
 * @since 1.2.0
4095
 *
4096
 * @param string $file File path.
4097
 * @param array  $allowed_files List of allowed files.
4098
 * @return int 0 means nothing is wrong, greater than 0 means something was wrong.
4099
 */
4100
function validate_file( $file, $allowed_files = '' ) {
4101
	if ( false !== strpos( $file, '..' ) )
4102
		return 1;
4103
4104
	if ( false !== strpos( $file, './' ) )
4105
		return 1;
4106
4107
	if ( ! empty( $allowed_files ) && ! in_array( $file, $allowed_files ) )
4108
		return 3;
4109
4110
	if (':' == substr( $file, 1, 1 ) )
4111
		return 2;
4112
4113
	return 0;
4114
}
4115
4116
/**
4117
 * Whether to force SSL used for the Administration Screens.
4118
 *
4119
 * @since 2.6.0
4120
 *
4121
 * @staticvar bool $forced
4122
 *
4123
 * @param string|bool $force Optional. Whether to force SSL in admin screens. Default null.
4124
 * @return bool True if forced, false if not forced.
4125
 */
4126
function force_ssl_admin( $force = null ) {
4127
	static $forced = false;
4128
4129
	if ( !is_null( $force ) ) {
4130
		$old_forced = $forced;
4131
		$forced = $force;
4132
		return $old_forced;
4133
	}
4134
4135
	return $forced;
4136
}
4137
4138
/**
4139
 * Guess the URL for the site.
4140
 *
4141
 * Will remove wp-admin links to retrieve only return URLs not in the wp-admin
4142
 * directory.
4143
 *
4144
 * @since 2.6.0
4145
 *
4146
 * @return string The guessed URL.
4147
 */
4148
function wp_guess_url() {
4149
	if ( defined('WP_SITEURL') && '' != WP_SITEURL ) {
4150
		$url = WP_SITEURL;
4151
	} else {
4152
		$abspath_fix = str_replace( '\\', '/', ABSPATH );
4153
		$script_filename_dir = dirname( $_SERVER['SCRIPT_FILENAME'] );
4154
4155
		// The request is for the admin
4156
		if ( strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) !== false || strpos( $_SERVER['REQUEST_URI'], 'wp-login.php' ) !== false ) {
4157
			$path = preg_replace( '#/(wp-admin/.*|wp-login.php)#i', '', $_SERVER['REQUEST_URI'] );
4158
4159
		// The request is for a file in ABSPATH
4160
		} elseif ( $script_filename_dir . '/' == $abspath_fix ) {
4161
			// Strip off any file/query params in the path
4162
			$path = preg_replace( '#/[^/]*$#i', '', $_SERVER['PHP_SELF'] );
4163
4164
		} else {
4165
			if ( false !== strpos( $_SERVER['SCRIPT_FILENAME'], $abspath_fix ) ) {
4166
				// Request is hitting a file inside ABSPATH
4167
				$directory = str_replace( ABSPATH, '', $script_filename_dir );
4168
				// Strip off the sub directory, and any file/query params
4169
				$path = preg_replace( '#/' . preg_quote( $directory, '#' ) . '/[^/]*$#i', '' , $_SERVER['REQUEST_URI'] );
4170
			} elseif ( false !== strpos( $abspath_fix, $script_filename_dir ) ) {
4171
				// Request is hitting a file above ABSPATH
4172
				$subdirectory = substr( $abspath_fix, strpos( $abspath_fix, $script_filename_dir ) + strlen( $script_filename_dir ) );
4173
				// Strip off any file/query params from the path, appending the sub directory to the install
4174
				$path = preg_replace( '#/[^/]*$#i', '' , $_SERVER['REQUEST_URI'] ) . $subdirectory;
4175
			} else {
4176
				$path = $_SERVER['REQUEST_URI'];
4177
			}
4178
		}
4179
4180
		$schema = is_ssl() ? 'https://' : 'http://'; // set_url_scheme() is not defined yet
4181
		$url = $schema . $_SERVER['HTTP_HOST'] . $path;
4182
	}
4183
4184
	return rtrim($url, '/');
4185
}
4186
4187
/**
4188
 * Temporarily suspend cache additions.
4189
 *
4190
 * Stops more data being added to the cache, but still allows cache retrieval.
4191
 * This is useful for actions, such as imports, when a lot of data would otherwise
4192
 * be almost uselessly added to the cache.
4193
 *
4194
 * Suspension lasts for a single page load at most. Remember to call this
4195
 * function again if you wish to re-enable cache adds earlier.
4196
 *
4197
 * @since 3.3.0
4198
 *
4199
 * @staticvar bool $_suspend
4200
 *
4201
 * @param bool $suspend Optional. Suspends additions if true, re-enables them if false.
4202
 * @return bool The current suspend setting
4203
 */
4204
function wp_suspend_cache_addition( $suspend = null ) {
4205
	static $_suspend = false;
4206
4207
	if ( is_bool( $suspend ) )
4208
		$_suspend = $suspend;
4209
4210
	return $_suspend;
4211
}
4212
4213
/**
4214
 * Suspend cache invalidation.
4215
 *
4216
 * Turns cache invalidation on and off. Useful during imports where you don't wont to do
4217
 * invalidations every time a post is inserted. Callers must be sure that what they are
4218
 * doing won't lead to an inconsistent cache when invalidation is suspended.
4219
 *
4220
 * @since 2.7.0
4221
 *
4222
 * @global bool $_wp_suspend_cache_invalidation
4223
 *
4224
 * @param bool $suspend Optional. Whether to suspend or enable cache invalidation. Default true.
4225
 * @return bool The current suspend setting.
4226
 */
4227
function wp_suspend_cache_invalidation( $suspend = true ) {
4228
	global $_wp_suspend_cache_invalidation;
4229
4230
	$current_suspend = $_wp_suspend_cache_invalidation;
4231
	$_wp_suspend_cache_invalidation = $suspend;
4232
	return $current_suspend;
4233
}
4234
4235
/**
4236
 * Determine whether a site is the main site of the current network.
4237
 *
4238
 * @since 3.0.0
4239
 *
4240
 * @global object $current_site
4241
 *
4242
 * @param int $site_id Optional. Site ID to test. Defaults to current site.
4243
 * @return bool True if $site_id is the main site of the network, or if not
4244
 *              running Multisite.
4245
 */
4246
function is_main_site( $site_id = null ) {
4247
	// This is the current network's information; 'site' is old terminology.
4248
	global $current_site;
4249
4250
	if ( ! is_multisite() )
4251
		return true;
4252
4253
	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...
4254
		$site_id = get_current_blog_id();
4255
4256
	return (int) $site_id === (int) $current_site->blog_id;
4257
}
4258
4259
/**
4260
 * Determine whether a network is the main network of the Multisite install.
4261
 *
4262
 * @since 3.7.0
4263
 *
4264
 * @param int $network_id Optional. Network ID to test. Defaults to current network.
4265
 * @return bool True if $network_id is the main network, or if not running Multisite.
4266
 */
4267
function is_main_network( $network_id = null ) {
4268
	if ( ! is_multisite() ) {
4269
		return true;
4270
	}
4271
4272
	$current_network_id = (int) get_current_site()->id;
4273
4274
	if ( null === $network_id ) {
4275
		$network_id = $current_network_id;
4276
	}
4277
4278
	$network_id = (int) $network_id;
4279
4280
	return ( $network_id === get_main_network_id() );
4281
}
4282
4283
/**
4284
 * Get the main network ID.
4285
 *
4286
 * @since 4.3.0
4287
 *
4288
 * @global wpdb $wpdb WordPress database abstraction object.
4289
 *
4290
 * @return int The ID of the main network.
4291
 */
4292
function get_main_network_id() {
4293
	global $wpdb;
4294
4295
	if ( ! is_multisite() ) {
4296
		return 1;
4297
	}
4298
4299
	$current_site = get_current_site();
4300
4301
	if ( defined( 'PRIMARY_NETWORK_ID' ) ) {
4302
		$main_network_id = PRIMARY_NETWORK_ID;
4303
	} elseif ( isset( $current_site->id ) && 1 === (int) $current_site->id ) {
4304
		// If the current network has an ID of 1, assume it is the main network.
4305
		$main_network_id = 1;
4306 View Code Duplication
	} else {
4307
		$main_network_id = wp_cache_get( 'primary_network_id', 'site-options' );
4308
4309
		if ( false === $main_network_id ) {
4310
			$main_network_id = (int) $wpdb->get_var( "SELECT id FROM {$wpdb->site} ORDER BY id LIMIT 1" );
4311
			wp_cache_add( 'primary_network_id', $main_network_id, 'site-options' );
4312
		}
4313
	}
4314
4315
	/**
4316
	 * Filters the main network ID.
4317
	 *
4318
	 * @since 4.3.0
4319
	 *
4320
	 * @param int $main_network_id The ID of the main network.
4321
	 */
4322
	return (int) apply_filters( 'get_main_network_id', $main_network_id );
4323
}
4324
4325
/**
4326
 * Determine whether global terms are enabled.
4327
 *
4328
 * @since 3.0.0
4329
 *
4330
 * @staticvar bool $global_terms
4331
 *
4332
 * @return bool True if multisite and global terms enabled.
4333
 */
4334
function global_terms_enabled() {
4335
	if ( ! is_multisite() )
4336
		return false;
4337
4338
	static $global_terms = null;
4339
	if ( is_null( $global_terms ) ) {
4340
4341
		/**
4342
		 * Filters whether global terms are enabled.
4343
		 *
4344
		 * Passing a non-null value to the filter will effectively short-circuit the function,
4345
		 * returning the value of the 'global_terms_enabled' site option instead.
4346
		 *
4347
		 * @since 3.0.0
4348
		 *
4349
		 * @param null $enabled Whether global terms are enabled.
4350
		 */
4351
		$filter = apply_filters( 'global_terms_enabled', null );
4352
		if ( ! is_null( $filter ) )
4353
			$global_terms = (bool) $filter;
4354
		else
4355
			$global_terms = (bool) get_site_option( 'global_terms_enabled', false );
4356
	}
4357
	return $global_terms;
4358
}
4359
4360
/**
4361
 * gmt_offset modification for smart timezone handling.
4362
 *
4363
 * Overrides the gmt_offset option if we have a timezone_string available.
4364
 *
4365
 * @since 2.8.0
4366
 *
4367
 * @return float|false Timezone GMT offset, false otherwise.
4368
 */
4369
function wp_timezone_override_offset() {
4370
	if ( !$timezone_string = get_option( 'timezone_string' ) ) {
4371
		return false;
4372
	}
4373
4374
	$timezone_object = timezone_open( $timezone_string );
4375
	$datetime_object = date_create();
4376
	if ( false === $timezone_object || false === $datetime_object ) {
4377
		return false;
4378
	}
4379
	return round( timezone_offset_get( $timezone_object, $datetime_object ) / HOUR_IN_SECONDS, 2 );
4380
}
4381
4382
/**
4383
 * Sort-helper for timezones.
4384
 *
4385
 * @since 2.9.0
4386
 * @access private
4387
 *
4388
 * @param array $a
4389
 * @param array $b
4390
 * @return int
4391
 */
4392
function _wp_timezone_choice_usort_callback( $a, $b ) {
4393
	// Don't use translated versions of Etc
4394
	if ( 'Etc' === $a['continent'] && 'Etc' === $b['continent'] ) {
4395
		// Make the order of these more like the old dropdown
4396
		if ( 'GMT+' === substr( $a['city'], 0, 4 ) && 'GMT+' === substr( $b['city'], 0, 4 ) ) {
4397
			return -1 * ( strnatcasecmp( $a['city'], $b['city'] ) );
4398
		}
4399 View Code Duplication
		if ( 'UTC' === $a['city'] ) {
4400
			if ( 'GMT+' === substr( $b['city'], 0, 4 ) ) {
4401
				return 1;
4402
			}
4403
			return -1;
4404
		}
4405 View Code Duplication
		if ( 'UTC' === $b['city'] ) {
4406
			if ( 'GMT+' === substr( $a['city'], 0, 4 ) ) {
4407
				return -1;
4408
			}
4409
			return 1;
4410
		}
4411
		return strnatcasecmp( $a['city'], $b['city'] );
4412
	}
4413
	if ( $a['t_continent'] == $b['t_continent'] ) {
4414
		if ( $a['t_city'] == $b['t_city'] ) {
4415
			return strnatcasecmp( $a['t_subcity'], $b['t_subcity'] );
4416
		}
4417
		return strnatcasecmp( $a['t_city'], $b['t_city'] );
4418
	} else {
4419
		// Force Etc to the bottom of the list
4420
		if ( 'Etc' === $a['continent'] ) {
4421
			return 1;
4422
		}
4423
		if ( 'Etc' === $b['continent'] ) {
4424
			return -1;
4425
		}
4426
		return strnatcasecmp( $a['t_continent'], $b['t_continent'] );
4427
	}
4428
}
4429
4430
/**
4431
 * Gives a nicely-formatted list of timezone strings.
4432
 *
4433
 * @since 2.9.0
4434
 *
4435
 * @staticvar bool $mo_loaded
4436
 *
4437
 * @param string $selected_zone Selected timezone.
4438
 * @return string
4439
 */
4440
function wp_timezone_choice( $selected_zone ) {
4441
	static $mo_loaded = false;
4442
4443
	$continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific');
4444
4445
	// Load translations for continents and cities
4446
	if ( !$mo_loaded ) {
4447
		$locale = get_locale();
4448
		$mofile = WP_LANG_DIR . '/continents-cities-' . $locale . '.mo';
4449
		load_textdomain( 'continents-cities', $mofile );
4450
		$mo_loaded = true;
4451
	}
4452
4453
	$zonen = array();
4454
	foreach ( timezone_identifiers_list() as $zone ) {
4455
		$zone = explode( '/', $zone );
4456
		if ( !in_array( $zone[0], $continents ) ) {
4457
			continue;
4458
		}
4459
4460
		// This determines what gets set and translated - we don't translate Etc/* strings here, they are done later
4461
		$exists = array(
4462
			0 => ( isset( $zone[0] ) && $zone[0] ),
4463
			1 => ( isset( $zone[1] ) && $zone[1] ),
4464
			2 => ( isset( $zone[2] ) && $zone[2] ),
4465
		);
4466
		$exists[3] = ( $exists[0] && 'Etc' !== $zone[0] );
4467
		$exists[4] = ( $exists[1] && $exists[3] );
4468
		$exists[5] = ( $exists[2] && $exists[3] );
4469
4470
		$zonen[] = array(
4471
			'continent'   => ( $exists[0] ? $zone[0] : '' ),
4472
			'city'        => ( $exists[1] ? $zone[1] : '' ),
4473
			'subcity'     => ( $exists[2] ? $zone[2] : '' ),
4474
			't_continent' => ( $exists[3] ? translate( str_replace( '_', ' ', $zone[0] ), 'continents-cities' ) : '' ),
4475
			't_city'      => ( $exists[4] ? translate( str_replace( '_', ' ', $zone[1] ), 'continents-cities' ) : '' ),
4476
			't_subcity'   => ( $exists[5] ? translate( str_replace( '_', ' ', $zone[2] ), 'continents-cities' ) : '' )
4477
		);
4478
	}
4479
	usort( $zonen, '_wp_timezone_choice_usort_callback' );
4480
4481
	$structure = array();
4482
4483
	if ( empty( $selected_zone ) ) {
4484
		$structure[] = '<option selected="selected" value="">' . __( 'Select a city' ) . '</option>';
4485
	}
4486
4487
	foreach ( $zonen as $key => $zone ) {
4488
		// Build value in an array to join later
4489
		$value = array( $zone['continent'] );
4490
4491
		if ( empty( $zone['city'] ) ) {
4492
			// It's at the continent level (generally won't happen)
4493
			$display = $zone['t_continent'];
4494
		} else {
4495
			// It's inside a continent group
4496
4497
			// Continent optgroup
4498
			if ( !isset( $zonen[$key - 1] ) || $zonen[$key - 1]['continent'] !== $zone['continent'] ) {
4499
				$label = $zone['t_continent'];
4500
				$structure[] = '<optgroup label="'. esc_attr( $label ) .'">';
4501
			}
4502
4503
			// Add the city to the value
4504
			$value[] = $zone['city'];
4505
4506
			$display = $zone['t_city'];
4507
			if ( !empty( $zone['subcity'] ) ) {
4508
				// Add the subcity to the value
4509
				$value[] = $zone['subcity'];
4510
				$display .= ' - ' . $zone['t_subcity'];
4511
			}
4512
		}
4513
4514
		// Build the value
4515
		$value = join( '/', $value );
4516
		$selected = '';
4517
		if ( $value === $selected_zone ) {
4518
			$selected = 'selected="selected" ';
4519
		}
4520
		$structure[] = '<option ' . $selected . 'value="' . esc_attr( $value ) . '">' . esc_html( $display ) . "</option>";
4521
4522
		// Close continent optgroup
4523
		if ( !empty( $zone['city'] ) && ( !isset($zonen[$key + 1]) || (isset( $zonen[$key + 1] ) && $zonen[$key + 1]['continent'] !== $zone['continent']) ) ) {
4524
			$structure[] = '</optgroup>';
4525
		}
4526
	}
4527
4528
	// Do UTC
4529
	$structure[] = '<optgroup label="'. esc_attr__( 'UTC' ) .'">';
4530
	$selected = '';
4531
	if ( 'UTC' === $selected_zone )
4532
		$selected = 'selected="selected" ';
4533
	$structure[] = '<option ' . $selected . 'value="' . esc_attr( 'UTC' ) . '">' . __('UTC') . '</option>';
4534
	$structure[] = '</optgroup>';
4535
4536
	// Do manual UTC offsets
4537
	$structure[] = '<optgroup label="'. esc_attr__( 'Manual Offsets' ) .'">';
4538
	$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,
4539
		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);
4540
	foreach ( $offset_range as $offset ) {
4541
		if ( 0 <= $offset )
4542
			$offset_name = '+' . $offset;
4543
		else
4544
			$offset_name = (string) $offset;
4545
4546
		$offset_value = $offset_name;
4547
		$offset_name = str_replace(array('.25','.5','.75'), array(':15',':30',':45'), $offset_name);
4548
		$offset_name = 'UTC' . $offset_name;
4549
		$offset_value = 'UTC' . $offset_value;
4550
		$selected = '';
4551
		if ( $offset_value === $selected_zone )
4552
			$selected = 'selected="selected" ';
4553
		$structure[] = '<option ' . $selected . 'value="' . esc_attr( $offset_value ) . '">' . esc_html( $offset_name ) . "</option>";
4554
4555
	}
4556
	$structure[] = '</optgroup>';
4557
4558
	return join( "\n", $structure );
4559
}
4560
4561
/**
4562
 * Strip close comment and close php tags from file headers used by WP.
4563
 *
4564
 * @since 2.8.0
4565
 * @access private
4566
 *
4567
 * @see https://core.trac.wordpress.org/ticket/8497
4568
 *
4569
 * @param string $str Header comment to clean up.
4570
 * @return string
4571
 */
4572
function _cleanup_header_comment( $str ) {
4573
	return trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $str));
4574
}
4575
4576
/**
4577
 * Permanently delete comments or posts of any type that have held a status
4578
 * of 'trash' for the number of days defined in EMPTY_TRASH_DAYS.
4579
 *
4580
 * The default value of `EMPTY_TRASH_DAYS` is 30 (days).
4581
 *
4582
 * @since 2.9.0
4583
 *
4584
 * @global wpdb $wpdb WordPress database abstraction object.
4585
 */
4586
function wp_scheduled_delete() {
4587
	global $wpdb;
4588
4589
	$delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
4590
4591
	$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);
4592
4593
	foreach ( (array) $posts_to_delete as $post ) {
4594
		$post_id = (int) $post['post_id'];
4595
		if ( !$post_id )
4596
			continue;
4597
4598
		$del_post = get_post($post_id);
4599
4600
		if ( !$del_post || 'trash' != $del_post->post_status ) {
4601
			delete_post_meta($post_id, '_wp_trash_meta_status');
4602
			delete_post_meta($post_id, '_wp_trash_meta_time');
4603
		} else {
4604
			wp_delete_post($post_id);
4605
		}
4606
	}
4607
4608
	$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);
4609
4610
	foreach ( (array) $comments_to_delete as $comment ) {
4611
		$comment_id = (int) $comment['comment_id'];
4612
		if ( !$comment_id )
4613
			continue;
4614
4615
		$del_comment = get_comment($comment_id);
4616
4617
		if ( !$del_comment || 'trash' != $del_comment->comment_approved ) {
4618
			delete_comment_meta($comment_id, '_wp_trash_meta_time');
4619
			delete_comment_meta($comment_id, '_wp_trash_meta_status');
4620
		} else {
4621
			wp_delete_comment( $del_comment );
0 ignored issues
show
Bug introduced by
It seems like $del_comment defined by get_comment($comment_id) on line 4615 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...
4622
		}
4623
	}
4624
}
4625
4626
/**
4627
 * Retrieve metadata from a file.
4628
 *
4629
 * Searches for metadata in the first 8kiB of a file, such as a plugin or theme.
4630
 * Each piece of metadata must be on its own line. Fields can not span multiple
4631
 * lines, the value will get cut at the end of the first line.
4632
 *
4633
 * If the file data is not within that first 8kiB, then the author should correct
4634
 * their plugin file and move the data headers to the top.
4635
 *
4636
 * @link https://codex.wordpress.org/File_Header
4637
 *
4638
 * @since 2.9.0
4639
 *
4640
 * @param string $file            Path to the file.
4641
 * @param array  $default_headers List of headers, in the format array('HeaderKey' => 'Header Name').
4642
 * @param string $context         Optional. If specified adds filter hook {@see 'extra_$context_headers'}.
4643
 *                                Default empty.
4644
 * @return array Array of file headers in `HeaderKey => Header Value` format.
4645
 */
4646
function get_file_data( $file, $default_headers, $context = '' ) {
4647
	// We don't need to write to the file, so just open for reading.
4648
	$fp = fopen( $file, 'r' );
4649
4650
	// Pull only the first 8kiB of the file in.
4651
	$file_data = fread( $fp, 8192 );
4652
4653
	// PHP will close file handle, but we are good citizens.
4654
	fclose( $fp );
4655
4656
	// Make sure we catch CR-only line endings.
4657
	$file_data = str_replace( "\r", "\n", $file_data );
4658
4659
	/**
4660
	 * Filters extra file headers by context.
4661
	 *
4662
	 * The dynamic portion of the hook name, `$context`, refers to
4663
	 * the context where extra headers might be loaded.
4664
	 *
4665
	 * @since 2.9.0
4666
	 *
4667
	 * @param array $extra_context_headers Empty array by default.
4668
	 */
4669
	if ( $context && $extra_headers = apply_filters( "extra_{$context}_headers", array() ) ) {
4670
		$extra_headers = array_combine( $extra_headers, $extra_headers ); // keys equal values
4671
		$all_headers = array_merge( $extra_headers, (array) $default_headers );
4672
	} else {
4673
		$all_headers = $default_headers;
4674
	}
4675
4676
	foreach ( $all_headers as $field => $regex ) {
4677
		if ( preg_match( '/^[ \t\/*#@]*' . preg_quote( $regex, '/' ) . ':(.*)$/mi', $file_data, $match ) && $match[1] )
4678
			$all_headers[ $field ] = _cleanup_header_comment( $match[1] );
4679
		else
4680
			$all_headers[ $field ] = '';
4681
	}
4682
4683
	return $all_headers;
4684
}
4685
4686
/**
4687
 * Returns true.
4688
 *
4689
 * Useful for returning true to filters easily.
4690
 *
4691
 * @since 3.0.0
4692
 *
4693
 * @see __return_false()
4694
 *
4695
 * @return true True.
4696
 */
4697
function __return_true() {
4698
	return true;
4699
}
4700
4701
/**
4702
 * Returns false.
4703
 *
4704
 * Useful for returning false to filters easily.
4705
 *
4706
 * @since 3.0.0
4707
 *
4708
 * @see __return_true()
4709
 *
4710
 * @return false False.
4711
 */
4712
function __return_false() {
4713
	return false;
4714
}
4715
4716
/**
4717
 * Returns 0.
4718
 *
4719
 * Useful for returning 0 to filters easily.
4720
 *
4721
 * @since 3.0.0
4722
 *
4723
 * @return int 0.
4724
 */
4725
function __return_zero() {
4726
	return 0;
4727
}
4728
4729
/**
4730
 * Returns an empty array.
4731
 *
4732
 * Useful for returning an empty array to filters easily.
4733
 *
4734
 * @since 3.0.0
4735
 *
4736
 * @return array Empty array.
4737
 */
4738
function __return_empty_array() {
4739
	return array();
4740
}
4741
4742
/**
4743
 * Returns null.
4744
 *
4745
 * Useful for returning null to filters easily.
4746
 *
4747
 * @since 3.4.0
4748
 *
4749
 * @return null Null value.
4750
 */
4751
function __return_null() {
4752
	return null;
4753
}
4754
4755
/**
4756
 * Returns an empty string.
4757
 *
4758
 * Useful for returning an empty string to filters easily.
4759
 *
4760
 * @since 3.7.0
4761
 *
4762
 * @see __return_null()
4763
 *
4764
 * @return string Empty string.
4765
 */
4766
function __return_empty_string() {
4767
	return '';
4768
}
4769
4770
/**
4771
 * Send a HTTP header to disable content type sniffing in browsers which support it.
4772
 *
4773
 * @since 3.0.0
4774
 *
4775
 * @see https://blogs.msdn.com/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx
4776
 * @see https://src.chromium.org/viewvc/chrome?view=rev&revision=6985
4777
 */
4778
function send_nosniff_header() {
4779
	@header( 'X-Content-Type-Options: nosniff' );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4780
}
4781
4782
/**
4783
 * Return a MySQL expression for selecting the week number based on the start_of_week option.
4784
 *
4785
 * @ignore
4786
 * @since 3.0.0
4787
 *
4788
 * @param string $column Database column.
4789
 * @return string SQL clause.
4790
 */
4791
function _wp_mysql_week( $column ) {
4792
	switch ( $start_of_week = (int) get_option( 'start_of_week' ) ) {
4793
	case 1 :
4794
		return "WEEK( $column, 1 )";
4795
	case 2 :
4796
	case 3 :
4797
	case 4 :
4798
	case 5 :
4799
	case 6 :
4800
		return "WEEK( DATE_SUB( $column, INTERVAL $start_of_week DAY ), 0 )";
4801
	case 0 :
4802
	default :
4803
		return "WEEK( $column, 0 )";
4804
	}
4805
}
4806
4807
/**
4808
 * Find hierarchy loops using a callback function that maps object IDs to parent IDs.
4809
 *
4810
 * @since 3.1.0
4811
 * @access private
4812
 *
4813
 * @param callable $callback      Function that accepts ( ID, $callback_args ) and outputs parent_ID.
4814
 * @param int      $start         The ID to start the loop check at.
4815
 * @param int      $start_parent  The parent_ID of $start to use instead of calling $callback( $start ).
4816
 *                                Use null to always use $callback
4817
 * @param array    $callback_args Optional. Additional arguments to send to $callback.
4818
 * @return array IDs of all members of loop.
4819
 */
4820
function wp_find_hierarchy_loop( $callback, $start, $start_parent, $callback_args = array() ) {
4821
	$override = is_null( $start_parent ) ? array() : array( $start => $start_parent );
4822
4823
	if ( !$arbitrary_loop_member = wp_find_hierarchy_loop_tortoise_hare( $callback, $start, $override, $callback_args ) )
4824
		return array();
4825
4826
	return wp_find_hierarchy_loop_tortoise_hare( $callback, $arbitrary_loop_member, $override, $callback_args, true );
4827
}
4828
4829
/**
4830
 * Use the "The Tortoise and the Hare" algorithm to detect loops.
4831
 *
4832
 * For every step of the algorithm, the hare takes two steps and the tortoise one.
4833
 * If the hare ever laps the tortoise, there must be a loop.
4834
 *
4835
 * @since 3.1.0
4836
 * @access private
4837
 *
4838
 * @param callable $callback      Function that accepts ( ID, callback_arg, ... ) and outputs parent_ID.
4839
 * @param int      $start         The ID to start the loop check at.
4840
 * @param array    $override      Optional. An array of ( ID => parent_ID, ... ) to use instead of $callback.
4841
 *                                Default empty array.
4842
 * @param array    $callback_args Optional. Additional arguments to send to $callback. Default empty array.
4843
 * @param bool     $_return_loop  Optional. Return loop members or just detect presence of loop? Only set
4844
 *                                to true if you already know the given $start is part of a loop (otherwise
4845
 *                                the returned array might include branches). Default false.
4846
 * @return mixed Scalar ID of some arbitrary member of the loop, or array of IDs of all members of loop if
4847
 *               $_return_loop
4848
 */
4849
function wp_find_hierarchy_loop_tortoise_hare( $callback, $start, $override = array(), $callback_args = array(), $_return_loop = false ) {
4850
	$tortoise = $hare = $evanescent_hare = $start;
4851
	$return = array();
4852
4853
	// Set evanescent_hare to one past hare
4854
	// Increment hare two steps
4855
	while (
4856
		$tortoise
4857
	&&
4858
		( $evanescent_hare = isset( $override[$hare] ) ? $override[$hare] : call_user_func_array( $callback, array_merge( array( $hare ), $callback_args ) ) )
4859
	&&
4860
		( $hare = isset( $override[$evanescent_hare] ) ? $override[$evanescent_hare] : call_user_func_array( $callback, array_merge( array( $evanescent_hare ), $callback_args ) ) )
4861
	) {
4862
		if ( $_return_loop )
4863
			$return[$tortoise] = $return[$evanescent_hare] = $return[$hare] = true;
4864
4865
		// tortoise got lapped - must be a loop
4866
		if ( $tortoise == $evanescent_hare || $tortoise == $hare )
4867
			return $_return_loop ? $return : $tortoise;
4868
4869
		// Increment tortoise by one step
4870
		$tortoise = isset( $override[$tortoise] ) ? $override[$tortoise] : call_user_func_array( $callback, array_merge( array( $tortoise ), $callback_args ) );
4871
	}
4872
4873
	return false;
4874
}
4875
4876
/**
4877
 * Send a HTTP header to limit rendering of pages to same origin iframes.
4878
 *
4879
 * @since 3.1.3
4880
 *
4881
 * @see https://developer.mozilla.org/en/the_x-frame-options_response_header
4882
 */
4883
function send_frame_options_header() {
4884
	@header( 'X-Frame-Options: SAMEORIGIN' );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4885
}
4886
4887
/**
4888
 * Retrieve a list of protocols to allow in HTML attributes.
4889
 *
4890
 * @since 3.3.0
4891
 * @since 4.3.0 Added 'webcal' to the protocols array.
4892
 *
4893
 * @see wp_kses()
4894
 * @see esc_url()
4895
 *
4896
 * @staticvar array $protocols
4897
 *
4898
 * @return array Array of allowed protocols. Defaults to an array containing 'http', 'https',
4899
 *               'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet',
4900
 *               'mms', 'rtsp', 'svn', 'tel', 'fax', 'xmpp', and 'webcal'.
4901
 */
4902
function wp_allowed_protocols() {
4903
	static $protocols = array();
4904
4905
	if ( empty( $protocols ) ) {
4906
		$protocols = array( 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn', 'tel', 'fax', 'xmpp', 'webcal' );
4907
4908
		/**
4909
		 * Filters the list of protocols allowed in HTML attributes.
4910
		 *
4911
		 * @since 3.0.0
4912
		 *
4913
		 * @param array $protocols Array of allowed protocols e.g. 'http', 'ftp', 'tel', and more.
4914
		 */
4915
		$protocols = apply_filters( 'kses_allowed_protocols', $protocols );
4916
	}
4917
4918
	return $protocols;
4919
}
4920
4921
/**
4922
 * Return a comma-separated string of functions that have been called to get
4923
 * to the current point in code.
4924
 *
4925
 * @since 3.4.0
4926
 *
4927
 * @see https://core.trac.wordpress.org/ticket/19589
4928
 *
4929
 * @param string $ignore_class Optional. A class to ignore all function calls within - useful
4930
 *                             when you want to just give info about the callee. Default null.
4931
 * @param int    $skip_frames  Optional. A number of stack frames to skip - useful for unwinding
4932
 *                             back to the source of the issue. Default 0.
4933
 * @param bool   $pretty       Optional. Whether or not you want a comma separated string or raw
4934
 *                             array returned. Default true.
4935
 * @return string|array Either a string containing a reversed comma separated trace or an array
4936
 *                      of individual calls.
4937
 */
4938
function wp_debug_backtrace_summary( $ignore_class = null, $skip_frames = 0, $pretty = true ) {
4939
	if ( version_compare( PHP_VERSION, '5.2.5', '>=' ) )
4940
		$trace = debug_backtrace( false );
4941
	else
4942
		$trace = debug_backtrace();
4943
4944
	$caller = array();
4945
	$check_class = ! is_null( $ignore_class );
4946
	$skip_frames++; // skip this function
4947
4948
	foreach ( $trace as $call ) {
4949
		if ( $skip_frames > 0 ) {
4950
			$skip_frames--;
4951
		} elseif ( isset( $call['class'] ) ) {
4952
			if ( $check_class && $ignore_class == $call['class'] )
4953
				continue; // Filter out calls
4954
4955
			$caller[] = "{$call['class']}{$call['type']}{$call['function']}";
4956
		} else {
4957
			if ( in_array( $call['function'], array( 'do_action', 'apply_filters' ) ) ) {
4958
				$caller[] = "{$call['function']}('{$call['args'][0]}')";
4959
			} elseif ( in_array( $call['function'], array( 'include', 'include_once', 'require', 'require_once' ) ) ) {
4960
				$caller[] = $call['function'] . "('" . str_replace( array( WP_CONTENT_DIR, ABSPATH ) , '', $call['args'][0] ) . "')";
4961
			} else {
4962
				$caller[] = $call['function'];
4963
			}
4964
		}
4965
	}
4966
	if ( $pretty )
4967
		return join( ', ', array_reverse( $caller ) );
4968
	else
4969
		return $caller;
4970
}
4971
4972
/**
4973
 * Retrieve ids that are not already present in the cache.
4974
 *
4975
 * @since 3.4.0
4976
 * @access private
4977
 *
4978
 * @param array  $object_ids ID list.
4979
 * @param string $cache_key  The cache bucket to check against.
4980
 *
4981
 * @return array List of ids not present in the cache.
4982
 */
4983
function _get_non_cached_ids( $object_ids, $cache_key ) {
4984
	$clean = array();
4985
	foreach ( $object_ids as $id ) {
4986
		$id = (int) $id;
4987
		if ( !wp_cache_get( $id, $cache_key ) ) {
4988
			$clean[] = $id;
4989
		}
4990
	}
4991
4992
	return $clean;
4993
}
4994
4995
/**
4996
 * Test if the current device has the capability to upload files.
4997
 *
4998
 * @since 3.4.0
4999
 * @access private
5000
 *
5001
 * @return bool Whether the device is able to upload files.
5002
 */
5003
function _device_can_upload() {
5004
	if ( ! wp_is_mobile() )
5005
		return true;
5006
5007
	$ua = $_SERVER['HTTP_USER_AGENT'];
5008
5009
	if ( strpos($ua, 'iPhone') !== false
5010
		|| strpos($ua, 'iPad') !== false
5011
		|| strpos($ua, 'iPod') !== false ) {
5012
			return preg_match( '#OS ([\d_]+) like Mac OS X#', $ua, $version ) && version_compare( $version[1], '6', '>=' );
5013
	}
5014
5015
	return true;
5016
}
5017
5018
/**
5019
 * Test if a given path is a stream URL
5020
 *
5021
 * @param string $path The resource path or URL.
5022
 * @return bool True if the path is a stream URL.
5023
 */
5024
function wp_is_stream( $path ) {
5025
	$wrappers = stream_get_wrappers();
5026
	$wrappers_re = '(' . join('|', $wrappers) . ')';
5027
5028
	return preg_match( "!^$wrappers_re://!", $path ) === 1;
5029
}
5030
5031
/**
5032
 * Test if the supplied date is valid for the Gregorian calendar.
5033
 *
5034
 * @since 3.5.0
5035
 *
5036
 * @see checkdate()
5037
 *
5038
 * @param  int    $month       Month number.
5039
 * @param  int    $day         Day number.
5040
 * @param  int    $year        Year number.
5041
 * @param  string $source_date The date to filter.
5042
 * @return bool True if valid date, false if not valid date.
5043
 */
5044
function wp_checkdate( $month, $day, $year, $source_date ) {
5045
	/**
5046
	 * Filters whether the given date is valid for the Gregorian calendar.
5047
	 *
5048
	 * @since 3.5.0
5049
	 *
5050
	 * @param bool   $checkdate   Whether the given date is valid.
5051
	 * @param string $source_date Date to check.
5052
	 */
5053
	return apply_filters( 'wp_checkdate', checkdate( $month, $day, $year ), $source_date );
5054
}
5055
5056
/**
5057
 * Load the auth check for monitoring whether the user is still logged in.
5058
 *
5059
 * Can be disabled with remove_action( 'admin_enqueue_scripts', 'wp_auth_check_load' );
5060
 *
5061
 * This is disabled for certain screens where a login screen could cause an
5062
 * inconvenient interruption. A filter called {@see 'wp_auth_check_load'} can be used
5063
 * for fine-grained control.
5064
 *
5065
 * @since 3.6.0
5066
 */
5067
function wp_auth_check_load() {
5068
	if ( ! is_admin() && ! is_user_logged_in() )
5069
		return;
5070
5071
	if ( defined( 'IFRAME_REQUEST' ) )
5072
		return;
5073
5074
	$screen = get_current_screen();
5075
	$hidden = array( 'update', 'update-network', 'update-core', 'update-core-network', 'upgrade', 'upgrade-network', 'network' );
5076
	$show = ! in_array( $screen->id, $hidden );
5077
5078
	/**
5079
	 * Filters whether to load the authentication check.
5080
	 *
5081
	 * Passing a falsey value to the filter will effectively short-circuit
5082
	 * loading the authentication check.
5083
	 *
5084
	 * @since 3.6.0
5085
	 *
5086
	 * @param bool      $show   Whether to load the authentication check.
5087
	 * @param WP_Screen $screen The current screen object.
5088
	 */
5089
	if ( apply_filters( 'wp_auth_check_load', $show, $screen ) ) {
5090
		wp_enqueue_style( 'wp-auth-check' );
5091
		wp_enqueue_script( 'wp-auth-check' );
5092
5093
		add_action( 'admin_print_footer_scripts', 'wp_auth_check_html', 5 );
5094
		add_action( 'wp_print_footer_scripts', 'wp_auth_check_html', 5 );
5095
	}
5096
}
5097
5098
/**
5099
 * Output the HTML that shows the wp-login dialog when the user is no longer logged in.
5100
 *
5101
 * @since 3.6.0
5102
 */
5103
function wp_auth_check_html() {
5104
	$login_url = wp_login_url();
5105
	$current_domain = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'];
5106
	$same_domain = ( strpos( $login_url, $current_domain ) === 0 );
5107
5108
	/**
5109
	 * Filters whether the authentication check originated at the same domain.
5110
	 *
5111
	 * @since 3.6.0
5112
	 *
5113
	 * @param bool $same_domain Whether the authentication check originated at the same domain.
5114
	 */
5115
	$same_domain = apply_filters( 'wp_auth_check_same_domain', $same_domain );
5116
	$wrap_class = $same_domain ? 'hidden' : 'hidden fallback';
5117
5118
	?>
5119
	<div id="wp-auth-check-wrap" class="<?php echo $wrap_class; ?>">
5120
	<div id="wp-auth-check-bg"></div>
5121
	<div id="wp-auth-check">
5122
	<button type="button" class="wp-auth-check-close button-link"><span class="screen-reader-text"><?php _e( 'Close dialog' ); ?></span></button>
5123
	<?php
5124
5125
	if ( $same_domain ) {
5126
		?>
5127
		<div id="wp-auth-check-form" class="loading" data-src="<?php echo esc_url( add_query_arg( array( 'interim-login' => 1 ), $login_url ) ); ?>"></div>
5128
		<?php
5129
	}
5130
5131
	?>
5132
	<div class="wp-auth-fallback">
5133
		<p><b class="wp-auth-fallback-expired" tabindex="0"><?php _e('Session expired'); ?></b></p>
5134
		<p><a href="<?php echo esc_url( $login_url ); ?>" target="_blank"><?php _e('Please log in again.'); ?></a>
5135
		<?php _e('The login page will open in a new window. After logging in you can close it and return to this page.'); ?></p>
5136
	</div>
5137
	</div>
5138
	</div>
5139
	<?php
5140
}
5141
5142
/**
5143
 * Check whether a user is still logged in, for the heartbeat.
5144
 *
5145
 * Send a result that shows a log-in box if the user is no longer logged in,
5146
 * or if their cookie is within the grace period.
5147
 *
5148
 * @since 3.6.0
5149
 *
5150
 * @global int $login_grace_period
5151
 *
5152
 * @param array $response  The Heartbeat response.
5153
 * @return array $response The Heartbeat response with 'wp-auth-check' value set.
5154
 */
5155
function wp_auth_check( $response ) {
5156
	$response['wp-auth-check'] = is_user_logged_in() && empty( $GLOBALS['login_grace_period'] );
5157
	return $response;
5158
}
5159
5160
/**
5161
 * Return RegEx body to liberally match an opening HTML tag.
5162
 *
5163
 * Matches an opening HTML tag that:
5164
 * 1. Is self-closing or
5165
 * 2. Has no body but has a closing tag of the same name or
5166
 * 3. Contains a body and a closing tag of the same name
5167
 *
5168
 * Note: this RegEx does not balance inner tags and does not attempt
5169
 * to produce valid HTML
5170
 *
5171
 * @since 3.6.0
5172
 *
5173
 * @param string $tag An HTML tag name. Example: 'video'.
5174
 * @return string Tag RegEx.
5175
 */
5176
function get_tag_regex( $tag ) {
5177
	if ( empty( $tag ) )
5178
		return;
5179
	return sprintf( '<%1$s[^<]*(?:>[\s\S]*<\/%1$s>|\s*\/>)', tag_escape( $tag ) );
5180
}
5181
5182
/**
5183
 * Retrieve a canonical form of the provided charset appropriate for passing to PHP
5184
 * functions such as htmlspecialchars() and charset html attributes.
5185
 *
5186
 * @since 3.6.0
5187
 * @access private
5188
 *
5189
 * @see https://core.trac.wordpress.org/ticket/23688
5190
 *
5191
 * @param string $charset A charset name.
5192
 * @return string The canonical form of the charset.
5193
 */
5194
function _canonical_charset( $charset ) {
5195
	if ( 'UTF-8' === $charset || 'utf-8' === $charset || 'utf8' === $charset ||
5196
		'UTF8' === $charset )
5197
		return 'UTF-8';
5198
5199
	if ( 'ISO-8859-1' === $charset || 'iso-8859-1' === $charset ||
5200
		'iso8859-1' === $charset || 'ISO8859-1' === $charset )
5201
		return 'ISO-8859-1';
5202
5203
	return $charset;
5204
}
5205
5206
/**
5207
 * Set the mbstring internal encoding to a binary safe encoding when func_overload
5208
 * is enabled.
5209
 *
5210
 * When mbstring.func_overload is in use for multi-byte encodings, the results from
5211
 * strlen() and similar functions respect the utf8 characters, causing binary data
5212
 * to return incorrect lengths.
5213
 *
5214
 * This function overrides the mbstring encoding to a binary-safe encoding, and
5215
 * resets it to the users expected encoding afterwards through the
5216
 * `reset_mbstring_encoding` function.
5217
 *
5218
 * It is safe to recursively call this function, however each
5219
 * `mbstring_binary_safe_encoding()` call must be followed up with an equal number
5220
 * of `reset_mbstring_encoding()` calls.
5221
 *
5222
 * @since 3.7.0
5223
 *
5224
 * @see reset_mbstring_encoding()
5225
 *
5226
 * @staticvar array $encodings
5227
 * @staticvar bool  $overloaded
5228
 *
5229
 * @param bool $reset Optional. Whether to reset the encoding back to a previously-set encoding.
5230
 *                    Default false.
5231
 */
5232
function mbstring_binary_safe_encoding( $reset = false ) {
5233
	static $encodings = array();
5234
	static $overloaded = null;
5235
5236
	if ( is_null( $overloaded ) )
5237
		$overloaded = function_exists( 'mb_internal_encoding' ) && ( ini_get( 'mbstring.func_overload' ) & 2 );
5238
5239
	if ( false === $overloaded )
5240
		return;
5241
5242
	if ( ! $reset ) {
5243
		$encoding = mb_internal_encoding();
5244
		array_push( $encodings, $encoding );
5245
		mb_internal_encoding( 'ISO-8859-1' );
5246
	}
5247
5248
	if ( $reset && $encodings ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encodings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
5249
		$encoding = array_pop( $encodings );
5250
		mb_internal_encoding( $encoding );
5251
	}
5252
}
5253
5254
/**
5255
 * Reset the mbstring internal encoding to a users previously set encoding.
5256
 *
5257
 * @see mbstring_binary_safe_encoding()
5258
 *
5259
 * @since 3.7.0
5260
 */
5261
function reset_mbstring_encoding() {
5262
	mbstring_binary_safe_encoding( true );
5263
}
5264
5265
/**
5266
 * Filter/validate a variable as a boolean.
5267
 *
5268
 * Alternative to `filter_var( $var, FILTER_VALIDATE_BOOLEAN )`.
5269
 *
5270
 * @since 4.0.0
5271
 *
5272
 * @param mixed $var Boolean value to validate.
5273
 * @return bool Whether the value is validated.
5274
 */
5275
function wp_validate_boolean( $var ) {
5276
	if ( is_bool( $var ) ) {
5277
		return $var;
5278
	}
5279
5280
	if ( is_string( $var ) && 'false' === strtolower( $var ) ) {
5281
		return false;
5282
	}
5283
5284
	return (bool) $var;
5285
}
5286
5287
/**
5288
 * Delete a file
5289
 *
5290
 * @since 4.2.0
5291
 *
5292
 * @param string $file The path to the file to delete.
5293
 */
5294
function wp_delete_file( $file ) {
5295
	/**
5296
	 * Filters the path of the file to delete.
5297
	 *
5298
	 * @since 2.1.0
5299
	 *
5300
	 * @param string $medium Path to the file to delete.
5301
	 */
5302
	$delete = apply_filters( 'wp_delete_file', $file );
5303
	if ( ! empty( $delete ) ) {
5304
		@unlink( $delete );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
5305
	}
5306
}
5307
5308
/**
5309
 * Outputs a small JS snippet on preview tabs/windows to remove `window.name` on unload.
5310
 *
5311
 * This prevents reusing the same tab for a preview when the user has navigated away.
5312
 *
5313
 * @since 4.3.0
5314
 */
5315
function wp_post_preview_js() {
5316
	global $post;
5317
5318
	if ( ! is_preview() || empty( $post ) ) {
5319
		return;
5320
	}
5321
5322
	// Has to match the window name used in post_submit_meta_box()
5323
	$name = 'wp-preview-' . (int) $post->ID;
5324
5325
	?>
5326
	<script>
5327
	( function() {
5328
		var query = document.location.search;
5329
5330
		if ( query && query.indexOf( 'preview=true' ) !== -1 ) {
5331
			window.name = '<?php echo $name; ?>';
5332
		}
5333
5334
		if ( window.addEventListener ) {
5335
			window.addEventListener( 'unload', function() { window.name = ''; }, false );
5336
		}
5337
	}());
5338
	</script>
5339
	<?php
5340
}
5341
5342
/**
5343
 * Parses and formats a MySQL datetime (Y-m-d H:i:s) for ISO8601/RFC3339.
5344
 *
5345
 * Explicitly strips timezones, as datetimes are not saved with any timezone
5346
 * information. Including any information on the offset could be misleading.
5347
 *
5348
 * @since 4.4.0
5349
 *
5350
 * @param string $date_string Date string to parse and format.
5351
 * @return string Date formatted for ISO8601/RFC3339.
5352
 */
5353
function mysql_to_rfc3339( $date_string ) {
5354
	$formatted = mysql2date( 'c', $date_string, false );
5355
5356
	// Strip timezone information
5357
	return preg_replace( '/(?:Z|[+-]\d{2}(?::\d{2})?)$/', '', $formatted );
5358
}
5359
5360
/**
5361
 * Attempts to raise the PHP memory limit for memory intensive processes.
5362
 *
5363
 * Only allows raising the existing limit and prevents lowering it.
5364
 *
5365
 * @since 4.6.0
5366
 *
5367
 * @param string $context Optional. Context in which the function is called. Accepts either 'admin',
5368
 *                        'image', or an arbitrary other context. If an arbitrary context is passed,
5369
 *                        the similarly arbitrary {@see '{$context}_memory_limit'} filter will be
5370
 *                        invoked. Default 'admin'.
5371
 * @return bool|int|string The limit that was set or false on failure.
5372
 */
5373
function wp_raise_memory_limit( $context = 'admin' ) {
5374
	// Exit early if the limit cannot be changed.
5375
	if ( false === wp_is_ini_value_changeable( 'memory_limit' ) ) {
5376
		return false;
5377
	}
5378
5379
	$current_limit     = @ini_get( 'memory_limit' );
5380
	$current_limit_int = wp_convert_hr_to_bytes( $current_limit );
5381
5382
	if ( -1 === $current_limit_int ) {
5383
		return false;
5384
	}
5385
5386
	$wp_max_limit     = WP_MAX_MEMORY_LIMIT;
5387
	$wp_max_limit_int = wp_convert_hr_to_bytes( $wp_max_limit );
5388
	$filtered_limit   = $wp_max_limit;
5389
5390
	switch ( $context ) {
5391
		case 'admin':
5392
			/**
5393
			 * Filters the maximum memory limit available for administration screens.
5394
			 *
5395
			 * This only applies to administrators, who may require more memory for tasks
5396
			 * like updates. Memory limits when processing images (uploaded or edited by
5397
			 * users of any role) are handled separately.
5398
			 *
5399
			 * The `WP_MAX_MEMORY_LIMIT` constant specifically defines the maximum memory
5400
			 * limit available when in the administration back end. The default is 256M
5401
			 * (256 megabytes of memory) or the original `memory_limit` php.ini value if
5402
			 * this is higher.
5403
			 *
5404
			 * @since 3.0.0
5405
			 * @since 4.6.0 The default now takes the original `memory_limit` into account.
5406
			 *
5407
			 * @param int|string $filtered_limit The maximum WordPress memory limit. Accepts an integer
5408
			 *                                   (bytes), or a shorthand string notation, such as '256M'.
5409
			 */
5410
			$filtered_limit = apply_filters( 'admin_memory_limit', $filtered_limit );
5411
			break;
5412
5413
		case 'image':
5414
			/**
5415
			 * Filters the memory limit allocated for image manipulation.
5416
			 *
5417
			 * @since 3.5.0
5418
			 * @since 4.6.0 The default now takes the original `memory_limit` into account.
5419
			 *
5420
			 * @param int|string $filtered_limit Maximum memory limit to allocate for images.
5421
			 *                                   Default `WP_MAX_MEMORY_LIMIT` or the original
5422
			 *                                   php.ini `memory_limit`, whichever is higher.
5423
			 *                                   Accepts an integer (bytes), or a shorthand string
5424
			 *                                   notation, such as '256M'.
5425
			 */
5426
			$filtered_limit = apply_filters( 'image_memory_limit', $filtered_limit );
5427
			break;
5428
5429
		default:
5430
			/**
5431
			 * Filters the memory limit allocated for arbitrary contexts.
5432
			 *
5433
			 * The dynamic portion of the hook name, `$context`, refers to an arbitrary
5434
			 * context passed on calling the function. This allows for plugins to define
5435
			 * their own contexts for raising the memory limit.
5436
			 *
5437
			 * @since 4.6.0
5438
			 *
5439
			 * @param int|string $filtered_limit Maximum memory limit to allocate for images.
5440
			 *                                   Default '256M' or the original php.ini `memory_limit`,
5441
			 *                                   whichever is higher. Accepts an integer (bytes), or a
5442
			 *                                   shorthand string notation, such as '256M'.
5443
			 */
5444
			$filtered_limit = apply_filters( "{$context}_memory_limit", $filtered_limit );
5445
			break;
5446
	}
5447
5448
	$filtered_limit_int = wp_convert_hr_to_bytes( $filtered_limit );
5449
5450
	if ( -1 === $filtered_limit_int || ( $filtered_limit_int > $wp_max_limit_int && $filtered_limit_int > $current_limit_int ) ) {
5451
		if ( false !== @ini_set( 'memory_limit', $filtered_limit ) ) {
5452
			return $filtered_limit;
5453
		} else {
5454
			return false;
5455
		}
5456
	} elseif ( -1 === $wp_max_limit_int || $wp_max_limit_int > $current_limit_int ) {
5457
		if ( false !== @ini_set( 'memory_limit', $wp_max_limit ) ) {
5458
			return $wp_max_limit;
5459
		} else {
5460
			return false;
5461
		}
5462
	}
5463
5464
	return false;
5465
}
5466