Issues (4967)

Security Analysis    not enabled

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

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

src/wp-includes/update.php (14 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
2
/**
3
 * A simple set of functions to check our version 1.0 update service.
4
 *
5
 * @package WordPress
6
 * @since 2.3.0
7
 */
8
9
/**
10
 * Check WordPress version against the newest version.
11
 *
12
 * The WordPress version, PHP version, and Locale is sent. Checks against the
13
 * WordPress server at api.wordpress.org server. Will only check if WordPress
14
 * isn't installing.
15
 *
16
 * @since 2.3.0
17
 * @global string $wp_version Used to check against the newest WordPress version.
18
 * @global wpdb   $wpdb
19
 * @global string $wp_local_package
20
 *
21
 * @param array $extra_stats Extra statistics to report to the WordPress.org API.
22
 * @param bool  $force_check Whether to bypass the transient cache and force a fresh update check. Defaults to false, true if $extra_stats is set.
23
 */
24
function wp_version_check( $extra_stats = array(), $force_check = false ) {
25
	if ( wp_installing() ) {
26
		return;
27
	}
28
29
	global $wpdb, $wp_local_package;
30
	// include an unmodified $wp_version
31
	include( ABSPATH . WPINC . '/version.php' );
32
	$php_version = phpversion();
33
34
	$current = get_site_transient( 'update_core' );
35
	$translations = wp_get_installed_translations( 'core' );
36
37
	// Invalidate the transient when $wp_version changes
38
	if ( is_object( $current ) && $wp_version != $current->version_checked )
0 ignored issues
show
The variable $wp_version does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
39
		$current = false;
40
41
	if ( ! is_object($current) ) {
42
		$current = new stdClass;
43
		$current->updates = array();
44
		$current->version_checked = $wp_version;
45
	}
46
47
	if ( ! empty( $extra_stats ) )
48
		$force_check = true;
49
50
	// Wait 60 seconds between multiple version check requests
51
	$timeout = 60;
52
	$time_not_changed = isset( $current->last_checked ) && $timeout > ( time() - $current->last_checked );
53
	if ( ! $force_check && $time_not_changed ) {
54
		return;
55
	}
56
57
	/**
58
	 * Filters the locale requested for WordPress core translations.
59
	 *
60
	 * @since 2.8.0
61
	 *
62
	 * @param string $locale Current locale.
63
	 */
64
	$locale = apply_filters( 'core_version_check_locale', get_locale() );
65
66
	// Update last_checked for current to prevent multiple blocking requests if request hangs
67
	$current->last_checked = time();
68
	set_site_transient( 'update_core', $current );
69
70
	if ( method_exists( $wpdb, 'db_version' ) )
71
		$mysql_version = preg_replace('/[^0-9.].*/', '', $wpdb->db_version());
72
	else
73
		$mysql_version = 'N/A';
74
75
	if ( is_multisite() ) {
76
		$user_count = get_user_count();
77
		$num_blogs = get_blog_count();
78
		$wp_install = network_site_url();
79
		$multisite_enabled = 1;
80
	} else {
81
		$user_count = count_users();
82
		$user_count = $user_count['total_users'];
83
		$multisite_enabled = 0;
84
		$num_blogs = 1;
85
		$wp_install = home_url( '/' );
86
	}
87
88
	$query = array(
89
		'version'            => $wp_version,
90
		'php'                => $php_version,
91
		'locale'             => $locale,
92
		'mysql'              => $mysql_version,
93
		'local_package'      => isset( $wp_local_package ) ? $wp_local_package : '',
94
		'blogs'              => $num_blogs,
95
		'users'              => $user_count,
96
		'multisite_enabled'  => $multisite_enabled,
97
		'initial_db_version' => get_site_option( 'initial_db_version' ),
98
	);
99
100
	$post_body = array(
101
		'translations' => wp_json_encode( $translations ),
102
	);
103
104
	if ( is_array( $extra_stats ) )
105
		$post_body = array_merge( $post_body, $extra_stats );
106
107
	$url = $http_url = 'http://api.wordpress.org/core/version-check/1.7/?' . http_build_query( $query, null, '&' );
108
	if ( $ssl = wp_http_supports( array( 'ssl' ) ) )
109
		$url = set_url_scheme( $url, 'https' );
110
111
	$doing_cron = wp_doing_cron();
112
113
	$options = array(
114
		'timeout' => $doing_cron ? 30 : 3,
115
		'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
116
		'headers' => array(
117
			'wp_install' => $wp_install,
118
			'wp_blog' => home_url( '/' )
119
		),
120
		'body' => $post_body,
121
	);
122
123
	$response = wp_remote_post( $url, $options );
124 View Code Duplication
	if ( $ssl && is_wp_error( $response ) ) {
125
		trigger_error(
126
			sprintf(
127
				/* translators: %s: support forums URL */
128
				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
129
				__( 'https://wordpress.org/support/' )
130
			) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
131
			headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
132
		);
133
		$response = wp_remote_post( $http_url, $options );
134
	}
135
136
	if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
0 ignored issues
show
It seems like $response can also be of type object<WP_Error>; however, wp_remote_retrieve_response_code() 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...
137
		return;
138
	}
139
140
	$body = trim( wp_remote_retrieve_body( $response ) );
0 ignored issues
show
It seems like $response 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...
141
	$body = json_decode( $body, true );
142
143
	if ( ! is_array( $body ) || ! isset( $body['offers'] ) ) {
144
		return;
145
	}
146
147
	$offers = $body['offers'];
148
149
	foreach ( $offers as &$offer ) {
150
		foreach ( $offer as $offer_key => $value ) {
151
			if ( 'packages' == $offer_key )
152
				$offer['packages'] = (object) array_intersect_key( array_map( 'esc_url', $offer['packages'] ),
153
					array_fill_keys( array( 'full', 'no_content', 'new_bundled', 'partial', 'rollback' ), '' ) );
154
			elseif ( 'download' == $offer_key )
155
				$offer['download'] = esc_url( $value );
156
			else
157
				$offer[ $offer_key ] = esc_html( $value );
158
		}
159
		$offer = (object) array_intersect_key( $offer, array_fill_keys( array( 'response', 'download', 'locale',
160
			'packages', 'current', 'version', 'php_version', 'mysql_version', 'new_bundled', 'partial_version', 'notify_email', 'support_email', 'new_files' ), '' ) );
161
	}
162
163
	$updates = new stdClass();
164
	$updates->updates = $offers;
165
	$updates->last_checked = time();
166
	$updates->version_checked = $wp_version;
167
168
	if ( isset( $body['translations'] ) )
169
		$updates->translations = $body['translations'];
170
171
	set_site_transient( 'update_core', $updates );
172
173
	if ( ! empty( $body['ttl'] ) ) {
174
		$ttl = (int) $body['ttl'];
175
		if ( $ttl && ( time() + $ttl < wp_next_scheduled( 'wp_version_check' ) ) ) {
176
			// Queue an event to re-run the update check in $ttl seconds.
177
			wp_schedule_single_event( time() + $ttl, 'wp_version_check' );
178
		}
179
	}
180
181
	// Trigger background updates if running non-interactively, and we weren't called from the update handler.
182
	if ( $doing_cron && ! doing_action( 'wp_maybe_auto_update' ) ) {
183
		do_action( 'wp_maybe_auto_update' );
184
	}
185
}
186
187
/**
188
 * Check plugin versions against the latest versions hosted on WordPress.org.
189
 *
190
 * The WordPress version, PHP version, and Locale is sent along with a list of
191
 * all plugins installed. Checks against the WordPress server at
192
 * api.wordpress.org. Will only check if WordPress isn't installing.
193
 *
194
 * @since 2.3.0
195
 * @global string $wp_version Used to notify the WordPress version.
196
 *
197
 * @param array $extra_stats Extra statistics to report to the WordPress.org API.
198
 */
199
function wp_update_plugins( $extra_stats = array() ) {
200
	if ( wp_installing() ) {
201
		return;
202
	}
203
204
	// include an unmodified $wp_version
205
	include( ABSPATH . WPINC . '/version.php' );
206
207
	// If running blog-side, bail unless we've not checked in the last 12 hours
208
	if ( !function_exists( 'get_plugins' ) )
209
		require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
210
211
	$plugins = get_plugins();
212
	$translations = wp_get_installed_translations( 'plugins' );
213
214
	$active  = get_option( 'active_plugins', array() );
215
	$current = get_site_transient( 'update_plugins' );
216
	if ( ! is_object($current) )
217
		$current = new stdClass;
218
219
	$new_option = new stdClass;
220
	$new_option->last_checked = time();
221
222
	$doing_cron = wp_doing_cron();
223
224
	// Check for update on a different schedule, depending on the page.
225 View Code Duplication
	switch ( current_filter() ) {
226
		case 'upgrader_process_complete' :
227
			$timeout = 0;
228
			break;
229
		case 'load-update-core.php' :
230
			$timeout = MINUTE_IN_SECONDS;
231
			break;
232
		case 'load-plugins.php' :
233
		case 'load-update.php' :
234
			$timeout = HOUR_IN_SECONDS;
235
			break;
236
		default :
237
			if ( $doing_cron ) {
238
				$timeout = 0;
239
			} else {
240
				$timeout = 12 * HOUR_IN_SECONDS;
241
			}
242
	}
243
244
	$time_not_changed = isset( $current->last_checked ) && $timeout > ( time() - $current->last_checked );
245
246
	if ( $time_not_changed && ! $extra_stats ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $extra_stats 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...
247
		$plugin_changed = false;
248
		foreach ( $plugins as $file => $p ) {
249
			$new_option->checked[ $file ] = $p['Version'];
250
251
			if ( !isset( $current->checked[ $file ] ) || strval($current->checked[ $file ]) !== strval($p['Version']) )
252
				$plugin_changed = true;
253
		}
254
255
		if ( isset ( $current->response ) && is_array( $current->response ) ) {
256
			foreach ( $current->response as $plugin_file => $update_details ) {
257
				if ( ! isset($plugins[ $plugin_file ]) ) {
258
					$plugin_changed = true;
259
					break;
260
				}
261
			}
262
		}
263
264
		// Bail if we've checked recently and if nothing has changed
265
		if ( ! $plugin_changed ) {
266
			return;
267
		}
268
	}
269
270
	// Update last_checked for current to prevent multiple blocking requests if request hangs
271
	$current->last_checked = time();
272
	set_site_transient( 'update_plugins', $current );
273
274
	$to_send = compact( 'plugins', 'active' );
275
276
	$locales = array_values( get_available_languages() );
277
278
	/**
279
	 * Filters the locales requested for plugin translations.
280
	 *
281
	 * @since 3.7.0
282
	 * @since 4.5.0 The default value of the `$locales` parameter changed to include all locales.
283
	 *
284
	 * @param array $locales Plugin locales. Default is all available locales of the site.
285
	 */
286
	$locales = apply_filters( 'plugins_update_check_locales', $locales );
287
	$locales = array_unique( $locales );
288
289
	if ( $doing_cron ) {
290
		$timeout = 30;
291
	} else {
292
		// Three seconds, plus one extra second for every 10 plugins
293
		$timeout = 3 + (int) ( count( $plugins ) / 10 );
294
	}
295
296
	$options = array(
297
		'timeout' => $timeout,
298
		'body' => array(
299
			'plugins'      => wp_json_encode( $to_send ),
300
			'translations' => wp_json_encode( $translations ),
301
			'locale'       => wp_json_encode( $locales ),
302
			'all'          => wp_json_encode( true ),
303
		),
304
		'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )
0 ignored issues
show
The variable $wp_version does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
305
	);
306
307
	if ( $extra_stats ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $extra_stats 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...
308
		$options['body']['update_stats'] = wp_json_encode( $extra_stats );
309
	}
310
311
	$url = $http_url = 'http://api.wordpress.org/plugins/update-check/1.1/';
312
	if ( $ssl = wp_http_supports( array( 'ssl' ) ) )
313
		$url = set_url_scheme( $url, 'https' );
314
315
	$raw_response = wp_remote_post( $url, $options );
316 View Code Duplication
	if ( $ssl && is_wp_error( $raw_response ) ) {
317
		trigger_error(
318
			sprintf(
319
				/* translators: %s: support forums URL */
320
				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
321
				__( 'https://wordpress.org/support/' )
322
			) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
323
			headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
324
		);
325
		$raw_response = wp_remote_post( $http_url, $options );
326
	}
327
328
	if ( is_wp_error( $raw_response ) || 200 != wp_remote_retrieve_response_code( $raw_response ) ) {
0 ignored issues
show
It seems like $raw_response can also be of type object<WP_Error>; however, wp_remote_retrieve_response_code() 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...
329
		return;
330
	}
331
332
	$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
0 ignored issues
show
It seems like $raw_response 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...
333
	foreach ( $response['plugins'] as &$plugin ) {
334
		$plugin = (object) $plugin;
335
		if ( isset( $plugin->compatibility ) ) {
336
			$plugin->compatibility = (object) $plugin->compatibility;
337
			foreach ( $plugin->compatibility as &$data ) {
338
				$data = (object) $data;
339
			}
340
		}
341
	}
342
	unset( $plugin, $data );
343
	foreach ( $response['no_update'] as &$plugin ) {
344
		$plugin = (object) $plugin;
345
	}
346
	unset( $plugin );
347
348
	if ( is_array( $response ) ) {
349
		$new_option->response = $response['plugins'];
350
		$new_option->translations = $response['translations'];
351
		// TODO: Perhaps better to store no_update in a separate transient with an expiry?
352
		$new_option->no_update = $response['no_update'];
353
	} else {
354
		$new_option->response = array();
355
		$new_option->translations = array();
356
		$new_option->no_update = array();
357
	}
358
359
	set_site_transient( 'update_plugins', $new_option );
360
}
361
362
/**
363
 * Check theme versions against the latest versions hosted on WordPress.org.
364
 *
365
 * A list of all themes installed in sent to WP. Checks against the
366
 * WordPress server at api.wordpress.org. Will only check if WordPress isn't
367
 * installing.
368
 *
369
 * @since 2.7.0
370
 *
371
 * @param array $extra_stats Extra statistics to report to the WordPress.org API.
372
 */
373
function wp_update_themes( $extra_stats = array() ) {
374
	if ( wp_installing() ) {
375
		return;
376
	}
377
378
	// include an unmodified $wp_version
379
	include( ABSPATH . WPINC . '/version.php' );
380
381
	$installed_themes = wp_get_themes();
382
	$translations = wp_get_installed_translations( 'themes' );
383
384
	$last_update = get_site_transient( 'update_themes' );
385
	if ( ! is_object($last_update) )
386
		$last_update = new stdClass;
387
388
	$themes = $checked = $request = array();
389
390
	// Put slug of current theme into request.
391
	$request['active'] = get_option( 'stylesheet' );
392
393
	foreach ( $installed_themes as $theme ) {
394
		$checked[ $theme->get_stylesheet() ] = $theme->get('Version');
395
396
		$themes[ $theme->get_stylesheet() ] = array(
397
			'Name'       => $theme->get('Name'),
398
			'Title'      => $theme->get('Name'),
399
			'Version'    => $theme->get('Version'),
400
			'Author'     => $theme->get('Author'),
401
			'Author URI' => $theme->get('AuthorURI'),
402
			'Template'   => $theme->get_template(),
403
			'Stylesheet' => $theme->get_stylesheet(),
404
		);
405
	}
406
407
	$doing_cron = wp_doing_cron();
408
409
	// Check for update on a different schedule, depending on the page.
410 View Code Duplication
	switch ( current_filter() ) {
411
		case 'upgrader_process_complete' :
412
			$timeout = 0;
413
			break;
414
		case 'load-update-core.php' :
415
			$timeout = MINUTE_IN_SECONDS;
416
			break;
417
		case 'load-themes.php' :
418
		case 'load-update.php' :
419
			$timeout = HOUR_IN_SECONDS;
420
			break;
421
		default :
422
			$timeout = $doing_cron ? 0 : 12 * HOUR_IN_SECONDS;
423
	}
424
425
	$time_not_changed = isset( $last_update->last_checked ) && $timeout > ( time() - $last_update->last_checked );
426
427
	if ( $time_not_changed && ! $extra_stats ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $extra_stats 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...
428
		$theme_changed = false;
429
		foreach ( $checked as $slug => $v ) {
430
			if ( !isset( $last_update->checked[ $slug ] ) || strval($last_update->checked[ $slug ]) !== strval($v) )
431
				$theme_changed = true;
432
		}
433
434
		if ( isset ( $last_update->response ) && is_array( $last_update->response ) ) {
435
			foreach ( $last_update->response as $slug => $update_details ) {
436
				if ( ! isset($checked[ $slug ]) ) {
437
					$theme_changed = true;
438
					break;
439
				}
440
			}
441
		}
442
443
		// Bail if we've checked recently and if nothing has changed
444
		if ( ! $theme_changed ) {
445
			return;
446
		}
447
	}
448
449
	// Update last_checked for current to prevent multiple blocking requests if request hangs
450
	$last_update->last_checked = time();
451
	set_site_transient( 'update_themes', $last_update );
452
453
	$request['themes'] = $themes;
454
455
	$locales = array_values( get_available_languages() );
456
457
	/**
458
	 * Filters the locales requested for theme translations.
459
	 *
460
	 * @since 3.7.0
461
	 * @since 4.5.0 The default value of the `$locales` parameter changed to include all locales.
462
	 *
463
	 * @param array $locales Theme locales. Default is all available locales of the site.
464
	 */
465
	$locales = apply_filters( 'themes_update_check_locales', $locales );
466
	$locales = array_unique( $locales );
467
468
	if ( $doing_cron ) {
469
		$timeout = 30;
470
	} else {
471
		// Three seconds, plus one extra second for every 10 themes
472
		$timeout = 3 + (int) ( count( $themes ) / 10 );
473
	}
474
475
	$options = array(
476
		'timeout' => $timeout,
477
		'body' => array(
478
			'themes'       => wp_json_encode( $request ),
479
			'translations' => wp_json_encode( $translations ),
480
			'locale'       => wp_json_encode( $locales ),
481
		),
482
		'user-agent'	=> 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )
0 ignored issues
show
The variable $wp_version does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
483
	);
484
485
	if ( $extra_stats ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $extra_stats 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...
486
		$options['body']['update_stats'] = wp_json_encode( $extra_stats );
487
	}
488
489
	$url = $http_url = 'http://api.wordpress.org/themes/update-check/1.1/';
490
	if ( $ssl = wp_http_supports( array( 'ssl' ) ) )
491
		$url = set_url_scheme( $url, 'https' );
492
493
	$raw_response = wp_remote_post( $url, $options );
494 View Code Duplication
	if ( $ssl && is_wp_error( $raw_response ) ) {
495
		trigger_error(
496
			sprintf(
497
				/* translators: %s: support forums URL */
498
				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
499
				__( 'https://wordpress.org/support/' )
500
			) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
501
			headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
502
		);
503
		$raw_response = wp_remote_post( $http_url, $options );
504
	}
505
506
	if ( is_wp_error( $raw_response ) || 200 != wp_remote_retrieve_response_code( $raw_response ) ) {
0 ignored issues
show
It seems like $raw_response can also be of type object<WP_Error>; however, wp_remote_retrieve_response_code() 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...
507
		return;
508
	}
509
510
	$new_update = new stdClass;
511
	$new_update->last_checked = time();
512
	$new_update->checked = $checked;
513
514
	$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
0 ignored issues
show
It seems like $raw_response 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...
515
516
	if ( is_array( $response ) ) {
517
		$new_update->response     = $response['themes'];
518
		$new_update->translations = $response['translations'];
519
	}
520
521
	set_site_transient( 'update_themes', $new_update );
522
}
523
524
/**
525
 * Performs WordPress automatic background updates.
526
 *
527
 * @since 3.7.0
528
 */
529
function wp_maybe_auto_update() {
530
	include_once( ABSPATH . '/wp-admin/includes/admin.php' );
531
	include_once( ABSPATH . '/wp-admin/includes/class-wp-upgrader.php' );
532
533
	$upgrader = new WP_Automatic_Updater;
534
	$upgrader->run();
535
}
536
537
/**
538
 * Retrieves a list of all language updates available.
539
 *
540
 * @since 3.7.0
541
 *
542
 * @return array
543
 */
544
function wp_get_translation_updates() {
545
	$updates = array();
546
	$transients = array( 'update_core' => 'core', 'update_plugins' => 'plugin', 'update_themes' => 'theme' );
547
	foreach ( $transients as $transient => $type ) {
548
		$transient = get_site_transient( $transient );
549
		if ( empty( $transient->translations ) )
550
			continue;
551
552
		foreach ( $transient->translations as $translation ) {
553
			$updates[] = (object) $translation;
554
		}
555
	}
556
	return $updates;
557
}
558
559
/**
560
 * Collect counts and UI strings for available updates
561
 *
562
 * @since 3.3.0
563
 *
564
 * @return array
565
 */
566
function wp_get_update_data() {
567
	$counts = array( 'plugins' => 0, 'themes' => 0, 'wordpress' => 0, 'translations' => 0 );
568
569 View Code Duplication
	if ( $plugins = current_user_can( 'update_plugins' ) ) {
570
		$update_plugins = get_site_transient( 'update_plugins' );
571
		if ( ! empty( $update_plugins->response ) )
572
			$counts['plugins'] = count( $update_plugins->response );
573
	}
574
575 View Code Duplication
	if ( $themes = current_user_can( 'update_themes' ) ) {
576
		$update_themes = get_site_transient( 'update_themes' );
577
		if ( ! empty( $update_themes->response ) )
578
			$counts['themes'] = count( $update_themes->response );
579
	}
580
581
	if ( ( $core = current_user_can( 'update_core' ) ) && function_exists( 'get_core_updates' ) ) {
582
		$update_wordpress = get_core_updates( array('dismissed' => false) );
583
		if ( ! empty( $update_wordpress ) && ! in_array( $update_wordpress[0]->response, array('development', 'latest') ) && current_user_can('update_core') )
584
			$counts['wordpress'] = 1;
585
	}
586
587
	if ( ( $core || $plugins || $themes ) && wp_get_translation_updates() )
588
		$counts['translations'] = 1;
589
590
	$counts['total'] = $counts['plugins'] + $counts['themes'] + $counts['wordpress'] + $counts['translations'];
591
	$titles = array();
592
	if ( $counts['wordpress'] ) {
593
		/* translators: 1: Number of updates available to WordPress */
594
		$titles['wordpress'] = sprintf( __( '%d WordPress Update'), $counts['wordpress'] );
595
	}
596
	if ( $counts['plugins'] ) {
597
		/* translators: 1: Number of updates available to plugins */
598
		$titles['plugins'] = sprintf( _n( '%d Plugin Update', '%d Plugin Updates', $counts['plugins'] ), $counts['plugins'] );
599
	}
600
	if ( $counts['themes'] ) {
601
		/* translators: 1: Number of updates available to themes */
602
		$titles['themes'] = sprintf( _n( '%d Theme Update', '%d Theme Updates', $counts['themes'] ), $counts['themes'] );
603
	}
604
	if ( $counts['translations'] ) {
605
		$titles['translations'] = __( 'Translation Updates' );
606
	}
607
608
	$update_title = $titles ? esc_attr( implode( ', ', $titles ) ) : '';
609
610
	$update_data = array( 'counts' => $counts, 'title' => $update_title );
611
	/**
612
	 * Filters the returned array of update data for plugins, themes, and WordPress core.
613
	 *
614
	 * @since 3.5.0
615
	 *
616
	 * @param array $update_data {
617
	 *     Fetched update data.
618
	 *
619
	 *     @type array   $counts       An array of counts for available plugin, theme, and WordPress updates.
620
	 *     @type string  $update_title Titles of available updates.
621
	 * }
622
	 * @param array $titles An array of update counts and UI strings for available updates.
623
	 */
624
	return apply_filters( 'wp_get_update_data', $update_data, $titles );
625
}
626
627
/**
628
 * Determines whether core should be updated.
629
 *
630
 * @since 2.8.0
631
 *
632
 * @global string $wp_version
633
 */
634
function _maybe_update_core() {
635
	// include an unmodified $wp_version
636
	include( ABSPATH . WPINC . '/version.php' );
637
638
	$current = get_site_transient( 'update_core' );
639
640
	if ( isset( $current->last_checked, $current->version_checked ) &&
641
		12 * HOUR_IN_SECONDS > ( time() - $current->last_checked ) &&
642
		$current->version_checked == $wp_version ) {
0 ignored issues
show
The variable $wp_version does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
643
		return;
644
	}
645
	wp_version_check();
646
}
647
/**
648
 * Check the last time plugins were run before checking plugin versions.
649
 *
650
 * This might have been backported to WordPress 2.6.1 for performance reasons.
651
 * This is used for the wp-admin to check only so often instead of every page
652
 * load.
653
 *
654
 * @since 2.7.0
655
 * @access private
656
 */
657
function _maybe_update_plugins() {
658
	$current = get_site_transient( 'update_plugins' );
659 View Code Duplication
	if ( isset( $current->last_checked ) && 12 * HOUR_IN_SECONDS > ( time() - $current->last_checked ) )
660
		return;
661
	wp_update_plugins();
662
}
663
664
/**
665
 * Check themes versions only after a duration of time.
666
 *
667
 * This is for performance reasons to make sure that on the theme version
668
 * checker is not run on every page load.
669
 *
670
 * @since 2.7.0
671
 * @access private
672
 */
673
function _maybe_update_themes() {
674
	$current = get_site_transient( 'update_themes' );
675 View Code Duplication
	if ( isset( $current->last_checked ) && 12 * HOUR_IN_SECONDS > ( time() - $current->last_checked ) )
676
		return;
677
	wp_update_themes();
678
}
679
680
/**
681
 * Schedule core, theme, and plugin update checks.
682
 *
683
 * @since 3.1.0
684
 */
685
function wp_schedule_update_checks() {
686
	if ( ! wp_next_scheduled( 'wp_version_check' ) && ! wp_installing() )
687
		wp_schedule_event(time(), 'twicedaily', 'wp_version_check');
688
689
	if ( ! wp_next_scheduled( 'wp_update_plugins' ) && ! wp_installing() )
690
		wp_schedule_event(time(), 'twicedaily', 'wp_update_plugins');
691
692
	if ( ! wp_next_scheduled( 'wp_update_themes' ) && ! wp_installing() )
693
		wp_schedule_event(time(), 'twicedaily', 'wp_update_themes');
694
}
695
696
/**
697
 * Clear existing update caches for plugins, themes, and core.
698
 *
699
 * @since 4.1.0
700
 */
701
function wp_clean_update_cache() {
702
	if ( function_exists( 'wp_clean_plugins_cache' ) ) {
703
		wp_clean_plugins_cache();
704
	} else {
705
		delete_site_transient( 'update_plugins' );
706
	}
707
	wp_clean_themes_cache();
708
	delete_site_transient( 'update_core' );
709
}
710
711
if ( ( ! is_main_site() && ! is_network_admin() ) || wp_doing_ajax() ) {
712
	return;
713
}
714
715
add_action( 'admin_init', '_maybe_update_core' );
716
add_action( 'wp_version_check', 'wp_version_check' );
717
718
add_action( 'load-plugins.php', 'wp_update_plugins' );
719
add_action( 'load-update.php', 'wp_update_plugins' );
720
add_action( 'load-update-core.php', 'wp_update_plugins' );
721
add_action( 'admin_init', '_maybe_update_plugins' );
722
add_action( 'wp_update_plugins', 'wp_update_plugins' );
723
724
add_action( 'load-themes.php', 'wp_update_themes' );
725
add_action( 'load-update.php', 'wp_update_themes' );
726
add_action( 'load-update-core.php', 'wp_update_themes' );
727
add_action( 'admin_init', '_maybe_update_themes' );
728
add_action( 'wp_update_themes', 'wp_update_themes' );
729
730
add_action( 'update_option_WPLANG', 'wp_clean_update_cache' , 10, 0 );
731
732
add_action( 'wp_maybe_auto_update', 'wp_maybe_auto_update' );
733
734
add_action( 'init', 'wp_schedule_update_checks' );
735