Issues (2010)

Security Analysis    not enabled

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

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

wp-admin/includes/ajax-actions.php (55 issues)

Upgrade to new PHP Analysis Engine

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

Code
1
<?php
2
/**
3
 * Administration API: Core Ajax handlers
4
 *
5
 * @package WordPress
6
 * @subpackage Administration
7
 * @since 2.1.0
8
 */
9
10
//
11
// No-privilege Ajax handlers.
12
//
13
14
/**
15
 * Ajax handler for the Heartbeat API in
16
 * the no-privilege context.
17
 *
18
 * Runs when the user is not logged in.
19
 *
20
 * @since 3.6.0
21
 */
22
function wp_ajax_nopriv_heartbeat() {
23
	$response = array();
24
25
	// screen_id is the same as $current_screen->id and the JS global 'pagenow'.
26 View Code Duplication
	if ( ! empty($_POST['screen_id']) )
27
		$screen_id = sanitize_key($_POST['screen_id']);
28
	else
29
		$screen_id = 'front';
30
31
	if ( ! empty($_POST['data']) ) {
32
		$data = wp_unslash( (array) $_POST['data'] );
33
34
		/**
35
		 * Filters Heartbeat Ajax response in no-privilege environments.
36
		 *
37
		 * @since 3.6.0
38
		 *
39
		 * @param array|object $response  The no-priv Heartbeat response object or array.
40
		 * @param array        $data      An array of data passed via $_POST.
41
		 * @param string       $screen_id The screen id.
42
		 */
43
		$response = apply_filters( 'heartbeat_nopriv_received', $response, $data, $screen_id );
44
	}
45
46
	/**
47
	 * Filters Heartbeat Ajax response when no data is passed.
48
	 *
49
	 * @since 3.6.0
50
	 *
51
	 * @param array|object $response  The Heartbeat response object or array.
52
	 * @param string       $screen_id The screen id.
53
	 */
54
	$response = apply_filters( 'heartbeat_nopriv_send', $response, $screen_id );
55
56
	/**
57
	 * Fires when Heartbeat ticks in no-privilege environments.
58
	 *
59
	 * Allows the transport to be easily replaced with long-polling.
60
	 *
61
	 * @since 3.6.0
62
	 *
63
	 * @param array|object $response  The no-priv Heartbeat response.
64
	 * @param string       $screen_id The screen id.
65
	 */
66
	do_action( 'heartbeat_nopriv_tick', $response, $screen_id );
67
68
	// Send the current time according to the server.
69
	$response['server_time'] = time();
70
71
	wp_send_json($response);
72
}
73
74
//
75
// GET-based Ajax handlers.
76
//
77
78
/**
79
 * Ajax handler for fetching a list table.
80
 *
81
 * @since 3.1.0
82
 *
83
 * @global WP_List_Table $wp_list_table
84
 */
85
function wp_ajax_fetch_list() {
86
	global $wp_list_table;
87
88
	$list_class = $_GET['list_args']['class'];
89
	check_ajax_referer( "fetch-list-$list_class", '_ajax_fetch_list_nonce' );
90
91
	$wp_list_table = _get_list_table( $list_class, array( 'screen' => $_GET['list_args']['screen']['id'] ) );
92
	if ( ! $wp_list_table )
93
		wp_die( 0 );
94
95
	if ( ! $wp_list_table->ajax_user_can() )
96
		wp_die( -1 );
97
98
	$wp_list_table->ajax_response();
99
100
	wp_die( 0 );
101
}
102
103
/**
104
 * Ajax handler for tag search.
105
 *
106
 * @since 3.1.0
107
 */
108
function wp_ajax_ajax_tag_search() {
109
	if ( ! isset( $_GET['tax'] ) ) {
110
		wp_die( 0 );
111
	}
112
113
	$taxonomy = sanitize_key( $_GET['tax'] );
114
	$tax = get_taxonomy( $taxonomy );
115
	if ( ! $tax ) {
116
		wp_die( 0 );
117
	}
118
119
	if ( ! current_user_can( $tax->cap->assign_terms ) ) {
120
		wp_die( -1 );
121
	}
122
123
	$s = wp_unslash( $_GET['q'] );
124
125
	$comma = _x( ',', 'tag delimiter' );
126
	if ( ',' !== $comma )
127
		$s = str_replace( $comma, ',', $s );
128 View Code Duplication
	if ( false !== strpos( $s, ',' ) ) {
129
		$s = explode( ',', $s );
130
		$s = $s[count( $s ) - 1];
131
	}
132
	$s = trim( $s );
133
134
	/**
135
	 * Filters the minimum number of characters required to fire a tag search via Ajax.
136
	 *
137
	 * @since 4.0.0
138
	 *
139
	 * @param int    $characters The minimum number of characters required. Default 2.
140
	 * @param object $tax        The taxonomy object.
141
	 * @param string $s          The search term.
142
	 */
143
	$term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $tax, $s );
144
145
	/*
146
	 * Require $term_search_min_chars chars for matching (default: 2)
147
	 * ensure it's a non-negative, non-zero integer.
148
	 */
149
	if ( ( $term_search_min_chars == 0 ) || ( strlen( $s ) < $term_search_min_chars ) ){
150
		wp_die();
151
	}
152
153
	$results = get_terms( $taxonomy, array( 'name__like' => $s, 'fields' => 'names', 'hide_empty' => false ) );
154
155
	echo join( $results, "\n" );
156
	wp_die();
157
}
158
159
/**
160
 * Ajax handler for compression testing.
161
 *
162
 * @since 3.1.0
163
 */
164
function wp_ajax_wp_compression_test() {
165
	if ( !current_user_can( 'manage_options' ) )
166
		wp_die( -1 );
167
168
	if ( ini_get('zlib.output_compression') || 'ob_gzhandler' == ini_get('output_handler') ) {
169
		update_site_option('can_compress_scripts', 0);
170
		wp_die( 0 );
171
	}
172
173
	if ( isset($_GET['test']) ) {
174
		header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' );
175
		header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
176
		header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
177
		header('Content-Type: application/javascript; charset=UTF-8');
178
		$force_gzip = ( defined('ENFORCE_GZIP') && ENFORCE_GZIP );
179
		$test_str = '"wpCompressionTest Lorem ipsum dolor sit amet consectetuer mollis sapien urna ut a. Eu nonummy condimentum fringilla tempor pretium platea vel nibh netus Maecenas. Hac molestie amet justo quis pellentesque est ultrices interdum nibh Morbi. Cras mattis pretium Phasellus ante ipsum ipsum ut sociis Suspendisse Lorem. Ante et non molestie. Porta urna Vestibulum egestas id congue nibh eu risus gravida sit. Ac augue auctor Ut et non a elit massa id sodales. Elit eu Nulla at nibh adipiscing mattis lacus mauris at tempus. Netus nibh quis suscipit nec feugiat eget sed lorem et urna. Pellentesque lacus at ut massa consectetuer ligula ut auctor semper Pellentesque. Ut metus massa nibh quam Curabitur molestie nec mauris congue. Volutpat molestie elit justo facilisis neque ac risus Ut nascetur tristique. Vitae sit lorem tellus et quis Phasellus lacus tincidunt nunc Fusce. Pharetra wisi Suspendisse mus sagittis libero lacinia Integer consequat ac Phasellus. Et urna ac cursus tortor aliquam Aliquam amet tellus volutpat Vestibulum. Justo interdum condimentum In augue congue tellus sollicitudin Quisque quis nibh."';
180
181
		 if ( 1 == $_GET['test'] ) {
182
		 	echo $test_str;
183
		 	wp_die();
184
		 } elseif ( 2 == $_GET['test'] ) {
185
			if ( !isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
186
				wp_die( -1 );
187
			if ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') && function_exists('gzdeflate') && ! $force_gzip ) {
188
				header('Content-Encoding: deflate');
189
				$out = gzdeflate( $test_str, 1 );
190
			} elseif ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode') ) {
191
				header('Content-Encoding: gzip');
192
				$out = gzencode( $test_str, 1 );
193
			} else {
194
				wp_die( -1 );
195
			}
196
			echo $out;
0 ignored issues
show
The variable $out does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
197
			wp_die();
198
		} elseif ( 'no' == $_GET['test'] ) {
199
			check_ajax_referer( 'update_can_compress_scripts' );
200
			update_site_option('can_compress_scripts', 0);
201
		} elseif ( 'yes' == $_GET['test'] ) {
202
			check_ajax_referer( 'update_can_compress_scripts' );
203
			update_site_option('can_compress_scripts', 1);
204
		}
205
	}
206
207
	wp_die( 0 );
208
}
209
210
/**
211
 * Ajax handler for image editor previews.
212
 *
213
 * @since 3.1.0
214
 */
215
function wp_ajax_imgedit_preview() {
216
	$post_id = intval($_GET['postid']);
217
	if ( empty($post_id) || !current_user_can('edit_post', $post_id) )
218
		wp_die( -1 );
219
220
	check_ajax_referer( "image_editor-$post_id" );
221
222
	include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
223
	if ( ! stream_preview_image($post_id) )
224
		wp_die( -1 );
225
226
	wp_die();
227
}
228
229
/**
230
 * Ajax handler for oEmbed caching.
231
 *
232
 * @since 3.1.0
233
 *
234
 * @global WP_Embed $wp_embed
235
 */
236
function wp_ajax_oembed_cache() {
237
	$GLOBALS['wp_embed']->cache_oembed( $_GET['post'] );
238
	wp_die( 0 );
239
}
240
241
/**
242
 * Ajax handler for user autocomplete.
243
 *
244
 * @since 3.4.0
245
 */
246
function wp_ajax_autocomplete_user() {
247
	if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) )
248
		wp_die( -1 );
249
250
	/** This filter is documented in wp-admin/user-new.php */
251
	if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) )
252
		wp_die( -1 );
253
254
	$return = array();
255
256
	// Check the type of request
257
	// Current allowed values are `add` and `search`
258
	if ( isset( $_REQUEST['autocomplete_type'] ) && 'search' === $_REQUEST['autocomplete_type'] ) {
259
		$type = $_REQUEST['autocomplete_type'];
260
	} else {
261
		$type = 'add';
262
	}
263
264
	// Check the desired field for value
265
	// Current allowed values are `user_email` and `user_login`
266
	if ( isset( $_REQUEST['autocomplete_field'] ) && 'user_email' === $_REQUEST['autocomplete_field'] ) {
267
		$field = $_REQUEST['autocomplete_field'];
268
	} else {
269
		$field = 'user_login';
270
	}
271
272
	// Exclude current users of this blog
273
	if ( isset( $_REQUEST['site_id'] ) ) {
274
		$id = absint( $_REQUEST['site_id'] );
275
	} else {
276
		$id = get_current_blog_id();
277
	}
278
279
	$include_blog_users = ( $type == 'search' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
280
	$exclude_blog_users = ( $type == 'add' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
281
282
	$users = get_users( array(
283
		'blog_id' => false,
284
		'search'  => '*' . $_REQUEST['term'] . '*',
285
		'include' => $include_blog_users,
286
		'exclude' => $exclude_blog_users,
287
		'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ),
288
	) );
289
290
	foreach ( $users as $user ) {
291
		$return[] = array(
292
			/* translators: 1: user_login, 2: user_email */
293
			'label' => sprintf( _x( '%1$s (%2$s)', 'user autocomplete result' ), $user->user_login, $user->user_email ),
294
			'value' => $user->$field,
295
		);
296
	}
297
298
	wp_die( wp_json_encode( $return ) );
0 ignored issues
show
It seems like wp_json_encode($return) targeting wp_json_encode() can also be of type false; however, wp_die() does only seem to accept string|object<WP_Error>, did you maybe forget to handle an error condition?
Loading history...
299
}
300
301
/**
302
 * Ajax handler for dashboard widgets.
303
 *
304
 * @since 3.4.0
305
 */
306
function wp_ajax_dashboard_widgets() {
307
	require_once ABSPATH . 'wp-admin/includes/dashboard.php';
308
309
	$pagenow = $_GET['pagenow'];
310
	if ( $pagenow === 'dashboard-user' || $pagenow === 'dashboard-network' || $pagenow === 'dashboard' ) {
311
		set_current_screen( $pagenow );
312
	}
313
314
	switch ( $_GET['widget'] ) {
315
		case 'dashboard_primary' :
316
			wp_dashboard_primary();
317
			break;
318
	}
319
	wp_die();
320
}
321
322
/**
323
 * Ajax handler for Customizer preview logged-in status.
324
 *
325
 * @since 3.4.0
326
 */
327
function wp_ajax_logged_in() {
328
	wp_die( 1 );
329
}
330
331
//
332
// Ajax helpers.
333
//
334
335
/**
336
 * Sends back current comment total and new page links if they need to be updated.
337
 *
338
 * Contrary to normal success Ajax response ("1"), die with time() on success.
339
 *
340
 * @access private
341
 * @since 2.7.0
342
 *
343
 * @param int $comment_id
344
 * @param int $delta
345
 */
346
function _wp_ajax_delete_comment_response( $comment_id, $delta = -1 ) {
347
	$total    = isset( $_POST['_total'] )    ? (int) $_POST['_total']    : 0;
348
	$per_page = isset( $_POST['_per_page'] ) ? (int) $_POST['_per_page'] : 0;
349
	$page     = isset( $_POST['_page'] )     ? (int) $_POST['_page']     : 0;
350
	$url      = isset( $_POST['_url'] )      ? esc_url_raw( $_POST['_url'] ) : '';
351
352
	// JS didn't send us everything we need to know. Just die with success message
353
	if ( ! $total || ! $per_page || ! $page || ! $url ) {
354
		$time           = time();
355
		$comment        = get_comment( $comment_id );
356
		$comment_status = '';
357
		$comment_link   = '';
358
359
		if ( $comment ) {
360
			$comment_status = $comment->comment_approved;
361
		}
362
363
		if ( 1 === (int) $comment_status ) {
364
			$comment_link = get_comment_link( $comment );
365
		}
366
367
		$counts = wp_count_comments();
368
369
		$x = new WP_Ajax_Response( array(
370
			'what' => 'comment',
371
			// Here for completeness - not used.
372
			'id' => $comment_id,
373
			'supplemental' => array(
374
				'status' => $comment_status,
375
				'postId' => $comment ? $comment->comment_post_ID : '',
376
				'time' => $time,
377
				'in_moderation' => $counts->moderated,
378
				'i18n_comments_text' => sprintf(
379
					_n( '%s Comment', '%s Comments', $counts->approved ),
380
					number_format_i18n( $counts->approved )
381
				),
382
				'i18n_moderation_text' => sprintf(
383
					_nx( '%s in moderation', '%s in moderation', $counts->moderated, 'comments' ),
384
					number_format_i18n( $counts->moderated )
385
				),
386
				'comment_link' => $comment_link,
387
			)
388
		) );
389
		$x->send();
390
	}
391
392
	$total += $delta;
393
	if ( $total < 0 )
394
		$total = 0;
395
396
	// Only do the expensive stuff on a page-break, and about 1 other time per page
397
	if ( 0 == $total % $per_page || 1 == mt_rand( 1, $per_page ) ) {
398
		$post_id = 0;
399
		// What type of comment count are we looking for?
400
		$status = 'all';
401
		$parsed = parse_url( $url );
402
		if ( isset( $parsed['query'] ) ) {
403
			parse_str( $parsed['query'], $query_vars );
404
			if ( !empty( $query_vars['comment_status'] ) )
405
				$status = $query_vars['comment_status'];
406
			if ( !empty( $query_vars['p'] ) )
407
				$post_id = (int) $query_vars['p'];
408
			if ( ! empty( $query_vars['comment_type'] ) )
409
				$type = $query_vars['comment_type'];
410
		}
411
412
		if ( empty( $type ) ) {
413
			// Only use the comment count if not filtering by a comment_type.
414
			$comment_count = wp_count_comments($post_id);
415
416
			// We're looking for a known type of comment count.
417
			if ( isset( $comment_count->$status ) ) {
418
				$total = $comment_count->$status;
419
			}
420
		}
421
		// Else use the decremented value from above.
422
	}
423
424
	// The time since the last comment count.
425
	$time = time();
426
	$comment = get_comment( $comment_id );
427
428
	$x = new WP_Ajax_Response( array(
429
		'what' => 'comment',
430
		// Here for completeness - not used.
431
		'id' => $comment_id,
432
		'supplemental' => array(
433
			'status' => $comment ? $comment->comment_approved : '',
434
			'postId' => $comment ? $comment->comment_post_ID : '',
435
			'total_items_i18n' => sprintf( _n( '%s item', '%s items', $total ), number_format_i18n( $total ) ),
436
			'total_pages' => ceil( $total / $per_page ),
437
			'total_pages_i18n' => number_format_i18n( ceil( $total / $per_page ) ),
438
			'total' => $total,
439
			'time' => $time
440
		)
441
	) );
442
	$x->send();
443
}
444
445
//
446
// POST-based Ajax handlers.
447
//
448
449
/**
450
 * Ajax handler for adding a hierarchical term.
451
 *
452
 * @access private
453
 * @since 3.1.0
454
 */
455
function _wp_ajax_add_hierarchical_term() {
456
	$action = $_POST['action'];
457
	$taxonomy = get_taxonomy(substr($action, 4));
458
	check_ajax_referer( $action, '_ajax_nonce-add-' . $taxonomy->name );
459
	if ( !current_user_can( $taxonomy->cap->edit_terms ) )
460
		wp_die( -1 );
461
	$names = explode(',', $_POST['new'.$taxonomy->name]);
462
	$parent = isset($_POST['new'.$taxonomy->name.'_parent']) ? (int) $_POST['new'.$taxonomy->name.'_parent'] : 0;
463
	if ( 0 > $parent )
464
		$parent = 0;
465
	if ( $taxonomy->name == 'category' )
466
		$post_category = isset($_POST['post_category']) ? (array) $_POST['post_category'] : array();
467
	else
468
		$post_category = ( isset($_POST['tax_input']) && isset($_POST['tax_input'][$taxonomy->name]) ) ? (array) $_POST['tax_input'][$taxonomy->name] : array();
469
	$checked_categories = array_map( 'absint', (array) $post_category );
470
	$popular_ids = wp_popular_terms_checklist($taxonomy->name, 0, 10, false);
471
472
	foreach ( $names as $cat_name ) {
473
		$cat_name = trim($cat_name);
474
		$category_nicename = sanitize_title($cat_name);
475
		if ( '' === $category_nicename )
476
			continue;
477
		if ( !$cat_id = term_exists( $cat_name, $taxonomy->name, $parent ) )
478
			$cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) );
479
		if ( is_wp_error( $cat_id ) ) {
480
			continue;
481
		} elseif ( is_array( $cat_id ) ) {
482
			$cat_id = $cat_id['term_id'];
483
		}
484
		$checked_categories[] = $cat_id;
485
		if ( $parent ) // Do these all at once in a second
486
			continue;
487
488
		ob_start();
489
490
		wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name, 'descendants_and_self' => $cat_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids ));
491
492
		$data = ob_get_clean();
493
494
		$add = array(
495
			'what' => $taxonomy->name,
496
			'id' => $cat_id,
497
			'data' => str_replace( array("\n", "\t"), '', $data),
498
			'position' => -1
499
		);
500
	}
501
502
	if ( $parent ) { // Foncy - replace the parent and all its children
503
		$parent = get_term( $parent, $taxonomy->name );
504
		$term_id = $parent->term_id;
505
506 View Code Duplication
		while ( $parent->parent ) { // get the top parent
507
			$parent = get_term( $parent->parent, $taxonomy->name );
508
			if ( is_wp_error( $parent ) )
509
				break;
510
			$term_id = $parent->term_id;
511
		}
512
513
		ob_start();
514
515
		wp_terms_checklist( 0, array('taxonomy' => $taxonomy->name, 'descendants_and_self' => $term_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids));
516
517
		$data = ob_get_clean();
518
519
		$add = array(
520
			'what' => $taxonomy->name,
521
			'id' => $term_id,
522
			'data' => str_replace( array("\n", "\t"), '', $data),
523
			'position' => -1
524
		);
525
	}
526
527
	ob_start();
528
529
	wp_dropdown_categories( array(
530
		'taxonomy' => $taxonomy->name, 'hide_empty' => 0, 'name' => 'new'.$taxonomy->name.'_parent', 'orderby' => 'name',
531
		'hierarchical' => 1, 'show_option_none' => '&mdash; '.$taxonomy->labels->parent_item.' &mdash;'
532
	) );
533
534
	$sup = ob_get_clean();
535
536
	$add['supplemental'] = array( 'newcat_parent' => $sup );
0 ignored issues
show
The variable $add does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
537
538
	$x = new WP_Ajax_Response( $add );
539
	$x->send();
540
}
541
542
/**
543
 * Ajax handler for deleting a comment.
544
 *
545
 * @since 3.1.0
546
 */
547
function wp_ajax_delete_comment() {
548
	$id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
549
550
	if ( !$comment = get_comment( $id ) )
551
		wp_die( time() );
552
	if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
553
		wp_die( -1 );
554
555
	check_ajax_referer( "delete-comment_$id" );
556
	$status = wp_get_comment_status( $comment );
557
558
	$delta = -1;
559
	if ( isset($_POST['trash']) && 1 == $_POST['trash'] ) {
560
		if ( 'trash' == $status )
561
			wp_die( time() );
562
		$r = wp_trash_comment( $comment );
563 View Code Duplication
	} elseif ( isset($_POST['untrash']) && 1 == $_POST['untrash'] ) {
564
		if ( 'trash' != $status )
565
			wp_die( time() );
566
		$r = wp_untrash_comment( $comment );
567
		if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'trash' ) // undo trash, not in trash
568
			$delta = 1;
569
	} elseif ( isset($_POST['spam']) && 1 == $_POST['spam'] ) {
570
		if ( 'spam' == $status )
571
			wp_die( time() );
572
		$r = wp_spam_comment( $comment );
573 View Code Duplication
	} elseif ( isset($_POST['unspam']) && 1 == $_POST['unspam'] ) {
574
		if ( 'spam' != $status )
575
			wp_die( time() );
576
		$r = wp_unspam_comment( $comment );
577
		if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'spam' ) // undo spam, not in spam
578
			$delta = 1;
579
	} elseif ( isset($_POST['delete']) && 1 == $_POST['delete'] ) {
580
		$r = wp_delete_comment( $comment );
581
	} else {
582
		wp_die( -1 );
583
	}
584
585
	if ( $r ) // Decide if we need to send back '1' or a more complicated response including page links and comment counts
0 ignored issues
show
The variable $r does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
586
		_wp_ajax_delete_comment_response( $comment->comment_ID, $delta );
587
	wp_die( 0 );
588
}
589
590
/**
591
 * Ajax handler for deleting a tag.
592
 *
593
 * @since 3.1.0
594
 */
595
function wp_ajax_delete_tag() {
596
	$tag_id = (int) $_POST['tag_ID'];
597
	check_ajax_referer( "delete-tag_$tag_id" );
598
599
	$taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
600
	$tax = get_taxonomy($taxonomy);
601
602
	if ( !current_user_can( $tax->cap->delete_terms ) )
603
		wp_die( -1 );
604
605
	$tag = get_term( $tag_id, $taxonomy );
606
	if ( !$tag || is_wp_error( $tag ) )
607
		wp_die( 1 );
608
609
	if ( wp_delete_term($tag_id, $taxonomy))
610
		wp_die( 1 );
611
	else
612
		wp_die( 0 );
613
}
614
615
/**
616
 * Ajax handler for deleting a link.
617
 *
618
 * @since 3.1.0
619
 */
620 View Code Duplication
function wp_ajax_delete_link() {
0 ignored issues
show
This function seems to be duplicated in your project.

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

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

Loading history...
621
	$id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
622
623
	check_ajax_referer( "delete-bookmark_$id" );
624
	if ( !current_user_can( 'manage_links' ) )
625
		wp_die( -1 );
626
627
	$link = get_bookmark( $id );
628
	if ( !$link || is_wp_error( $link ) )
629
		wp_die( 1 );
630
631
	if ( wp_delete_link( $id ) )
632
		wp_die( 1 );
633
	else
634
		wp_die( 0 );
635
}
636
637
/**
638
 * Ajax handler for deleting meta.
639
 *
640
 * @since 3.1.0
641
 */
642
function wp_ajax_delete_meta() {
643
	$id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
644
645
	check_ajax_referer( "delete-meta_$id" );
646
	if ( !$meta = get_metadata_by_mid( 'post', $id ) )
647
		wp_die( 1 );
648
649
	if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta',  $meta->post_id, $meta->meta_key ) )
650
		wp_die( -1 );
651
	if ( delete_meta( $meta->meta_id ) )
652
		wp_die( 1 );
653
	wp_die( 0 );
654
}
655
656
/**
657
 * Ajax handler for deleting a post.
658
 *
659
 * @since 3.1.0
660
 *
661
 * @param string $action Action to perform.
662
 */
663 View Code Duplication
function wp_ajax_delete_post( $action ) {
0 ignored issues
show
This function seems to be duplicated in your project.

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

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

Loading history...
664
	if ( empty( $action ) )
665
		$action = 'delete-post';
666
	$id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
667
668
	check_ajax_referer( "{$action}_$id" );
669
	if ( !current_user_can( 'delete_post', $id ) )
670
		wp_die( -1 );
671
672
	if ( !get_post( $id ) )
673
		wp_die( 1 );
674
675
	if ( wp_delete_post( $id ) )
676
		wp_die( 1 );
677
	else
678
		wp_die( 0 );
679
}
680
681
/**
682
 * Ajax handler for sending a post to the trash.
683
 *
684
 * @since 3.1.0
685
 *
686
 * @param string $action Action to perform.
687
 */
688
function wp_ajax_trash_post( $action ) {
689
	if ( empty( $action ) )
690
		$action = 'trash-post';
691
	$id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
692
693
	check_ajax_referer( "{$action}_$id" );
694
	if ( !current_user_can( 'delete_post', $id ) )
695
		wp_die( -1 );
696
697
	if ( !get_post( $id ) )
698
		wp_die( 1 );
699
700
	if ( 'trash-post' == $action )
701
		$done = wp_trash_post( $id );
702
	else
703
		$done = wp_untrash_post( $id );
704
705
	if ( $done )
706
		wp_die( 1 );
707
708
	wp_die( 0 );
709
}
710
711
/**
712
 * Ajax handler to restore a post from the trash.
713
 *
714
 * @since 3.1.0
715
 *
716
 * @param string $action Action to perform.
717
 */
718
function wp_ajax_untrash_post( $action ) {
719
	if ( empty( $action ) )
720
		$action = 'untrash-post';
721
	wp_ajax_trash_post( $action );
722
}
723
724
/**
725
 * @since 3.1.0
726
 *
727
 * @param string $action
728
 */
729 View Code Duplication
function wp_ajax_delete_page( $action ) {
0 ignored issues
show
This function seems to be duplicated in your project.

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

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

Loading history...
730
	if ( empty( $action ) )
731
		$action = 'delete-page';
732
	$id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
733
734
	check_ajax_referer( "{$action}_$id" );
735
	if ( !current_user_can( 'delete_page', $id ) )
736
		wp_die( -1 );
737
738
	if ( ! get_post( $id ) )
739
		wp_die( 1 );
740
741
	if ( wp_delete_post( $id ) )
742
		wp_die( 1 );
743
	else
744
		wp_die( 0 );
745
}
746
747
/**
748
 * Ajax handler to dim a comment.
749
 *
750
 * @since 3.1.0
751
 */
752
function wp_ajax_dim_comment() {
753
	$id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
754
755
	if ( !$comment = get_comment( $id ) ) {
756
		$x = new WP_Ajax_Response( array(
757
			'what' => 'comment',
758
			'id' => new WP_Error('invalid_comment', sprintf(__('Comment %d does not exist'), $id))
759
		) );
760
		$x->send();
761
	}
762
763
	if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && ! current_user_can( 'moderate_comments' ) )
764
		wp_die( -1 );
765
766
	$current = wp_get_comment_status( $comment );
767
	if ( isset( $_POST['new'] ) && $_POST['new'] == $current )
768
		wp_die( time() );
769
770
	check_ajax_referer( "approve-comment_$id" );
771
	if ( in_array( $current, array( 'unapproved', 'spam' ) ) ) {
772
		$result = wp_set_comment_status( $comment, 'approve', true );
773
	} else {
774
		$result = wp_set_comment_status( $comment, 'hold', true );
775
	}
776
777
	if ( is_wp_error($result) ) {
778
		$x = new WP_Ajax_Response( array(
779
			'what' => 'comment',
780
			'id' => $result
781
		) );
782
		$x->send();
783
	}
784
785
	// Decide if we need to send back '1' or a more complicated response including page links and comment counts
786
	_wp_ajax_delete_comment_response( $comment->comment_ID );
787
	wp_die( 0 );
788
}
789
790
/**
791
 * Ajax handler for deleting a link category.
792
 *
793
 * @since 3.1.0
794
 *
795
 * @param string $action Action to perform.
796
 */
797
function wp_ajax_add_link_category( $action ) {
798
	if ( empty( $action ) )
799
		$action = 'add-link-category';
800
	check_ajax_referer( $action );
801
	if ( !current_user_can( 'manage_categories' ) )
802
		wp_die( -1 );
803
	$names = explode(',', wp_unslash( $_POST['newcat'] ) );
804
	$x = new WP_Ajax_Response();
805
	foreach ( $names as $cat_name ) {
806
		$cat_name = trim($cat_name);
807
		$slug = sanitize_title($cat_name);
808
		if ( '' === $slug )
809
			continue;
810
		if ( !$cat_id = term_exists( $cat_name, 'link_category' ) )
811
			$cat_id = wp_insert_term( $cat_name, 'link_category' );
812
		if ( is_wp_error( $cat_id ) ) {
813
			continue;
814
		} elseif ( is_array( $cat_id ) ) {
815
			$cat_id = $cat_id['term_id'];
816
		}
817
		$cat_name = esc_html( $cat_name );
818
		$x->add( array(
819
			'what' => 'link-category',
820
			'id' => $cat_id,
821
			'data' => "<li id='link-category-$cat_id'><label for='in-link-category-$cat_id' class='selectit'><input value='" . esc_attr($cat_id) . "' type='checkbox' checked='checked' name='link_category[]' id='in-link-category-$cat_id'/> $cat_name</label></li>",
822
			'position' => -1
823
		) );
824
	}
825
	$x->send();
826
}
827
828
/**
829
 * Ajax handler to add a tag.
830
 *
831
 * @since 3.1.0
832
 *
833
 * @global WP_List_Table $wp_list_table
834
 */
835
function wp_ajax_add_tag() {
836
	global $wp_list_table;
837
838
	check_ajax_referer( 'add-tag', '_wpnonce_add-tag' );
839
	$taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
840
	$tax = get_taxonomy($taxonomy);
841
842
	if ( !current_user_can( $tax->cap->edit_terms ) )
843
		wp_die( -1 );
844
845
	$x = new WP_Ajax_Response();
846
847
	$tag = wp_insert_term($_POST['tag-name'], $taxonomy, $_POST );
848
849
	if ( !$tag || is_wp_error($tag) || (!$tag = get_term( $tag['term_id'], $taxonomy )) ) {
850
		$message = __('An error has occurred. Please reload the page and try again.');
851
		if ( is_wp_error($tag) && $tag->get_error_message() )
852
			$message = $tag->get_error_message();
0 ignored issues
show
The method get_error_message does only exist in WP_Error, but not in WP_Term.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
853
854
		$x->add( array(
855
			'what' => 'taxonomy',
856
			'data' => new WP_Error('error', $message )
857
		) );
858
		$x->send();
859
	}
860
861
	$wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) );
862
863
	$level = 0;
864
	if ( is_taxonomy_hierarchical($taxonomy) ) {
865
		$level = count( get_ancestors( $tag->term_id, $taxonomy, 'taxonomy' ) );
866
		ob_start();
867
		$wp_list_table->single_row( $tag, $level );
868
		$noparents = ob_get_clean();
869
	}
870
871
	ob_start();
872
	$wp_list_table->single_row( $tag );
873
	$parents = ob_get_clean();
874
875
	$x->add( array(
876
		'what' => 'taxonomy',
877
		'supplemental' => compact('parents', 'noparents')
878
	) );
879
	$x->add( array(
880
		'what' => 'term',
881
		'position' => $level,
882
		'supplemental' => (array) $tag
883
	) );
884
	$x->send();
885
}
886
887
/**
888
 * Ajax handler for getting a tagcloud.
889
 *
890
 * @since 3.1.0
891
 */
892
function wp_ajax_get_tagcloud() {
893
	if ( ! isset( $_POST['tax'] ) ) {
894
		wp_die( 0 );
895
	}
896
897
	$taxonomy = sanitize_key( $_POST['tax'] );
898
	$tax = get_taxonomy( $taxonomy );
899
	if ( ! $tax ) {
900
		wp_die( 0 );
901
	}
902
903
	if ( ! current_user_can( $tax->cap->assign_terms ) ) {
904
		wp_die( -1 );
905
	}
906
907
	$tags = get_terms( $taxonomy, array( 'number' => 45, 'orderby' => 'count', 'order' => 'DESC' ) );
908
909
	if ( empty( $tags ) )
910
		wp_die( $tax->labels->not_found );
911
912
	if ( is_wp_error( $tags ) )
913
		wp_die( $tags->get_error_message() );
914
915
	foreach ( $tags as $key => $tag ) {
0 ignored issues
show
The expression $tags of type array|integer|object<WP_Error> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
916
		$tags[ $key ]->link = '#';
917
		$tags[ $key ]->id = $tag->term_id;
918
	}
919
920
	// We need raw tag names here, so don't filter the output
921
	$return = wp_generate_tag_cloud( $tags, array('filter' => 0) );
922
923
	if ( empty($return) )
924
		wp_die( 0 );
925
926
	echo $return;
927
928
	wp_die();
929
}
930
931
/**
932
 * Ajax handler for getting comments.
933
 *
934
 * @since 3.1.0
935
 *
936
 * @global WP_List_Table $wp_list_table
937
 * @global int           $post_id
938
 *
939
 * @param string $action Action to perform.
940
 */
941
function wp_ajax_get_comments( $action ) {
942
	global $wp_list_table, $post_id;
943
	if ( empty( $action ) )
944
		$action = 'get-comments';
945
946
	check_ajax_referer( $action );
947
948
	if ( empty( $post_id ) && ! empty( $_REQUEST['p'] ) ) {
949
		$id = absint( $_REQUEST['p'] );
950
		if ( ! empty( $id ) )
951
			$post_id = $id;
952
	}
953
954
	if ( empty( $post_id ) )
955
		wp_die( -1 );
956
957
	$wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
958
959
	if ( ! current_user_can( 'edit_post', $post_id ) )
960
		wp_die( -1 );
961
962
	$wp_list_table->prepare_items();
963
964
	if ( !$wp_list_table->has_items() )
965
		wp_die( 1 );
966
967
	$x = new WP_Ajax_Response();
968
	ob_start();
969
	foreach ( $wp_list_table->items as $comment ) {
970
		if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && 0 === $comment->comment_approved )
971
			continue;
972
		get_comment( $comment );
973
		$wp_list_table->single_row( $comment );
974
	}
975
	$comment_list_item = ob_get_clean();
976
977
	$x->add( array(
978
		'what' => 'comments',
979
		'data' => $comment_list_item
980
	) );
981
	$x->send();
982
}
983
984
/**
985
 * Ajax handler for replying to a comment.
986
 *
987
 * @since 3.1.0
988
 *
989
 * @global WP_List_Table $wp_list_table
990
 *
991
 * @param string $action Action to perform.
992
 */
993
function wp_ajax_replyto_comment( $action ) {
994
	global $wp_list_table;
995
	if ( empty( $action ) )
996
		$action = 'replyto-comment';
997
998
	check_ajax_referer( $action, '_ajax_nonce-replyto-comment' );
999
1000
	$comment_post_ID = (int) $_POST['comment_post_ID'];
1001
	$post = get_post( $comment_post_ID );
1002
	if ( ! $post )
1003
		wp_die( -1 );
1004
1005
	if ( !current_user_can( 'edit_post', $comment_post_ID ) )
1006
		wp_die( -1 );
1007
1008
	if ( empty( $post->post_status ) )
1009
		wp_die( 1 );
1010
	elseif ( in_array($post->post_status, array('draft', 'pending', 'trash') ) )
1011
		wp_die( __('ERROR: you are replying to a comment on a draft post.') );
1012
1013
	$user = wp_get_current_user();
1014
	if ( $user->exists() ) {
1015
		$user_ID = $user->ID;
1016
		$comment_author       = wp_slash( $user->display_name );
1017
		$comment_author_email = wp_slash( $user->user_email );
1018
		$comment_author_url   = wp_slash( $user->user_url );
1019
		$comment_content      = trim( $_POST['content'] );
1020
		$comment_type         = isset( $_POST['comment_type'] ) ? trim( $_POST['comment_type'] ) : '';
1021
		if ( current_user_can( 'unfiltered_html' ) ) {
1022
			if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) )
1023
				$_POST['_wp_unfiltered_html_comment'] = '';
1024
1025
			if ( wp_create_nonce( 'unfiltered-html-comment' ) != $_POST['_wp_unfiltered_html_comment'] ) {
1026
				kses_remove_filters(); // start with a clean slate
1027
				kses_init_filters(); // set up the filters
1028
			}
1029
		}
1030
	} else {
1031
		wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) );
1032
	}
1033
1034
	if ( '' == $comment_content )
0 ignored issues
show
The variable $comment_content does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1035
		wp_die( __( 'ERROR: please type a comment.' ) );
1036
1037
	$comment_parent = 0;
1038
	if ( isset( $_POST['comment_ID'] ) )
1039
		$comment_parent = absint( $_POST['comment_ID'] );
1040
	$comment_auto_approved = false;
1041
	$commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID');
1042
1043
	// Automatically approve parent comment.
1044
	if ( !empty($_POST['approve_parent']) ) {
1045
		$parent = get_comment( $comment_parent );
1046
1047
		if ( $parent && $parent->comment_approved === '0' && $parent->comment_post_ID == $comment_post_ID ) {
1048
			if ( ! current_user_can( 'edit_comment', $parent->comment_ID ) ) {
1049
				wp_die( -1 );
1050
			}
1051
1052
			if ( wp_set_comment_status( $parent, 'approve' ) )
0 ignored issues
show
It seems like $parent defined by get_comment($comment_parent) on line 1045 can also be of type array; however, wp_set_comment_status() 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...
1053
				$comment_auto_approved = true;
1054
		}
1055
	}
1056
1057
	$comment_id = wp_new_comment( $commentdata );
1058
	$comment = get_comment($comment_id);
1059
	if ( ! $comment ) wp_die( 1 );
1060
1061
	$position = ( isset($_POST['position']) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1';
1062
1063
	ob_start();
1064
	if ( isset( $_REQUEST['mode'] ) && 'dashboard' == $_REQUEST['mode'] ) {
1065
		require_once( ABSPATH . 'wp-admin/includes/dashboard.php' );
1066
		_wp_dashboard_recent_comments_row( $comment );
0 ignored issues
show
It seems like $comment defined by get_comment($comment_id) on line 1058 can also be of type array or null; however, _wp_dashboard_recent_comments_row() does only seem to accept 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...
1067
	} else {
1068
		if ( isset( $_REQUEST['mode'] ) && 'single' == $_REQUEST['mode'] ) {
1069
			$wp_list_table = _get_list_table('WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1070
		} else {
1071
			$wp_list_table = _get_list_table('WP_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1072
		}
1073
		$wp_list_table->single_row( $comment );
1074
	}
1075
	$comment_list_item = ob_get_clean();
1076
1077
	$response =  array(
1078
		'what' => 'comment',
1079
		'id' => $comment->comment_ID,
1080
		'data' => $comment_list_item,
1081
		'position' => $position
1082
	);
1083
1084
	$counts = wp_count_comments();
1085
	$response['supplemental'] = array(
1086
		'in_moderation' => $counts->moderated,
1087
		'i18n_comments_text' => sprintf(
1088
			_n( '%s Comment', '%s Comments', $counts->approved ),
1089
			number_format_i18n( $counts->approved )
1090
		),
1091
		'i18n_moderation_text' => sprintf(
1092
			_nx( '%s in moderation', '%s in moderation', $counts->moderated, 'comments' ),
1093
			number_format_i18n( $counts->moderated )
1094
		)
1095
	);
1096
1097
	if ( $comment_auto_approved ) {
1098
		$response['supplemental']['parent_approved'] = $parent->comment_ID;
0 ignored issues
show
The variable $parent does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1099
		$response['supplemental']['parent_post_id'] = $parent->comment_post_ID;
1100
	}
1101
1102
	$x = new WP_Ajax_Response();
1103
	$x->add( $response );
1104
	$x->send();
1105
}
1106
1107
/**
1108
 * Ajax handler for editing a comment.
1109
 *
1110
 * @since 3.1.0
1111
 *
1112
 * @global WP_List_Table $wp_list_table
1113
 */
1114
function wp_ajax_edit_comment() {
1115
	global $wp_list_table;
1116
1117
	check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' );
1118
1119
	$comment_id = (int) $_POST['comment_ID'];
1120
	if ( ! current_user_can( 'edit_comment', $comment_id ) )
1121
		wp_die( -1 );
1122
1123
	if ( '' == $_POST['content'] )
1124
		wp_die( __( 'ERROR: please type a comment.' ) );
1125
1126
	if ( isset( $_POST['status'] ) )
1127
		$_POST['comment_status'] = $_POST['status'];
1128
	edit_comment();
1129
1130
	$position = ( isset($_POST['position']) && (int) $_POST['position']) ? (int) $_POST['position'] : '-1';
1131
	$checkbox = ( isset($_POST['checkbox']) && true == $_POST['checkbox'] ) ? 1 : 0;
1132
	$wp_list_table = _get_list_table( $checkbox ? 'WP_Comments_List_Table' : 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1133
1134
	$comment = get_comment( $comment_id );
1135
	if ( empty( $comment->comment_ID ) )
1136
		wp_die( -1 );
1137
1138
	ob_start();
1139
	$wp_list_table->single_row( $comment );
1140
	$comment_list_item = ob_get_clean();
1141
1142
	$x = new WP_Ajax_Response();
1143
1144
	$x->add( array(
1145
		'what' => 'edit_comment',
1146
		'id' => $comment->comment_ID,
1147
		'data' => $comment_list_item,
1148
		'position' => $position
1149
	));
1150
1151
	$x->send();
1152
}
1153
1154
/**
1155
 * Ajax handler for adding a menu item.
1156
 *
1157
 * @since 3.1.0
1158
 */
1159
function wp_ajax_add_menu_item() {
1160
	check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1161
1162
	if ( ! current_user_can( 'edit_theme_options' ) )
1163
		wp_die( -1 );
1164
1165
	require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1166
1167
	// For performance reasons, we omit some object properties from the checklist.
1168
	// The following is a hacky way to restore them when adding non-custom items.
1169
1170
	$menu_items_data = array();
1171
	foreach ( (array) $_POST['menu-item'] as $menu_item_data ) {
1172
		if (
1173
			! empty( $menu_item_data['menu-item-type'] ) &&
1174
			'custom' != $menu_item_data['menu-item-type'] &&
1175
			! empty( $menu_item_data['menu-item-object-id'] )
1176
		) {
1177
			switch( $menu_item_data['menu-item-type'] ) {
1178
				case 'post_type' :
1179
					$_object = get_post( $menu_item_data['menu-item-object-id'] );
1180
				break;
1181
1182
				case 'post_type_archive' :
1183
					$_object = get_post_type_object( $menu_item_data['menu-item-object'] );
1184
				break;
1185
1186
				case 'taxonomy' :
1187
					$_object = get_term( $menu_item_data['menu-item-object-id'], $menu_item_data['menu-item-object'] );
1188
				break;
1189
			}
1190
1191
			$_menu_items = array_map( 'wp_setup_nav_menu_item', array( $_object ) );
0 ignored issues
show
The variable $_object does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1192
			$_menu_item = reset( $_menu_items );
1193
1194
			// Restore the missing menu item properties
1195
			$menu_item_data['menu-item-description'] = $_menu_item->description;
1196
		}
1197
1198
		$menu_items_data[] = $menu_item_data;
1199
	}
1200
1201
	$item_ids = wp_save_nav_menu_items( 0, $menu_items_data );
1202
	if ( is_wp_error( $item_ids ) )
1203
		wp_die( 0 );
1204
1205
	$menu_items = array();
1206
1207
	foreach ( (array) $item_ids as $menu_item_id ) {
1208
		$menu_obj = get_post( $menu_item_id );
1209
		if ( ! empty( $menu_obj->ID ) ) {
1210
			$menu_obj = wp_setup_nav_menu_item( $menu_obj );
0 ignored issues
show
It seems like $menu_obj can also be of type array; however, wp_setup_nav_menu_item() does only seem to accept object, 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...
1211
			$menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items
1212
			$menu_items[] = $menu_obj;
1213
		}
1214
	}
1215
1216
	/** This filter is documented in wp-admin/includes/nav-menu.php */
1217
	$walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $_POST['menu'] );
1218
1219
	if ( ! class_exists( $walker_class_name ) )
1220
		wp_die( 0 );
1221
1222
	if ( ! empty( $menu_items ) ) {
1223
		$args = array(
1224
			'after' => '',
1225
			'before' => '',
1226
			'link_after' => '',
1227
			'link_before' => '',
1228
			'walker' => new $walker_class_name,
1229
		);
1230
		echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
1231
	}
1232
	wp_die();
1233
}
1234
1235
/**
1236
 * Ajax handler for adding meta.
1237
 *
1238
 * @since 3.1.0
1239
 */
1240
function wp_ajax_add_meta() {
1241
	check_ajax_referer( 'add-meta', '_ajax_nonce-add-meta' );
1242
	$c = 0;
1243
	$pid = (int) $_POST['post_id'];
1244
	$post = get_post( $pid );
1245
1246
	if ( isset($_POST['metakeyselect']) || isset($_POST['metakeyinput']) ) {
1247
		if ( !current_user_can( 'edit_post', $pid ) )
1248
			wp_die( -1 );
1249
		if ( isset($_POST['metakeyselect']) && '#NONE#' == $_POST['metakeyselect'] && empty($_POST['metakeyinput']) )
1250
			wp_die( 1 );
1251
1252
		// If the post is an autodraft, save the post as a draft and then attempt to save the meta.
1253
		if ( $post->post_status == 'auto-draft' ) {
1254
			$post_data = array();
1255
			$post_data['action'] = 'draft'; // Warning fix
1256
			$post_data['post_ID'] = $pid;
1257
			$post_data['post_type'] = $post->post_type;
1258
			$post_data['post_status'] = 'draft';
1259
			$now = current_time('timestamp', 1);
1260
			$post_data['post_title'] = sprintf( __( 'Draft created on %1$s at %2$s' ), date( __( 'F j, Y' ), $now ), date( __( 'g:i a' ), $now ) );
1261
1262
			$pid = edit_post( $post_data );
1263
			if ( $pid ) {
1264 View Code Duplication
				if ( is_wp_error( $pid ) ) {
1265
					$x = new WP_Ajax_Response( array(
1266
						'what' => 'meta',
1267
						'data' => $pid
1268
					) );
1269
					$x->send();
1270
				}
1271
1272
				if ( !$mid = add_meta( $pid ) )
1273
					wp_die( __( 'Please provide a custom field value.' ) );
1274
			} else {
1275
				wp_die( 0 );
1276
			}
1277
		} elseif ( ! $mid = add_meta( $pid ) ) {
1278
			wp_die( __( 'Please provide a custom field value.' ) );
1279
		}
1280
1281
		$meta = get_metadata_by_mid( 'post', $mid );
0 ignored issues
show
The variable $mid does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
It seems like $mid can also be of type false; however, get_metadata_by_mid() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
1282
		$pid = (int) $meta->post_id;
1283
		$meta = get_object_vars( $meta );
1284
		$x = new WP_Ajax_Response( array(
1285
			'what' => 'meta',
1286
			'id' => $mid,
1287
			'data' => _list_meta_row( $meta, $c ),
1288
			'position' => 1,
1289
			'supplemental' => array('postid' => $pid)
1290
		) );
1291
	} else { // Update?
1292
		$mid = (int) key( $_POST['meta'] );
1293
		$key = wp_unslash( $_POST['meta'][$mid]['key'] );
1294
		$value = wp_unslash( $_POST['meta'][$mid]['value'] );
1295
		if ( '' == trim($key) )
1296
			wp_die( __( 'Please provide a custom field name.' ) );
1297
		if ( '' == trim($value) )
1298
			wp_die( __( 'Please provide a custom field value.' ) );
1299
		if ( ! $meta = get_metadata_by_mid( 'post', $mid ) )
1300
			wp_die( 0 ); // if meta doesn't exist
1301
		if ( is_protected_meta( $meta->meta_key, 'post' ) || is_protected_meta( $key, 'post' ) ||
0 ignored issues
show
It seems like $key defined by wp_unslash($_POST['meta'][$mid]['key']) on line 1293 can also be of type array; however, is_protected_meta() 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...
1302
			! current_user_can( 'edit_post_meta', $meta->post_id, $meta->meta_key ) ||
1303
			! current_user_can( 'edit_post_meta', $meta->post_id, $key ) )
1304
			wp_die( -1 );
1305
		if ( $meta->meta_value != $value || $meta->meta_key != $key ) {
1306
			if ( !$u = update_metadata_by_mid( 'post', $mid, $value, $key ) )
0 ignored issues
show
It seems like $value defined by wp_unslash($_POST['meta'][$mid]['value']) on line 1294 can also be of type array; however, update_metadata_by_mid() 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...
It seems like $key defined by wp_unslash($_POST['meta'][$mid]['key']) on line 1293 can also be of type array; however, update_metadata_by_mid() does only seem to accept false|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...
1307
				wp_die( 0 ); // We know meta exists; we also know it's unchanged (or DB error, in which case there are bigger problems).
1308
		}
1309
1310
		$x = new WP_Ajax_Response( array(
1311
			'what' => 'meta',
1312
			'id' => $mid, 'old_id' => $mid,
1313
			'data' => _list_meta_row( array(
1314
				'meta_key' => $key,
1315
				'meta_value' => $value,
1316
				'meta_id' => $mid
1317
			), $c ),
1318
			'position' => 0,
1319
			'supplemental' => array('postid' => $meta->post_id)
1320
		) );
1321
	}
1322
	$x->send();
1323
}
1324
1325
/**
1326
 * Ajax handler for adding a user.
1327
 *
1328
 * @since 3.1.0
1329
 *
1330
 * @global WP_List_Table $wp_list_table
1331
 *
1332
 * @param string $action Action to perform.
1333
 */
1334
function wp_ajax_add_user( $action ) {
1335
	global $wp_list_table;
1336
	if ( empty( $action ) )
1337
		$action = 'add-user';
1338
1339
	check_ajax_referer( $action );
1340
	if ( ! current_user_can('create_users') )
1341
		wp_die( -1 );
1342
	if ( ! $user_id = edit_user() ) {
1343
		wp_die( 0 );
1344 View Code Duplication
	} elseif ( is_wp_error( $user_id ) ) {
1345
		$x = new WP_Ajax_Response( array(
1346
			'what' => 'user',
1347
			'id' => $user_id
1348
		) );
1349
		$x->send();
1350
	}
1351
	$user_object = get_userdata( $user_id );
0 ignored issues
show
It seems like $user_id defined by edit_user() on line 1342 can also be of type object<WP_Error>; however, get_userdata() does only seem to accept integer, 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...
1352
1353
	$wp_list_table = _get_list_table('WP_Users_List_Table');
1354
1355
	$role = current( $user_object->roles );
1356
1357
	$x = new WP_Ajax_Response( array(
1358
		'what' => 'user',
1359
		'id' => $user_id,
1360
		'data' => $wp_list_table->single_row( $user_object, '', $role ),
1361
		'supplemental' => array(
1362
			'show-link' => sprintf(
1363
				/* translators: %s: the new user */
1364
				__( 'User %s added' ),
1365
				'<a href="#user-' . $user_id . '">' . $user_object->user_login . '</a>'
1366
			),
1367
			'role' => $role,
1368
		)
1369
	) );
1370
	$x->send();
1371
}
1372
1373
/**
1374
 * Ajax handler for closed post boxes.
1375
 *
1376
 * @since 3.1.0
1377
 */
1378
function wp_ajax_closed_postboxes() {
1379
	check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' );
1380
	$closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array();
1381
	$closed = array_filter($closed);
1382
1383
	$hidden = isset( $_POST['hidden'] ) ? explode( ',', $_POST['hidden']) : array();
1384
	$hidden = array_filter($hidden);
1385
1386
	$page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1387
1388
	if ( $page != sanitize_key( $page ) )
1389
		wp_die( 0 );
1390
1391
	if ( ! $user = wp_get_current_user() )
1392
		wp_die( -1 );
1393
1394
	if ( is_array($closed) )
1395
		update_user_option($user->ID, "closedpostboxes_$page", $closed, true);
1396
1397
	if ( is_array($hidden) ) {
1398
		$hidden = array_diff( $hidden, array('submitdiv', 'linksubmitdiv', 'manage-menu', 'create-menu') ); // postboxes that are always shown
1399
		update_user_option($user->ID, "metaboxhidden_$page", $hidden, true);
1400
	}
1401
1402
	wp_die( 1 );
1403
}
1404
1405
/**
1406
 * Ajax handler for hidden columns.
1407
 *
1408
 * @since 3.1.0
1409
 */
1410
function wp_ajax_hidden_columns() {
1411
	check_ajax_referer( 'screen-options-nonce', 'screenoptionnonce' );
1412
	$page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1413
1414
	if ( $page != sanitize_key( $page ) )
1415
		wp_die( 0 );
1416
1417
	if ( ! $user = wp_get_current_user() )
1418
		wp_die( -1 );
1419
1420
	$hidden = ! empty( $_POST['hidden'] ) ? explode( ',', $_POST['hidden'] ) : array();
1421
	update_user_option( $user->ID, "manage{$page}columnshidden", $hidden, true );
1422
1423
	wp_die( 1 );
1424
}
1425
1426
/**
1427
 * Ajax handler for updating whether to display the welcome panel.
1428
 *
1429
 * @since 3.1.0
1430
 */
1431
function wp_ajax_update_welcome_panel() {
1432
	check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' );
1433
1434
	if ( ! current_user_can( 'edit_theme_options' ) )
1435
		wp_die( -1 );
1436
1437
	update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 );
1438
1439
	wp_die( 1 );
1440
}
1441
1442
/**
1443
 * Ajax handler for retrieving menu meta boxes.
1444
 *
1445
 * @since 3.1.0
1446
 */
1447
function wp_ajax_menu_get_metabox() {
1448
	if ( ! current_user_can( 'edit_theme_options' ) )
1449
		wp_die( -1 );
1450
1451
	require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1452
1453
	if ( isset( $_POST['item-type'] ) && 'post_type' == $_POST['item-type'] ) {
1454
		$type = 'posttype';
1455
		$callback = 'wp_nav_menu_item_post_type_meta_box';
1456
		$items = (array) get_post_types( array( 'show_in_nav_menus' => true ), 'object' );
1457
	} elseif ( isset( $_POST['item-type'] ) && 'taxonomy' == $_POST['item-type'] ) {
1458
		$type = 'taxonomy';
1459
		$callback = 'wp_nav_menu_item_taxonomy_meta_box';
1460
		$items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' );
1461
	}
1462
1463
	if ( ! empty( $_POST['item-object'] ) && isset( $items[$_POST['item-object']] ) ) {
1464
		$menus_meta_box_object = $items[ $_POST['item-object'] ];
1465
1466
		/** This filter is documented in wp-admin/includes/nav-menu.php */
1467
		$item = apply_filters( 'nav_menu_meta_box_object', $menus_meta_box_object );
1468
		ob_start();
1469
		call_user_func_array($callback, array(
0 ignored issues
show
The variable $callback does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1470
			null,
1471
			array(
1472
				'id' => 'add-' . $item->name,
1473
				'title' => $item->labels->name,
1474
				'callback' => $callback,
1475
				'args' => $item,
1476
			)
1477
		));
1478
1479
		$markup = ob_get_clean();
1480
1481
		echo wp_json_encode(array(
1482
			'replace-id' => $type . '-' . $item->name,
0 ignored issues
show
The variable $type does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1483
			'markup' => $markup,
1484
		));
1485
	}
1486
1487
	wp_die();
1488
}
1489
1490
/**
1491
 * Ajax handler for internal linking.
1492
 *
1493
 * @since 3.1.0
1494
 */
1495
function wp_ajax_wp_link_ajax() {
1496
	check_ajax_referer( 'internal-linking', '_ajax_linking_nonce' );
1497
1498
	$args = array();
1499
1500
	if ( isset( $_POST['search'] ) ) {
1501
		$args['s'] = wp_unslash( $_POST['search'] );
1502
	}
1503
1504
	if ( isset( $_POST['term'] ) ) {
1505
		$args['s'] = wp_unslash( $_POST['term'] );
1506
	}
1507
1508
	$args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
1509
1510
	require(ABSPATH . WPINC . '/class-wp-editor.php');
1511
	$results = _WP_Editors::wp_link_query( $args );
1512
1513
	if ( ! isset( $results ) )
1514
		wp_die( 0 );
1515
1516
	echo wp_json_encode( $results );
1517
	echo "\n";
1518
1519
	wp_die();
1520
}
1521
1522
/**
1523
 * Ajax handler for menu locations save.
1524
 *
1525
 * @since 3.1.0
1526
 */
1527
function wp_ajax_menu_locations_save() {
1528
	if ( ! current_user_can( 'edit_theme_options' ) )
1529
		wp_die( -1 );
1530
	check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1531
	if ( ! isset( $_POST['menu-locations'] ) )
1532
		wp_die( 0 );
1533
	set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) );
1534
	wp_die( 1 );
1535
}
1536
1537
/**
1538
 * Ajax handler for saving the meta box order.
1539
 *
1540
 * @since 3.1.0
1541
 */
1542
function wp_ajax_meta_box_order() {
1543
	check_ajax_referer( 'meta-box-order' );
1544
	$order = isset( $_POST['order'] ) ? (array) $_POST['order'] : false;
1545
	$page_columns = isset( $_POST['page_columns'] ) ? $_POST['page_columns'] : 'auto';
1546
1547
	if ( $page_columns != 'auto' )
1548
		$page_columns = (int) $page_columns;
1549
1550
	$page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1551
1552
	if ( $page != sanitize_key( $page ) )
1553
		wp_die( 0 );
1554
1555
	if ( ! $user = wp_get_current_user() )
1556
		wp_die( -1 );
1557
1558
	if ( $order )
1559
		update_user_option($user->ID, "meta-box-order_$page", $order, true);
1560
1561
	if ( $page_columns )
1562
		update_user_option($user->ID, "screen_layout_$page", $page_columns, true);
1563
1564
	wp_die( 1 );
1565
}
1566
1567
/**
1568
 * Ajax handler for menu quick searching.
1569
 *
1570
 * @since 3.1.0
1571
 */
1572
function wp_ajax_menu_quick_search() {
1573
	if ( ! current_user_can( 'edit_theme_options' ) )
1574
		wp_die( -1 );
1575
1576
	require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1577
1578
	_wp_ajax_menu_quick_search( $_POST );
1579
1580
	wp_die();
1581
}
1582
1583
/**
1584
 * Ajax handler to retrieve a permalink.
1585
 *
1586
 * @since 3.1.0
1587
 */
1588
function wp_ajax_get_permalink() {
1589
	check_ajax_referer( 'getpermalink', 'getpermalinknonce' );
1590
	$post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1591
	wp_die( get_preview_post_link( $post_id ) );
1592
}
1593
1594
/**
1595
 * Ajax handler to retrieve a sample permalink.
1596
 *
1597
 * @since 3.1.0
1598
 */
1599
function wp_ajax_sample_permalink() {
1600
	check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' );
1601
	$post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1602
	$title = isset($_POST['new_title'])? $_POST['new_title'] : '';
1603
	$slug = isset($_POST['new_slug'])? $_POST['new_slug'] : null;
1604
	wp_die( get_sample_permalink_html( $post_id, $title, $slug ) );
1605
}
1606
1607
/**
1608
 * Ajax handler for Quick Edit saving a post from a list table.
1609
 *
1610
 * @since 3.1.0
1611
 *
1612
 * @global WP_List_Table $wp_list_table
1613
 */
1614
function wp_ajax_inline_save() {
1615
	global $wp_list_table, $mode;
1616
1617
	check_ajax_referer( 'inlineeditnonce', '_inline_edit' );
1618
1619
	if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) )
1620
		wp_die();
1621
1622 View Code Duplication
	if ( 'page' == $_POST['post_type'] ) {
1623
		if ( ! current_user_can( 'edit_page', $post_ID ) )
0 ignored issues
show
The variable $post_ID does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1624
			wp_die( __( 'Sorry, you are not allowed to edit this page.' ) );
1625
	} else {
1626
		if ( ! current_user_can( 'edit_post', $post_ID ) )
1627
			wp_die( __( 'Sorry, you are not allowed to edit this post.' ) );
1628
	}
1629
1630
	if ( $last = wp_check_post_lock( $post_ID ) ) {
1631
		$last_user = get_userdata( $last );
1632
		$last_user_name = $last_user ? $last_user->display_name : __( 'Someone' );
1633
		printf( $_POST['post_type'] == 'page' ? __( 'Saving is disabled: %s is currently editing this page.' ) : __( 'Saving is disabled: %s is currently editing this post.' ),	esc_html( $last_user_name ) );
1634
		wp_die();
1635
	}
1636
1637
	$data = &$_POST;
1638
1639
	$post = get_post( $post_ID, ARRAY_A );
1640
1641
	// Since it's coming from the database.
1642
	$post = wp_slash($post);
0 ignored issues
show
It seems like $post can also be of type null or object<WP_Post>; however, wp_slash() does only seem to accept string|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...
1643
1644
	$data['content'] = $post['post_content'];
1645
	$data['excerpt'] = $post['post_excerpt'];
1646
1647
	// Rename.
1648
	$data['user_ID'] = get_current_user_id();
1649
1650
	if ( isset($data['post_parent']) )
1651
		$data['parent_id'] = $data['post_parent'];
1652
1653
	// Status.
1654
	if ( isset( $data['keep_private'] ) && 'private' == $data['keep_private'] ) {
1655
		$data['visibility']  = 'private';
1656
		$data['post_status'] = 'private';
1657
	} else {
1658
		$data['post_status'] = $data['_status'];
1659
	}
1660
1661
	if ( empty($data['comment_status']) )
1662
		$data['comment_status'] = 'closed';
1663
	if ( empty($data['ping_status']) )
1664
		$data['ping_status'] = 'closed';
1665
1666
	// Exclude terms from taxonomies that are not supposed to appear in Quick Edit.
1667
	if ( ! empty( $data['tax_input'] ) ) {
1668
		foreach ( $data['tax_input'] as $taxonomy => $terms ) {
1669
			$tax_object = get_taxonomy( $taxonomy );
1670
			/** This filter is documented in wp-admin/includes/class-wp-posts-list-table.php */
1671
			if ( ! apply_filters( 'quick_edit_show_taxonomy', $tax_object->show_in_quick_edit, $taxonomy, $post['post_type'] ) ) {
1672
				unset( $data['tax_input'][ $taxonomy ] );
1673
			}
1674
		}
1675
	}
1676
1677
	// Hack: wp_unique_post_slug() doesn't work for drafts, so we will fake that our post is published.
1678
	if ( ! empty( $data['post_name'] ) && in_array( $post['post_status'], array( 'draft', 'pending' ) ) ) {
1679
		$post['post_status'] = 'publish';
1680
		$data['post_name'] = wp_unique_post_slug( $data['post_name'], $post['ID'], $post['post_status'], $post['post_type'], $post['post_parent'] );
1681
	}
1682
1683
	// Update the post.
1684
	edit_post();
1685
1686
	$wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) );
1687
1688
	$mode = $_POST['post_view'] === 'excerpt' ? 'excerpt' : 'list';
1689
1690
	$level = 0;
1691
	if ( is_post_type_hierarchical( $wp_list_table->screen->post_type ) ) {
1692
		$request_post = array( get_post( $_POST['post_ID'] ) );
1693
		$parent       = $request_post[0]->post_parent;
1694
1695
		while ( $parent > 0 ) {
1696
			$parent_post = get_post( $parent );
1697
			$parent      = $parent_post->post_parent;
1698
			$level++;
1699
		}
1700
	}
1701
1702
	$wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level );
1703
1704
	wp_die();
1705
}
1706
1707
/**
1708
 * Ajax handler for quick edit saving for a term.
1709
 *
1710
 * @since 3.1.0
1711
 *
1712
 * @global WP_List_Table $wp_list_table
1713
 */
1714
function wp_ajax_inline_save_tax() {
1715
	global $wp_list_table;
1716
1717
	check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' );
1718
1719
	$taxonomy = sanitize_key( $_POST['taxonomy'] );
1720
	$tax = get_taxonomy( $taxonomy );
1721
	if ( ! $tax )
1722
		wp_die( 0 );
1723
1724
	if ( ! current_user_can( $tax->cap->edit_terms ) )
1725
		wp_die( -1 );
1726
1727
	$wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => 'edit-' . $taxonomy ) );
1728
1729
	if ( ! isset($_POST['tax_ID']) || ! ( $id = (int) $_POST['tax_ID'] ) )
1730
		wp_die( -1 );
1731
1732
	$tag = get_term( $id, $taxonomy );
0 ignored issues
show
The variable $id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1733
	$_POST['description'] = $tag->description;
1734
1735
	$updated = wp_update_term($id, $taxonomy, $_POST);
1736
	if ( $updated && !is_wp_error($updated) ) {
1737
		$tag = get_term( $updated['term_id'], $taxonomy );
1738
		if ( !$tag || is_wp_error( $tag ) ) {
1739
			if ( is_wp_error($tag) && $tag->get_error_message() )
1740
				wp_die( $tag->get_error_message() );
0 ignored issues
show
The method get_error_message does only exist in WP_Error, but not in WP_Term.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1741
			wp_die( __( 'Item not updated.' ) );
1742
		}
1743
	} else {
1744
		if ( is_wp_error($updated) && $updated->get_error_message() )
1745
			wp_die( $updated->get_error_message() );
1746
		wp_die( __( 'Item not updated.' ) );
1747
	}
1748
	$level = 0;
1749
	$parent = $tag->parent;
1750
	while ( $parent > 0 ) {
1751
		$parent_tag = get_term( $parent, $taxonomy );
1752
		$parent = $parent_tag->parent;
1753
		$level++;
1754
	}
1755
	$wp_list_table->single_row( $tag, $level );
1756
	wp_die();
1757
}
1758
1759
/**
1760
 * Ajax handler for querying posts for the Find Posts modal.
1761
 *
1762
 * @see window.findPosts
1763
 *
1764
 * @since 3.1.0
1765
 */
1766
function wp_ajax_find_posts() {
1767
	check_ajax_referer( 'find-posts' );
1768
1769
	$post_types = get_post_types( array( 'public' => true ), 'objects' );
1770
	unset( $post_types['attachment'] );
1771
1772
	$s = wp_unslash( $_POST['ps'] );
1773
	$args = array(
1774
		'post_type' => array_keys( $post_types ),
1775
		'post_status' => 'any',
1776
		'posts_per_page' => 50,
1777
	);
1778
	if ( '' !== $s )
1779
		$args['s'] = $s;
1780
1781
	$posts = get_posts( $args );
1782
1783
	if ( ! $posts ) {
1784
		wp_send_json_error( __( 'No items found.' ) );
1785
	}
1786
1787
	$html = '<table class="widefat"><thead><tr><th class="found-radio"><br /></th><th>'.__('Title').'</th><th class="no-break">'.__('Type').'</th><th class="no-break">'.__('Date').'</th><th class="no-break">'.__('Status').'</th></tr></thead><tbody>';
1788
	$alt = '';
1789
	foreach ( $posts as $post ) {
1790
		$title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
1791
		$alt = ( 'alternate' == $alt ) ? '' : 'alternate';
1792
1793
		switch ( $post->post_status ) {
1794
			case 'publish' :
1795
			case 'private' :
1796
				$stat = __('Published');
1797
				break;
1798
			case 'future' :
1799
				$stat = __('Scheduled');
1800
				break;
1801
			case 'pending' :
1802
				$stat = __('Pending Review');
1803
				break;
1804
			case 'draft' :
1805
				$stat = __('Draft');
1806
				break;
1807
		}
1808
1809
		if ( '0000-00-00 00:00:00' == $post->post_date ) {
1810
			$time = '';
1811
		} else {
1812
			/* translators: date format in table columns, see https://secure.php.net/date */
1813
			$time = mysql2date(__('Y/m/d'), $post->post_date);
1814
		}
1815
1816
		$html .= '<tr class="' . trim( 'found-posts ' . $alt ) . '"><td class="found-radio"><input type="radio" id="found-'.$post->ID.'" name="found_post_id" value="' . esc_attr($post->ID) . '"></td>';
1817
		$html .= '<td><label for="found-'.$post->ID.'">' . esc_html( $title ) . '</label></td><td class="no-break">' . esc_html( $post_types[$post->post_type]->labels->singular_name ) . '</td><td class="no-break">'.esc_html( $time ) . '</td><td class="no-break">' . esc_html( $stat ). ' </td></tr>' . "\n\n";
0 ignored issues
show
The variable $stat does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1818
	}
1819
1820
	$html .= '</tbody></table>';
1821
1822
	wp_send_json_success( $html );
1823
}
1824
1825
/**
1826
 * Ajax handler for saving the widgets order.
1827
 *
1828
 * @since 3.1.0
1829
 */
1830
function wp_ajax_widgets_order() {
1831
	check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1832
1833
	if ( !current_user_can('edit_theme_options') )
1834
		wp_die( -1 );
1835
1836
	unset( $_POST['savewidgets'], $_POST['action'] );
1837
1838
	// Save widgets order for all sidebars.
1839
	if ( is_array($_POST['sidebars']) ) {
1840
		$sidebars = array();
1841
		foreach ( $_POST['sidebars'] as $key => $val ) {
1842
			$sb = array();
1843
			if ( !empty($val) ) {
1844
				$val = explode(',', $val);
1845
				foreach ( $val as $k => $v ) {
1846
					if ( strpos($v, 'widget-') === false )
1847
						continue;
1848
1849
					$sb[$k] = substr($v, strpos($v, '_') + 1);
1850
				}
1851
			}
1852
			$sidebars[$key] = $sb;
1853
		}
1854
		wp_set_sidebars_widgets($sidebars);
1855
		wp_die( 1 );
1856
	}
1857
1858
	wp_die( -1 );
1859
}
1860
1861
/**
1862
 * Ajax handler for saving a widget.
1863
 *
1864
 * @since 3.1.0
1865
 *
1866
 * @global array $wp_registered_widgets
1867
 * @global array $wp_registered_widget_controls
1868
 * @global array $wp_registered_widget_updates
1869
 */
1870
function wp_ajax_save_widget() {
1871
	global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
1872
1873
	check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1874
1875
	if ( !current_user_can('edit_theme_options') || !isset($_POST['id_base']) )
1876
		wp_die( -1 );
1877
1878
	unset( $_POST['savewidgets'], $_POST['action'] );
1879
1880
	/**
1881
	 * Fires early when editing the widgets displayed in sidebars.
1882
	 *
1883
	 * @since 2.8.0
1884
	 */
1885
	do_action( 'load-widgets.php' );
1886
1887
	/**
1888
	 * Fires early when editing the widgets displayed in sidebars.
1889
	 *
1890
	 * @since 2.8.0
1891
	 */
1892
	do_action( 'widgets.php' );
1893
1894
	/** This action is documented in wp-admin/widgets.php */
1895
	do_action( 'sidebar_admin_setup' );
1896
1897
	$id_base = $_POST['id_base'];
1898
	$widget_id = $_POST['widget-id'];
1899
	$sidebar_id = $_POST['sidebar'];
1900
	$multi_number = !empty($_POST['multi_number']) ? (int) $_POST['multi_number'] : 0;
1901
	$settings = isset($_POST['widget-' . $id_base]) && is_array($_POST['widget-' . $id_base]) ? $_POST['widget-' . $id_base] : false;
1902
	$error = '<p>' . __('An error has occurred. Please reload the page and try again.') . '</p>';
1903
1904
	$sidebars = wp_get_sidebars_widgets();
1905
	$sidebar = isset($sidebars[$sidebar_id]) ? $sidebars[$sidebar_id] : array();
1906
1907
	// Delete.
1908
	if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1909
1910
		if ( !isset($wp_registered_widgets[$widget_id]) )
1911
			wp_die( $error );
1912
1913
		$sidebar = array_diff( $sidebar, array($widget_id) );
1914
		$_POST = array('sidebar' => $sidebar_id, 'widget-' . $id_base => array(), 'the-widget-id' => $widget_id, 'delete_widget' => '1');
1915
1916
		/** This action is documented in wp-admin/widgets.php */
1917
		do_action( 'delete_widget', $widget_id, $sidebar_id, $id_base );
1918
1919
	} elseif ( $settings && preg_match( '/__i__|%i%/', key($settings) ) ) {
1920
		if ( !$multi_number )
1921
			wp_die( $error );
1922
1923
		$_POST[ 'widget-' . $id_base ] = array( $multi_number => reset( $settings ) );
1924
		$widget_id = $id_base . '-' . $multi_number;
1925
		$sidebar[] = $widget_id;
1926
	}
1927
	$_POST['widget-id'] = $sidebar;
1928
1929 View Code Duplication
	foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
1930
1931
		if ( $name == $id_base ) {
1932
			if ( !is_callable( $control['callback'] ) )
1933
				continue;
1934
1935
			ob_start();
1936
				call_user_func_array( $control['callback'], $control['params'] );
1937
			ob_end_clean();
1938
			break;
1939
		}
1940
	}
1941
1942
	if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1943
		$sidebars[$sidebar_id] = $sidebar;
1944
		wp_set_sidebars_widgets($sidebars);
1945
		echo "deleted:$widget_id";
1946
		wp_die();
1947
	}
1948
1949
	if ( !empty($_POST['add_new']) )
1950
		wp_die();
1951
1952
	if ( $form = $wp_registered_widget_controls[$widget_id] )
1953
		call_user_func_array( $form['callback'], $form['params'] );
1954
1955
	wp_die();
1956
}
1957
1958
/**
1959
 * Ajax handler for saving a widget.
1960
 *
1961
 * @since 3.9.0
1962
 *
1963
 * @global WP_Customize_Manager $wp_customize
1964
 */
1965
function wp_ajax_update_widget() {
1966
	global $wp_customize;
1967
	$wp_customize->widgets->wp_ajax_update_widget();
1968
}
1969
1970
/**
1971
 * Ajax handler for removing inactive widgets.
1972
 *
1973
 * @since 4.4.0
1974
 */
1975
function wp_ajax_delete_inactive_widgets() {
1976
	check_ajax_referer( 'remove-inactive-widgets', 'removeinactivewidgets' );
1977
1978
	if ( ! current_user_can( 'edit_theme_options' ) ) {
1979
		wp_die( -1 );
1980
	}
1981
1982
	unset( $_POST['removeinactivewidgets'], $_POST['action'] );
1983
1984
	do_action( 'load-widgets.php' );
1985
	do_action( 'widgets.php' );
1986
	do_action( 'sidebar_admin_setup' );
1987
1988
	$sidebars_widgets = wp_get_sidebars_widgets();
1989
1990 View Code Duplication
	foreach ( $sidebars_widgets['wp_inactive_widgets'] as $key => $widget_id ) {
1991
		$pieces = explode( '-', $widget_id );
1992
		$multi_number = array_pop( $pieces );
1993
		$id_base = implode( '-', $pieces );
1994
		$widget = get_option( 'widget_' . $id_base );
1995
		unset( $widget[$multi_number] );
1996
		update_option( 'widget_' . $id_base, $widget );
1997
		unset( $sidebars_widgets['wp_inactive_widgets'][$key] );
1998
	}
1999
2000
	wp_set_sidebars_widgets( $sidebars_widgets );
2001
2002
	wp_die();
2003
}
2004
2005
/**
2006
 * Ajax handler for uploading attachments
2007
 *
2008
 * @since 3.3.0
2009
 */
2010
function wp_ajax_upload_attachment() {
2011
	check_ajax_referer( 'media-form' );
2012
	/*
2013
	 * This function does not use wp_send_json_success() / wp_send_json_error()
2014
	 * as the html4 Plupload handler requires a text/html content-type for older IE.
2015
	 * See https://core.trac.wordpress.org/ticket/31037
2016
	 */
2017
2018
	if ( ! current_user_can( 'upload_files' ) ) {
2019
		echo wp_json_encode( array(
2020
			'success' => false,
2021
			'data'    => array(
2022
				'message'  => __( 'Sorry, you are not allowed to upload files.' ),
2023
				'filename' => $_FILES['async-upload']['name'],
2024
			)
2025
		) );
2026
2027
		wp_die();
2028
	}
2029
2030
	if ( isset( $_REQUEST['post_id'] ) ) {
2031
		$post_id = $_REQUEST['post_id'];
2032 View Code Duplication
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
2033
			echo wp_json_encode( array(
2034
				'success' => false,
2035
				'data'    => array(
2036
					'message'  => __( "You don't have permission to attach files to this post." ),
2037
					'filename' => $_FILES['async-upload']['name'],
2038
				)
2039
			) );
2040
2041
			wp_die();
2042
		}
2043
	} else {
2044
		$post_id = null;
2045
	}
2046
2047
	$post_data = isset( $_REQUEST['post_data'] ) ? $_REQUEST['post_data'] : array();
2048
2049
	// If the context is custom header or background, make sure the uploaded file is an image.
2050
	if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ) ) ) {
2051
		$wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'] );
2052 View Code Duplication
		if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) {
2053
			echo wp_json_encode( array(
2054
				'success' => false,
2055
				'data'    => array(
2056
					'message'  => __( 'The uploaded file is not a valid image. Please try again.' ),
2057
					'filename' => $_FILES['async-upload']['name'],
2058
				)
2059
			) );
2060
2061
			wp_die();
2062
		}
2063
	}
2064
2065
	$attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data );
2066
2067
	if ( is_wp_error( $attachment_id ) ) {
2068
		echo wp_json_encode( array(
2069
			'success' => false,
2070
			'data'    => array(
2071
				'message'  => $attachment_id->get_error_message(),
2072
				'filename' => $_FILES['async-upload']['name'],
2073
			)
2074
		) );
2075
2076
		wp_die();
2077
	}
2078
2079
	if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) {
2080
		if ( 'custom-background' === $post_data['context'] )
2081
			update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] );
0 ignored issues
show
It seems like $attachment_id defined by media_handle_upload('asy..., $post_id, $post_data) on line 2065 can also be of type object<WP_Error>; however, update_post_meta() does only seem to accept integer, 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...
2082
2083
		if ( 'custom-header' === $post_data['context'] )
2084
			update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] );
0 ignored issues
show
It seems like $attachment_id defined by media_handle_upload('asy..., $post_id, $post_data) on line 2065 can also be of type object<WP_Error>; however, update_post_meta() does only seem to accept integer, 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...
2085
	}
2086
2087
	if ( ! $attachment = wp_prepare_attachment_for_js( $attachment_id ) )
2088
		wp_die();
2089
2090
	echo wp_json_encode( array(
2091
		'success' => true,
2092
		'data'    => $attachment,
2093
	) );
2094
2095
	wp_die();
2096
}
2097
2098
/**
2099
 * Ajax handler for image editing.
2100
 *
2101
 * @since 3.1.0
2102
 */
2103
function wp_ajax_image_editor() {
2104
	$attachment_id = intval($_POST['postid']);
2105
	if ( empty($attachment_id) || !current_user_can('edit_post', $attachment_id) )
2106
		wp_die( -1 );
2107
2108
	check_ajax_referer( "image_editor-$attachment_id" );
2109
	include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
2110
2111
	$msg = false;
2112
	switch ( $_POST['do'] ) {
2113
		case 'save' :
2114
			$msg = wp_save_image($attachment_id);
2115
			$msg = wp_json_encode($msg);
2116
			wp_die( $msg );
0 ignored issues
show
It seems like $msg defined by wp_json_encode($msg) on line 2115 can also be of type false; however, wp_die() does only seem to accept string|object<WP_Error>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
2117
			break;
2118
		case 'scale' :
2119
			$msg = wp_save_image($attachment_id);
2120
			break;
2121
		case 'restore' :
2122
			$msg = wp_restore_image($attachment_id);
2123
			break;
2124
	}
2125
2126
	wp_image_editor($attachment_id, $msg);
2127
	wp_die();
2128
}
2129
2130
/**
2131
 * Ajax handler for setting the featured image.
2132
 *
2133
 * @since 3.1.0
2134
 */
2135
function wp_ajax_set_post_thumbnail() {
2136
	$json = ! empty( $_REQUEST['json'] ); // New-style request
2137
2138
	$post_ID = intval( $_POST['post_id'] );
2139
	if ( ! current_user_can( 'edit_post', $post_ID ) )
2140
		wp_die( -1 );
2141
2142
	$thumbnail_id = intval( $_POST['thumbnail_id'] );
2143
2144
	if ( $json )
2145
		check_ajax_referer( "update-post_$post_ID" );
2146
	else
2147
		check_ajax_referer( "set_post_thumbnail-$post_ID" );
2148
2149
	if ( $thumbnail_id == '-1' ) {
2150
		if ( delete_post_thumbnail( $post_ID ) ) {
2151
			$return = _wp_post_thumbnail_html( null, $post_ID );
2152
			$json ? wp_send_json_success( $return ) : wp_die( $return );
2153
		} else {
2154
			wp_die( 0 );
2155
		}
2156
	}
2157
2158
	if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) {
2159
		$return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
2160
		$json ? wp_send_json_success( $return ) : wp_die( $return );
2161
	}
2162
2163
	wp_die( 0 );
2164
}
2165
2166
/**
2167
 * Ajax handler for retrieving HTML for the featured image.
2168
 *
2169
 * @since 4.6.0
2170
 */
2171
function wp_ajax_get_post_thumbnail_html() {
2172
	$post_ID = intval( $_POST['post_id'] );
2173
2174
	check_ajax_referer( "update-post_$post_ID" );
2175
2176
	if ( ! current_user_can( 'edit_post', $post_ID ) ) {
2177
		wp_die( -1 );
2178
	}
2179
2180
	$thumbnail_id = intval( $_POST['thumbnail_id'] );
2181
2182
	// For backward compatibility, -1 refers to no featured image.
2183
	if ( -1 === $thumbnail_id ) {
2184
		$thumbnail_id = null;
2185
	}
2186
2187
	$return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
2188
	wp_send_json_success( $return );
2189
}
2190
2191
/**
2192
 * Ajax handler for setting the featured image for an attachment.
2193
 *
2194
 * @since 4.0.0
2195
 *
2196
 * @see set_post_thumbnail()
2197
 */
2198
function wp_ajax_set_attachment_thumbnail() {
2199
	if ( empty( $_POST['urls'] ) || ! is_array( $_POST['urls'] ) ) {
2200
		wp_send_json_error();
2201
	}
2202
2203
	$thumbnail_id = (int) $_POST['thumbnail_id'];
2204
	if ( empty( $thumbnail_id ) ) {
2205
		wp_send_json_error();
2206
	}
2207
2208
	$post_ids = array();
2209
	// For each URL, try to find its corresponding post ID.
2210
	foreach ( $_POST['urls'] as $url ) {
2211
		$post_id = attachment_url_to_postid( $url );
2212
		if ( ! empty( $post_id ) ) {
2213
			$post_ids[] = $post_id;
2214
		}
2215
	}
2216
2217
	if ( empty( $post_ids ) ) {
2218
		wp_send_json_error();
2219
	}
2220
2221
	$success = 0;
2222
	// For each found attachment, set its thumbnail.
2223
	foreach ( $post_ids as $post_id ) {
2224
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
2225
			continue;
2226
		}
2227
2228
		if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) {
2229
			$success++;
2230
		}
2231
	}
2232
2233
	if ( 0 === $success ) {
2234
		wp_send_json_error();
2235
	} else {
2236
		wp_send_json_success();
2237
	}
2238
2239
	wp_send_json_error();
2240
}
2241
2242
/**
2243
 * Ajax handler for date formatting.
2244
 *
2245
 * @since 3.1.0
2246
 */
2247
function wp_ajax_date_format() {
2248
	wp_die( date_i18n( sanitize_option( 'date_format', wp_unslash( $_POST['date'] ) ) ) );
0 ignored issues
show
It seems like wp_unslash($_POST['date']) targeting wp_unslash() can also be of type array; however, sanitize_option() 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...
2249
}
2250
2251
/**
2252
 * Ajax handler for time formatting.
2253
 *
2254
 * @since 3.1.0
2255
 */
2256
function wp_ajax_time_format() {
2257
	wp_die( date_i18n( sanitize_option( 'time_format', wp_unslash( $_POST['date'] ) ) ) );
0 ignored issues
show
It seems like wp_unslash($_POST['date']) targeting wp_unslash() can also be of type array; however, sanitize_option() 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...
2258
}
2259
2260
/**
2261
 * Ajax handler for saving posts from the fullscreen editor.
2262
 *
2263
 * @since 3.1.0
2264
 * @deprecated 4.3.0
2265
 */
2266
function wp_ajax_wp_fullscreen_save_post() {
2267
	$post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0;
2268
2269
	$post = null;
2270
2271
	if ( $post_id )
2272
		$post = get_post( $post_id );
2273
2274
	check_ajax_referer('update-post_' . $post_id, '_wpnonce');
2275
2276
	$post_id = edit_post();
2277
2278
	if ( is_wp_error( $post_id ) ) {
2279
		wp_send_json_error();
2280
	}
2281
2282
	if ( $post ) {
2283
		$last_date = mysql2date( __( 'F j, Y' ), $post->post_modified );
2284
		$last_time = mysql2date( __( 'g:i a' ), $post->post_modified );
2285
	} else {
2286
		$last_date = date_i18n( __( 'F j, Y' ) );
2287
		$last_time = date_i18n( __( 'g:i a' ) );
2288
	}
2289
2290
	if ( $last_id = get_post_meta( $post_id, '_edit_last', true ) ) {
2291
		$last_user = get_userdata( $last_id );
2292
		$last_edited = sprintf( __('Last edited by %1$s on %2$s at %3$s'), esc_html( $last_user->display_name ), $last_date, $last_time );
2293
	} else {
2294
		$last_edited = sprintf( __('Last edited on %1$s at %2$s'), $last_date, $last_time );
2295
	}
2296
2297
	wp_send_json_success( array( 'last_edited' => $last_edited ) );
2298
}
2299
2300
/**
2301
 * Ajax handler for removing a post lock.
2302
 *
2303
 * @since 3.1.0
2304
 */
2305
function wp_ajax_wp_remove_post_lock() {
2306
	if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) )
2307
		wp_die( 0 );
2308
	$post_id = (int) $_POST['post_ID'];
2309
	if ( ! $post = get_post( $post_id ) )
2310
		wp_die( 0 );
2311
2312
	check_ajax_referer( 'update-post_' . $post_id );
2313
2314
	if ( ! current_user_can( 'edit_post', $post_id ) )
2315
		wp_die( -1 );
2316
2317
	$active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) );
2318
	if ( $active_lock[1] != get_current_user_id() )
2319
		wp_die( 0 );
2320
2321
	/**
2322
	 * Filters the post lock window duration.
2323
	 *
2324
	 * @since 3.3.0
2325
	 *
2326
	 * @param int $interval The interval in seconds the post lock duration
2327
	 *                      should last, plus 5 seconds. Default 150.
2328
	 */
2329
	$new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1];
2330
	update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) );
2331
	wp_die( 1 );
2332
}
2333
2334
/**
2335
 * Ajax handler for dismissing a WordPress pointer.
2336
 *
2337
 * @since 3.1.0
2338
 */
2339
function wp_ajax_dismiss_wp_pointer() {
2340
	$pointer = $_POST['pointer'];
2341
	if ( $pointer != sanitize_key( $pointer ) )
2342
		wp_die( 0 );
2343
2344
//	check_ajax_referer( 'dismiss-pointer_' . $pointer );
2345
2346
	$dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );
2347
2348
	if ( in_array( $pointer, $dismissed ) )
2349
		wp_die( 0 );
2350
2351
	$dismissed[] = $pointer;
2352
	$dismissed = implode( ',', $dismissed );
2353
2354
	update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed );
2355
	wp_die( 1 );
2356
}
2357
2358
/**
2359
 * Ajax handler for getting an attachment.
2360
 *
2361
 * @since 3.5.0
2362
 */
2363
function wp_ajax_get_attachment() {
2364
	if ( ! isset( $_REQUEST['id'] ) )
2365
		wp_send_json_error();
2366
2367
	if ( ! $id = absint( $_REQUEST['id'] ) )
2368
		wp_send_json_error();
2369
2370
	if ( ! $post = get_post( $id ) )
2371
		wp_send_json_error();
2372
2373
	if ( 'attachment' != $post->post_type )
2374
		wp_send_json_error();
2375
2376
	if ( ! current_user_can( 'upload_files' ) )
2377
		wp_send_json_error();
2378
2379
	if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2380
		wp_send_json_error();
2381
2382
	wp_send_json_success( $attachment );
2383
}
2384
2385
/**
2386
 * Ajax handler for querying attachments.
2387
 *
2388
 * @since 3.5.0
2389
 */
2390
function wp_ajax_query_attachments() {
2391
	if ( ! current_user_can( 'upload_files' ) )
2392
		wp_send_json_error();
2393
2394
	$query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array();
2395
	$keys = array(
2396
		's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type',
2397
		'post_parent', 'post__in', 'post__not_in', 'year', 'monthnum'
2398
	);
2399
	foreach ( get_taxonomies_for_attachments( 'objects' ) as $t ) {
2400
		if ( $t->query_var && isset( $query[ $t->query_var ] ) ) {
2401
			$keys[] = $t->query_var;
2402
		}
2403
	}
2404
2405
	$query = array_intersect_key( $query, array_flip( $keys ) );
2406
	$query['post_type'] = 'attachment';
2407
	if ( MEDIA_TRASH
2408
		&& ! empty( $_REQUEST['query']['post_status'] )
2409
		&& 'trash' === $_REQUEST['query']['post_status'] ) {
2410
		$query['post_status'] = 'trash';
2411
	} else {
2412
		$query['post_status'] = 'inherit';
2413
	}
2414
2415
	if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
2416
		$query['post_status'] .= ',private';
2417
2418
	/**
2419
	 * Filters the arguments passed to WP_Query during an Ajax
2420
	 * call for querying attachments.
2421
	 *
2422
	 * @since 3.7.0
2423
	 *
2424
	 * @see WP_Query::parse_query()
2425
	 *
2426
	 * @param array $query An array of query variables.
2427
	 */
2428
	$query = apply_filters( 'ajax_query_attachments_args', $query );
2429
	$query = new WP_Query( $query );
2430
2431
	$posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
2432
	$posts = array_filter( $posts );
2433
2434
	wp_send_json_success( $posts );
2435
}
2436
2437
/**
2438
 * Ajax handler for updating attachment attributes.
2439
 *
2440
 * @since 3.5.0
2441
 */
2442
function wp_ajax_save_attachment() {
2443
	if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) )
2444
		wp_send_json_error();
2445
2446
	if ( ! $id = absint( $_REQUEST['id'] ) )
2447
		wp_send_json_error();
2448
2449
	check_ajax_referer( 'update-post_' . $id, 'nonce' );
2450
2451
	if ( ! current_user_can( 'edit_post', $id ) )
2452
		wp_send_json_error();
2453
2454
	$changes = $_REQUEST['changes'];
2455
	$post    = get_post( $id, ARRAY_A );
2456
2457
	if ( 'attachment' != $post['post_type'] )
2458
		wp_send_json_error();
2459
2460
	if ( isset( $changes['parent'] ) )
2461
		$post['post_parent'] = $changes['parent'];
2462
2463
	if ( isset( $changes['title'] ) )
2464
		$post['post_title'] = $changes['title'];
2465
2466
	if ( isset( $changes['caption'] ) )
2467
		$post['post_excerpt'] = $changes['caption'];
2468
2469
	if ( isset( $changes['description'] ) )
2470
		$post['post_content'] = $changes['description'];
2471
2472
	if ( MEDIA_TRASH && isset( $changes['status'] ) )
2473
		$post['post_status'] = $changes['status'];
2474
2475 View Code Duplication
	if ( isset( $changes['alt'] ) ) {
2476
		$alt = wp_unslash( $changes['alt'] );
2477
		if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) {
2478
			$alt = wp_strip_all_tags( $alt, true );
0 ignored issues
show
It seems like $alt can also be of type array; however, wp_strip_all_tags() 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...
2479
			update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) );
2480
		}
2481
	}
2482
2483
	if ( wp_attachment_is( 'audio', $post['ID'] ) ) {
2484
		$changed = false;
2485
		$id3data = wp_get_attachment_metadata( $post['ID'] );
2486
		if ( ! is_array( $id3data ) ) {
2487
			$changed = true;
2488
			$id3data = array();
2489
		}
2490
		foreach ( wp_get_attachment_id3_keys( (object) $post, 'edit' ) as $key => $label ) {
2491
			if ( isset( $changes[ $key ] ) ) {
2492
				$changed = true;
2493
				$id3data[ $key ] = sanitize_text_field( wp_unslash( $changes[ $key ] ) );
0 ignored issues
show
It seems like wp_unslash($changes[$key]) targeting wp_unslash() can also be of type array; however, sanitize_text_field() 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...
2494
			}
2495
		}
2496
2497
		if ( $changed ) {
2498
			wp_update_attachment_metadata( $id, $id3data );
2499
		}
2500
	}
2501
2502
	if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) {
2503
		wp_delete_post( $id );
2504
	} else {
2505
		wp_update_post( $post );
2506
	}
2507
2508
	wp_send_json_success();
2509
}
2510
2511
/**
2512
 * Ajax handler for saving backward compatible attachment attributes.
2513
 *
2514
 * @since 3.5.0
2515
 */
2516
function wp_ajax_save_attachment_compat() {
2517
	if ( ! isset( $_REQUEST['id'] ) )
2518
		wp_send_json_error();
2519
2520
	if ( ! $id = absint( $_REQUEST['id'] ) )
2521
		wp_send_json_error();
2522
2523
	if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) )
2524
		wp_send_json_error();
2525
	$attachment_data = $_REQUEST['attachments'][ $id ];
2526
2527
	check_ajax_referer( 'update-post_' . $id, 'nonce' );
2528
2529
	if ( ! current_user_can( 'edit_post', $id ) )
2530
		wp_send_json_error();
2531
2532
	$post = get_post( $id, ARRAY_A );
2533
2534
	if ( 'attachment' != $post['post_type'] )
2535
		wp_send_json_error();
2536
2537
	/** This filter is documented in wp-admin/includes/media.php */
2538
	$post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data );
2539
2540
	if ( isset( $post['errors'] ) ) {
2541
		$errors = $post['errors']; // @todo return me and display me!
0 ignored issues
show
$errors 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...
2542
		unset( $post['errors'] );
2543
	}
2544
2545
	wp_update_post( $post );
2546
2547 View Code Duplication
	foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) {
2548
		if ( isset( $attachment_data[ $taxonomy ] ) )
2549
			wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false );
2550
	}
2551
2552
	if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2553
		wp_send_json_error();
2554
2555
	wp_send_json_success( $attachment );
2556
}
2557
2558
/**
2559
 * Ajax handler for saving the attachment order.
2560
 *
2561
 * @since 3.5.0
2562
 */
2563
function wp_ajax_save_attachment_order() {
2564
	if ( ! isset( $_REQUEST['post_id'] ) )
2565
		wp_send_json_error();
2566
2567
	if ( ! $post_id = absint( $_REQUEST['post_id'] ) )
2568
		wp_send_json_error();
2569
2570
	if ( empty( $_REQUEST['attachments'] ) )
2571
		wp_send_json_error();
2572
2573
	check_ajax_referer( 'update-post_' . $post_id, 'nonce' );
2574
2575
	$attachments = $_REQUEST['attachments'];
2576
2577
	if ( ! current_user_can( 'edit_post', $post_id ) )
2578
		wp_send_json_error();
2579
2580
	foreach ( $attachments as $attachment_id => $menu_order ) {
2581
		if ( ! current_user_can( 'edit_post', $attachment_id ) )
2582
			continue;
2583
		if ( ! $attachment = get_post( $attachment_id ) )
2584
			continue;
2585
		if ( 'attachment' != $attachment->post_type )
2586
			continue;
2587
2588
		wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) );
2589
	}
2590
2591
	wp_send_json_success();
2592
}
2593
2594
/**
2595
 * Ajax handler for sending an attachment to the editor.
2596
 *
2597
 * Generates the HTML to send an attachment to the editor.
2598
 * Backward compatible with the {@see 'media_send_to_editor'} filter
2599
 * and the chain of filters that follow.
2600
 *
2601
 * @since 3.5.0
2602
 */
2603
function wp_ajax_send_attachment_to_editor() {
2604
	check_ajax_referer( 'media-send-to-editor', 'nonce' );
2605
2606
	$attachment = wp_unslash( $_POST['attachment'] );
2607
2608
	$id = intval( $attachment['id'] );
2609
2610
	if ( ! $post = get_post( $id ) )
2611
		wp_send_json_error();
2612
2613
	if ( 'attachment' != $post->post_type )
2614
		wp_send_json_error();
2615
2616
	if ( current_user_can( 'edit_post', $id ) ) {
2617
		// If this attachment is unattached, attach it. Primarily a back compat thing.
2618
		if ( 0 == $post->post_parent && $insert_into_post_id = intval( $_POST['post_id'] ) ) {
2619
			wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id ) );
2620
		}
2621
	}
2622
2623
	$url = empty( $attachment['url'] ) ? '' : $attachment['url'];
2624
	$rel = ( strpos( $url, 'attachment_id') || get_attachment_link( $id ) == $url );
2625
2626
	remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' );
2627
2628
	if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) {
2629
		$align = isset( $attachment['align'] ) ? $attachment['align'] : 'none';
2630
		$size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium';
2631
		$alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : '';
2632
2633
		// No whitespace-only captions.
2634
		$caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
2635
		if ( '' === trim( $caption ) ) {
2636
			$caption = '';
2637
		}
2638
2639
		$title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
2640
		$html = get_image_send_to_editor( $id, $caption, $title, $align, $url, $rel, $size, $alt );
2641
	} elseif ( wp_attachment_is( 'video', $post ) || wp_attachment_is( 'audio', $post )  ) {
0 ignored issues
show
It seems like $post defined by get_post($id) on line 2610 can also be of type array; however, wp_attachment_is() does only seem to accept integer|object<WP_Post>|null, 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...
2642
		$html = stripslashes_deep( $_POST['html'] );
2643
	} else {
2644
		$html = isset( $attachment['post_title'] ) ? $attachment['post_title'] : '';
2645
		$rel = $rel ? ' rel="attachment wp-att-' . $id . '"' : ''; // Hard-coded string, $id is already sanitized
2646
2647
		if ( ! empty( $url ) ) {
2648
			$html = '<a href="' . esc_url( $url ) . '"' . $rel . '>' . $html . '</a>';
2649
		}
2650
	}
2651
2652
	/** This filter is documented in wp-admin/includes/media.php */
2653
	$html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
2654
2655
	wp_send_json_success( $html );
2656
}
2657
2658
/**
2659
 * Ajax handler for sending a link to the editor.
2660
 *
2661
 * Generates the HTML to send a non-image embed link to the editor.
2662
 *
2663
 * Backward compatible with the following filters:
2664
 * - file_send_to_editor_url
2665
 * - audio_send_to_editor_url
2666
 * - video_send_to_editor_url
2667
 *
2668
 * @since 3.5.0
2669
 *
2670
 * @global WP_Post  $post
2671
 * @global WP_Embed $wp_embed
2672
 */
2673
function wp_ajax_send_link_to_editor() {
2674
	global $post, $wp_embed;
2675
2676
	check_ajax_referer( 'media-send-to-editor', 'nonce' );
2677
2678
	if ( ! $src = wp_unslash( $_POST['src'] ) )
2679
		wp_send_json_error();
2680
2681
	if ( ! strpos( $src, '://' ) )
2682
		$src = 'http://' . $src;
2683
2684
	if ( ! $src = esc_url_raw( $src ) )
0 ignored issues
show
It seems like $src can also be of type array; however, esc_url_raw() 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...
2685
		wp_send_json_error();
2686
2687
	if ( ! $link_text = trim( wp_unslash( $_POST['link_text'] ) ) )
2688
		$link_text = wp_basename( $src );
2689
2690
	$post = get_post( isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0 );
2691
2692
	// Ping WordPress for an embed.
2693
	$check_embed = $wp_embed->run_shortcode( '[embed]'. $src .'[/embed]' );
2694
2695
	// Fallback that WordPress creates when no oEmbed was found.
2696
	$fallback = $wp_embed->maybe_make_link( $src );
2697
2698
	if ( $check_embed !== $fallback ) {
2699
		// TinyMCE view for [embed] will parse this
2700
		$html = '[embed]' . $src . '[/embed]';
2701
	} elseif ( $link_text ) {
2702
		$html = '<a href="' . esc_url( $src ) . '">' . $link_text . '</a>';
2703
	} else {
2704
		$html = '';
2705
	}
2706
2707
	// Figure out what filter to run:
2708
	$type = 'file';
2709 View Code Duplication
	if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) )
2710
		&& ( 'audio' == $ext_type || 'video' == $ext_type ) )
2711
			$type = $ext_type;
2712
2713
	/** This filter is documented in wp-admin/includes/media.php */
2714
	$html = apply_filters( $type . '_send_to_editor_url', $html, $src, $link_text );
2715
2716
	wp_send_json_success( $html );
2717
}
2718
2719
/**
2720
 * Ajax handler for the Heartbeat API.
2721
 *
2722
 * Runs when the user is logged in.
2723
 *
2724
 * @since 3.6.0
2725
 */
2726
function wp_ajax_heartbeat() {
2727
	if ( empty( $_POST['_nonce'] ) ) {
2728
		wp_send_json_error();
2729
	}
2730
2731
	$response = $data = array();
2732
	$nonce_state = wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' );
2733
2734
	// screen_id is the same as $current_screen->id and the JS global 'pagenow'.
2735 View Code Duplication
	if ( ! empty( $_POST['screen_id'] ) ) {
2736
		$screen_id = sanitize_key($_POST['screen_id']);
2737
	} else {
2738
		$screen_id = 'front';
2739
	}
2740
2741
	if ( ! empty( $_POST['data'] ) ) {
2742
		$data = wp_unslash( (array) $_POST['data'] );
2743
	}
2744
2745
	if ( 1 !== $nonce_state ) {
2746
		$response = apply_filters( 'wp_refresh_nonces', $response, $data, $screen_id );
2747
2748
		if ( false === $nonce_state ) {
2749
			// User is logged in but nonces have expired.
2750
			$response['nonces_expired'] = true;
2751
			wp_send_json( $response );
2752
		}
2753
	}
2754
2755
	if ( ! empty( $data ) ) {
2756
		/**
2757
		 * Filters the Heartbeat response received.
2758
		 *
2759
		 * @since 3.6.0
2760
		 *
2761
		 * @param array  $response  The Heartbeat response.
2762
		 * @param array  $data      The $_POST data sent.
2763
		 * @param string $screen_id The screen id.
2764
		 */
2765
		$response = apply_filters( 'heartbeat_received', $response, $data, $screen_id );
2766
	}
2767
2768
	/**
2769
	 * Filters the Heartbeat response sent.
2770
	 *
2771
	 * @since 3.6.0
2772
	 *
2773
	 * @param array  $response  The Heartbeat response.
2774
	 * @param string $screen_id The screen id.
2775
	 */
2776
	$response = apply_filters( 'heartbeat_send', $response, $screen_id );
2777
2778
	/**
2779
	 * Fires when Heartbeat ticks in logged-in environments.
2780
	 *
2781
	 * Allows the transport to be easily replaced with long-polling.
2782
	 *
2783
	 * @since 3.6.0
2784
	 *
2785
	 * @param array  $response  The Heartbeat response.
2786
	 * @param string $screen_id The screen id.
2787
	 */
2788
	do_action( 'heartbeat_tick', $response, $screen_id );
2789
2790
	// Send the current time according to the server
2791
	$response['server_time'] = time();
2792
2793
	wp_send_json( $response );
2794
}
2795
2796
/**
2797
 * Ajax handler for getting revision diffs.
2798
 *
2799
 * @since 3.6.0
2800
 */
2801
function wp_ajax_get_revision_diffs() {
2802
	require ABSPATH . 'wp-admin/includes/revision.php';
2803
2804
	if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
2805
		wp_send_json_error();
2806
2807
	if ( ! current_user_can( 'edit_post', $post->ID ) )
2808
		wp_send_json_error();
2809
2810
	// Really just pre-loading the cache here.
2811
	if ( ! $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ) )
2812
		wp_send_json_error();
2813
2814
	$return = array();
2815
	@set_time_limit( 0 );
2816
2817
	foreach ( $_REQUEST['compare'] as $compare_key ) {
2818
		list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
2819
2820
		$return[] = array(
2821
			'id' => $compare_key,
2822
			'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
0 ignored issues
show
It seems like $post defined by get_post((int) $_REQUEST['post_id']) on line 2804 can also be of type array; however, wp_get_revision_ui_diff() does only seem to accept object|integer, 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...
2823
		);
2824
	}
2825
	wp_send_json_success( $return );
2826
}
2827
2828
/**
2829
 * Ajax handler for auto-saving the selected color scheme for
2830
 * a user's own profile.
2831
 *
2832
 * @since 3.8.0
2833
 *
2834
 * @global array $_wp_admin_css_colors
2835
 */
2836
function wp_ajax_save_user_color_scheme() {
2837
	global $_wp_admin_css_colors;
2838
2839
	check_ajax_referer( 'save-color-scheme', 'nonce' );
2840
2841
	$color_scheme = sanitize_key( $_POST['color_scheme'] );
2842
2843
	if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) {
2844
		wp_send_json_error();
2845
	}
2846
2847
	$previous_color_scheme = get_user_meta( get_current_user_id(), 'admin_color', true );
2848
	update_user_meta( get_current_user_id(), 'admin_color', $color_scheme );
2849
2850
	wp_send_json_success( array(
2851
		'previousScheme' => 'admin-color-' . $previous_color_scheme,
2852
		'currentScheme'  => 'admin-color-' . $color_scheme
2853
	) );
2854
}
2855
2856
/**
2857
 * Ajax handler for getting themes from themes_api().
2858
 *
2859
 * @since 3.9.0
2860
 *
2861
 * @global array $themes_allowedtags
2862
 * @global array $theme_field_defaults
2863
 */
2864
function wp_ajax_query_themes() {
2865
	global $themes_allowedtags, $theme_field_defaults;
2866
2867
	if ( ! current_user_can( 'install_themes' ) ) {
2868
		wp_send_json_error();
2869
	}
2870
2871
	$args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array(
2872
		'per_page' => 20,
2873
		'fields'   => $theme_field_defaults
2874
	) );
2875
2876
	if ( isset( $args['browse'] ) && 'favorites' === $args['browse'] && ! isset( $args['user'] ) ) {
2877
		$user = get_user_option( 'wporg_favorites' );
2878
		if ( $user ) {
2879
			$args['user'] = $user;
2880
		}
2881
	}
2882
2883
	$old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
2884
2885
	/** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
2886
	$args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
2887
2888
	$api = themes_api( 'query_themes', $args );
2889
2890
	if ( is_wp_error( $api ) ) {
2891
		wp_send_json_error();
2892
	}
2893
2894
	$update_php = network_admin_url( 'update.php?action=install-theme' );
2895
	foreach ( $api->themes as &$theme ) {
2896
		$theme->install_url = add_query_arg( array(
2897
			'theme'    => $theme->slug,
2898
			'_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug )
2899
		), $update_php );
2900
2901
		if ( current_user_can( 'switch_themes' ) ) {
2902
			if ( is_multisite() ) {
2903
				$theme->activate_url = add_query_arg( array(
2904
					'action'   => 'enable',
2905
					'_wpnonce' => wp_create_nonce( 'enable-theme_' . $theme->slug ),
2906
					'theme'    => $theme->slug,
2907
				), network_admin_url( 'themes.php' ) );
2908
			} else {
2909
				$theme->activate_url = add_query_arg( array(
2910
					'action'     => 'activate',
2911
					'_wpnonce'   => wp_create_nonce( 'switch-theme_' . $theme->slug ),
2912
					'stylesheet' => $theme->slug,
2913
				), admin_url( 'themes.php' ) );
2914
			}
2915
		}
2916
2917
		if ( ! is_multisite() && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
2918
			$theme->customize_url = add_query_arg( array(
2919
				'return' => urlencode( network_admin_url( 'theme-install.php', 'relative' ) ),
2920
			), wp_customize_url( $theme->slug ) );
2921
		}
2922
2923
		$theme->name        = wp_kses( $theme->name, $themes_allowedtags );
2924
		$theme->author      = wp_kses( $theme->author, $themes_allowedtags );
2925
		$theme->version     = wp_kses( $theme->version, $themes_allowedtags );
2926
		$theme->description = wp_kses( $theme->description, $themes_allowedtags );
2927
		$theme->stars       = wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings, 'echo' => false ) );
2928
		$theme->num_ratings = number_format_i18n( $theme->num_ratings );
2929
		$theme->preview_url = set_url_scheme( $theme->preview_url );
2930
	}
2931
2932
	wp_send_json_success( $api );
2933
}
2934
2935
/**
2936
 * Apply [embed] Ajax handlers to a string.
2937
 *
2938
 * @since 4.0.0
2939
 *
2940
 * @global WP_Post    $post       Global $post.
2941
 * @global WP_Embed   $wp_embed   Embed API instance.
2942
 * @global WP_Scripts $wp_scripts
2943
 */
2944
function wp_ajax_parse_embed() {
2945
	global $post, $wp_embed;
2946
2947
	if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
2948
		wp_send_json_error();
2949
	}
2950
2951
	if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
2952
		wp_send_json_error();
2953
	}
2954
2955
	$shortcode = wp_unslash( $_POST['shortcode'] );
2956
2957
	preg_match( '/' . get_shortcode_regex() . '/s', $shortcode, $matches );
2958
	$atts = shortcode_parse_atts( $matches[3] );
2959
	if ( ! empty( $matches[5] ) ) {
2960
		$url = $matches[5];
2961
	} elseif ( ! empty( $atts['src'] ) ) {
2962
		$url = $atts['src'];
2963
	} else {
2964
		$url = '';
2965
	}
2966
2967
	$parsed = false;
2968
	setup_postdata( $post );
0 ignored issues
show
It seems like $post defined by get_post((int) $_POST['post_ID']) on line 2947 can also be of type array or null; however, setup_postdata() does only seem to accept object|integer, 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...
2969
2970
	$wp_embed->return_false_on_fail = true;
2971
2972
	if ( is_ssl() && 0 === strpos( $url, 'http://' ) ) {
2973
		// Admin is ssl and the user pasted non-ssl URL.
2974
		// Check if the provider supports ssl embeds and use that for the preview.
2975
		$ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode );
2976
		$parsed = $wp_embed->run_shortcode( $ssl_shortcode );
2977
2978
		if ( ! $parsed ) {
2979
			$no_ssl_support = true;
2980
		}
2981
	}
2982
2983
	if ( $url && ! $parsed ) {
2984
		$parsed = $wp_embed->run_shortcode( $shortcode );
2985
	}
2986
2987
	if ( ! $parsed ) {
2988
		wp_send_json_error( array(
2989
			'type' => 'not-embeddable',
2990
			'message' => sprintf( __( '%s failed to embed.' ), '<code>' . esc_html( $url ) . '</code>' ),
2991
		) );
2992
	}
2993
2994
	if ( has_shortcode( $parsed, 'audio' ) || has_shortcode( $parsed, 'video' ) ) {
2995
		$styles = '';
2996
		$mce_styles = wpview_media_sandbox_styles();
2997
		foreach ( $mce_styles as $style ) {
2998
			$styles .= sprintf( '<link rel="stylesheet" href="%s"/>', $style );
2999
		}
3000
3001
		$html = do_shortcode( $parsed );
3002
3003
		global $wp_scripts;
3004
		if ( ! empty( $wp_scripts ) ) {
3005
			$wp_scripts->done = array();
3006
		}
3007
		ob_start();
3008
		wp_print_scripts( 'wp-mediaelement' );
3009
		$scripts = ob_get_clean();
3010
3011
		$parsed = $styles . $html . $scripts;
3012
	}
3013
3014
3015
	if ( ! empty( $no_ssl_support ) || ( is_ssl() && ( preg_match( '%<(iframe|script|embed) [^>]*src="http://%', $parsed ) ||
3016
		preg_match( '%<link [^>]*href="http://%', $parsed ) ) ) ) {
3017
		// Admin is ssl and the embed is not. Iframes, scripts, and other "active content" will be blocked.
3018
		wp_send_json_error( array(
3019
			'type' => 'not-ssl',
3020
			'message' => __( 'This preview is unavailable in the editor.' ),
3021
		) );
3022
	}
3023
3024
	wp_send_json_success( array(
3025
		'body' => $parsed,
3026
		'attr' => $wp_embed->last_attr
3027
	) );
3028
}
3029
3030
/**
3031
 * @since 4.0.0
3032
 *
3033
 * @global WP_Post    $post
3034
 * @global WP_Scripts $wp_scripts
3035
 */
3036
function wp_ajax_parse_media_shortcode() {
3037
	global $post, $wp_scripts;
3038
3039
	if ( empty( $_POST['shortcode'] ) ) {
3040
		wp_send_json_error();
3041
	}
3042
3043
	$shortcode = wp_unslash( $_POST['shortcode'] );
3044
3045
	if ( ! empty( $_POST['post_ID'] ) ) {
3046
		$post = get_post( (int) $_POST['post_ID'] );
3047
	}
3048
3049
	// the embed shortcode requires a post
3050
	if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) {
3051
		if ( 'embed' === $shortcode ) {
3052
			wp_send_json_error();
3053
		}
3054
	} else {
3055
		setup_postdata( $post );
0 ignored issues
show
It seems like $post defined by get_post((int) $_POST['post_ID']) on line 3046 can also be of type array; however, setup_postdata() does only seem to accept object|integer, 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...
3056
	}
3057
3058
	$parsed = do_shortcode( $shortcode  );
0 ignored issues
show
It seems like $shortcode defined by wp_unslash($_POST['shortcode']) on line 3043 can also be of type array; however, do_shortcode() 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...
3059
3060
	if ( empty( $parsed ) ) {
3061
		wp_send_json_error( array(
3062
			'type' => 'no-items',
3063
			'message' => __( 'No items found.' ),
3064
		) );
3065
	}
3066
3067
	$head = '';
3068
	$styles = wpview_media_sandbox_styles();
3069
3070
	foreach ( $styles as $style ) {
3071
		$head .= '<link type="text/css" rel="stylesheet" href="' . $style . '">';
3072
	}
3073
3074
	if ( ! empty( $wp_scripts ) ) {
3075
		$wp_scripts->done = array();
3076
	}
3077
3078
	ob_start();
3079
3080
	echo $parsed;
3081
3082
	if ( 'playlist' === $_REQUEST['type'] ) {
3083
		wp_underscore_playlist_templates();
3084
3085
		wp_print_scripts( 'wp-playlist' );
3086
	} else {
3087
		wp_print_scripts( array( 'froogaloop', 'wp-mediaelement' ) );
3088
	}
3089
3090
	wp_send_json_success( array(
3091
		'head' => $head,
3092
		'body' => ob_get_clean()
3093
	) );
3094
}
3095
3096
/**
3097
 * Ajax handler for destroying multiple open sessions for a user.
3098
 *
3099
 * @since 4.1.0
3100
 */
3101
function wp_ajax_destroy_sessions() {
3102
	$user = get_userdata( (int) $_POST['user_id'] );
3103
	if ( $user ) {
3104
		if ( ! current_user_can( 'edit_user', $user->ID ) ) {
3105
			$user = false;
3106
		} elseif ( ! wp_verify_nonce( $_POST['nonce'], 'update-user_' . $user->ID ) ) {
3107
			$user = false;
3108
		}
3109
	}
3110
3111
	if ( ! $user ) {
3112
		wp_send_json_error( array(
3113
			'message' => __( 'Could not log out user sessions. Please try again.' ),
3114
		) );
3115
	}
3116
3117
	$sessions = WP_Session_Tokens::get_instance( $user->ID );
3118
3119
	if ( $user->ID === get_current_user_id() ) {
3120
		$sessions->destroy_others( wp_get_session_token() );
3121
		$message = __( 'You are now logged out everywhere else.' );
3122
	} else {
3123
		$sessions->destroy_all();
3124
		/* translators: 1: User's display name. */
3125
		$message = sprintf( __( '%s has been logged out.' ), $user->display_name );
3126
	}
3127
3128
	wp_send_json_success( array( 'message' => $message ) );
3129
}
3130
3131
/**
3132
 * Ajax handler for saving a post from Press This.
3133
 *
3134
 * @since 4.2.0
3135
 *
3136
 * @global WP_Press_This $wp_press_this
3137
 */
3138
function wp_ajax_press_this_save_post() {
3139
	if ( empty( $GLOBALS['wp_press_this'] ) ) {
3140
		include( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
3141
	}
3142
3143
	$GLOBALS['wp_press_this']->save_post();
3144
}
3145
3146
/**
3147
 * Ajax handler for creating new category from Press This.
3148
 *
3149
 * @since 4.2.0
3150
 *
3151
 * @global WP_Press_This $wp_press_this
3152
 */
3153
function wp_ajax_press_this_add_category() {
3154
	if ( empty( $GLOBALS['wp_press_this'] ) ) {
3155
		include( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
3156
	}
3157
3158
	$GLOBALS['wp_press_this']->add_category();
3159
}
3160
3161
/**
3162
 * Ajax handler for cropping an image.
3163
 *
3164
 * @since 4.3.0
3165
 *
3166
 * @global WP_Site_Icon $wp_site_icon
3167
 */
3168
function wp_ajax_crop_image() {
3169
	$attachment_id = absint( $_POST['id'] );
3170
3171
	check_ajax_referer( 'image_editor-' . $attachment_id, 'nonce' );
3172
	if ( ! current_user_can( 'customize' ) ) {
3173
		wp_send_json_error();
3174
	}
3175
3176
	$context = str_replace( '_', '-', $_POST['context'] );
3177
	$data    = array_map( 'absint', $_POST['cropDetails'] );
3178
	$cropped = wp_crop_image( $attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height'] );
3179
3180
	if ( ! $cropped || is_wp_error( $cropped ) ) {
3181
		wp_send_json_error( array( 'message' => __( 'Image could not be processed.' ) ) );
3182
	}
3183
3184
	switch ( $context ) {
3185
		case 'site-icon':
3186
			require_once ABSPATH . '/wp-admin/includes/class-wp-site-icon.php';
3187
			global $wp_site_icon;
3188
3189
			// Skip creating a new attachment if the attachment is a Site Icon.
3190
			if ( get_post_meta( $attachment_id, '_wp_attachment_context', true ) == $context ) {
3191
3192
				// Delete the temporary cropped file, we don't need it.
3193
				wp_delete_file( $cropped );
0 ignored issues
show
It seems like $cropped defined by wp_crop_image($attachmen...], $data['dst_height']) on line 3178 can also be of type object<WP_Error>; however, wp_delete_file() 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...
3194
3195
				// Additional sizes in wp_prepare_attachment_for_js().
3196
				add_filter( 'image_size_names_choose', array( $wp_site_icon, 'additional_sizes' ) );
3197
				break;
3198
			}
3199
3200
			/** This filter is documented in wp-admin/custom-header.php */
3201
			$cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
3202
			$object  = $wp_site_icon->create_attachment_object( $cropped, $attachment_id );
3203
			unset( $object['ID'] );
3204
3205
			// Update the attachment.
3206
			add_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) );
3207
			$attachment_id = $wp_site_icon->insert_attachment( $object, $cropped );
3208
			remove_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) );
3209
3210
			// Additional sizes in wp_prepare_attachment_for_js().
3211
			add_filter( 'image_size_names_choose', array( $wp_site_icon, 'additional_sizes' ) );
3212
			break;
3213
3214
		default:
3215
3216
			/**
3217
			 * Fires before a cropped image is saved.
3218
			 *
3219
			 * Allows to add filters to modify the way a cropped image is saved.
3220
			 *
3221
			 * @since 4.3.0
3222
			 *
3223
			 * @param string $context       The Customizer control requesting the cropped image.
3224
			 * @param int    $attachment_id The attachment ID of the original image.
3225
			 * @param string $cropped       Path to the cropped image file.
3226
			 */
3227
			do_action( 'wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped );
3228
3229
			/** This filter is documented in wp-admin/custom-header.php */
3230
			$cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
3231
3232
			$parent_url = wp_get_attachment_url( $attachment_id );
3233
			$url        = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
3234
3235
			$size       = @getimagesize( $cropped );
3236
			$image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
3237
3238
			$object = array(
3239
				'post_title'     => basename( $cropped ),
3240
				'post_content'   => $url,
3241
				'post_mime_type' => $image_type,
3242
				'guid'           => $url,
3243
				'context'        => $context,
3244
			);
3245
3246
			$attachment_id = wp_insert_attachment( $object, $cropped );
3247
			$metadata = wp_generate_attachment_metadata( $attachment_id, $cropped );
3248
3249
			/**
3250
			 * Filters the cropped image attachment metadata.
3251
			 *
3252
			 * @since 4.3.0
3253
			 *
3254
			 * @see wp_generate_attachment_metadata()
3255
			 *
3256
			 * @param array $metadata Attachment metadata.
3257
			 */
3258
			$metadata = apply_filters( 'wp_ajax_cropped_attachment_metadata', $metadata );
3259
			wp_update_attachment_metadata( $attachment_id, $metadata );
0 ignored issues
show
It seems like $attachment_id defined by wp_insert_attachment($object, $cropped) on line 3246 can also be of type object<WP_Error>; however, wp_update_attachment_metadata() does only seem to accept integer, 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...
3260
3261
			/**
3262
			 * Filters the attachment ID for a cropped image.
3263
			 *
3264
			 * @since 4.3.0
3265
			 *
3266
			 * @param int    $attachment_id The attachment ID of the cropped image.
3267
			 * @param string $context       The Customizer control requesting the cropped image.
3268
			 */
3269
			$attachment_id = apply_filters( 'wp_ajax_cropped_attachment_id', $attachment_id, $context );
3270
	}
3271
3272
	wp_send_json_success( wp_prepare_attachment_for_js( $attachment_id ) );
3273
}
3274
3275
/**
3276
 * Ajax handler for generating a password.
3277
 *
3278
 * @since 4.4.0
3279
 */
3280
function wp_ajax_generate_password() {
3281
	wp_send_json_success( wp_generate_password( 24 ) );
3282
}
3283
3284
/**
3285
 * Ajax handler for saving the user's WordPress.org username.
3286
 *
3287
 * @since 4.4.0
3288
 */
3289
function wp_ajax_save_wporg_username() {
3290
	if ( ! current_user_can( 'install_themes' ) && ! current_user_can( 'install_plugins' ) ) {
3291
		wp_send_json_error();
3292
	}
3293
3294
	check_ajax_referer( 'save_wporg_username_' . get_current_user_id() );
3295
3296
	$username = isset( $_REQUEST['username'] ) ? wp_unslash( $_REQUEST['username'] ) : false;
3297
3298
	if ( ! $username ) {
3299
		wp_send_json_error();
3300
	}
3301
3302
	wp_send_json_success( update_user_meta( get_current_user_id(), 'wporg_favorites', $username ) );
3303
}
3304
3305
/**
3306
 * Ajax handler for installing a theme.
3307
 *
3308
 * @since 4.6.0
3309
 *
3310
 * @see Theme_Upgrader
3311
 */
3312
function wp_ajax_install_theme() {
3313
	check_ajax_referer( 'updates' );
3314
3315
	if ( empty( $_POST['slug'] ) ) {
3316
		wp_send_json_error( array(
3317
			'slug'         => '',
3318
			'errorCode'    => 'no_theme_specified',
3319
			'errorMessage' => __( 'No theme specified.' ),
3320
		) );
3321
	}
3322
3323
	$slug = sanitize_key( wp_unslash( $_POST['slug'] ) );
0 ignored issues
show
It seems like wp_unslash($_POST['slug']) targeting wp_unslash() can also be of type array; however, sanitize_key() 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...
3324
3325
	$status = array(
3326
		'install' => 'theme',
3327
		'slug'    => $slug,
3328
	);
3329
3330
	if ( ! current_user_can( 'install_themes' ) ) {
3331
		$status['errorMessage'] = __( 'Sorry, you are not allowed to install themes on this site.' );
3332
		wp_send_json_error( $status );
3333
	}
3334
3335
	include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
3336
	include_once( ABSPATH . 'wp-admin/includes/theme.php' );
3337
3338
	$api = themes_api( 'theme_information', array(
3339
		'slug'   => $slug,
3340
		'fields' => array( 'sections' => false ),
3341
	) );
3342
3343
	if ( is_wp_error( $api ) ) {
3344
		$status['errorMessage'] = $api->get_error_message();
3345
		wp_send_json_error( $status );
3346
	}
3347
3348
	$skin     = new WP_Ajax_Upgrader_Skin();
3349
	$upgrader = new Theme_Upgrader( $skin );
3350
	$result   = $upgrader->install( $api->download_link );
3351
3352
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
3353
		$status['debug'] = $skin->get_upgrade_messages();
3354
	}
3355
3356 View Code Duplication
	if ( is_wp_error( $result ) ) {
3357
		$status['errorCode']    = $result->get_error_code();
3358
		$status['errorMessage'] = $result->get_error_message();
3359
		wp_send_json_error( $status );
3360
	} elseif ( is_wp_error( $skin->result ) ) {
3361
		$status['errorCode']    = $skin->result->get_error_code();
3362
		$status['errorMessage'] = $skin->result->get_error_message();
3363
		wp_send_json_error( $status );
3364
	} elseif ( $skin->get_errors()->get_error_code() ) {
3365
		$status['errorMessage'] = $skin->get_error_messages();
3366
		wp_send_json_error( $status );
3367
	} elseif ( is_null( $result ) ) {
3368
		global $wp_filesystem;
3369
3370
		$status['errorCode']    = 'unable_to_connect_to_filesystem';
3371
		$status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
3372
3373
		// Pass through the error from WP_Filesystem if one was raised.
3374
		if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
3375
			$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
3376
		}
3377
3378
		wp_send_json_error( $status );
3379
	}
3380
3381
	$status['themeName'] = wp_get_theme( $slug )->get( 'Name' );
3382
3383
	if ( current_user_can( 'switch_themes' ) ) {
3384
		if ( is_multisite() ) {
3385
			$status['activateUrl'] = add_query_arg( array(
3386
				'action'   => 'enable',
3387
				'_wpnonce' => wp_create_nonce( 'enable-theme_' . $slug ),
3388
				'theme'    => $slug,
3389
			), network_admin_url( 'themes.php' ) );
3390
		} else {
3391
			$status['activateUrl'] = add_query_arg( array(
3392
				'action'     => 'activate',
3393
				'_wpnonce'   => wp_create_nonce( 'switch-theme_' . $slug ),
3394
				'stylesheet' => $slug,
3395
			), admin_url( 'themes.php' ) );
3396
		}
3397
	}
3398
3399 View Code Duplication
	if ( ! is_multisite() && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
3400
		$status['customizeUrl'] = add_query_arg( array(
3401
			'return' => urlencode( network_admin_url( 'theme-install.php', 'relative' ) ),
3402
		), wp_customize_url( $slug ) );
3403
	}
3404
3405
	/*
3406
	 * See WP_Theme_Install_List_Table::_get_theme_status() if we wanted to check
3407
	 * on post-install status.
3408
	 */
3409
	wp_send_json_success( $status );
3410
}
3411
3412
/**
3413
 * Ajax handler for updating a theme.
3414
 *
3415
 * @since 4.6.0
3416
 *
3417
 * @see Theme_Upgrader
3418
 */
3419
function wp_ajax_update_theme() {
3420
	check_ajax_referer( 'updates' );
3421
3422
	if ( empty( $_POST['slug'] ) ) {
3423
		wp_send_json_error( array(
3424
			'slug'         => '',
3425
			'errorCode'    => 'no_theme_specified',
3426
			'errorMessage' => __( 'No theme specified.' ),
3427
		) );
3428
	}
3429
3430
	$stylesheet = sanitize_key( wp_unslash( $_POST['slug'] ) );
0 ignored issues
show
It seems like wp_unslash($_POST['slug']) targeting wp_unslash() can also be of type array; however, sanitize_key() 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...
3431
	$status     = array(
3432
		'update'     => 'theme',
3433
		'slug'       => $stylesheet,
3434
		'newVersion' => '',
3435
	);
3436
3437
	if ( ! current_user_can( 'update_themes' ) ) {
3438
		$status['errorMessage'] = __( 'Sorry, you are not allowed to update themes for this site.' );
3439
		wp_send_json_error( $status );
3440
	}
3441
3442
	include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
3443
3444
	$current = get_site_transient( 'update_themes' );
3445
	if ( empty( $current ) ) {
3446
		wp_update_themes();
3447
	}
3448
3449
	$skin     = new WP_Ajax_Upgrader_Skin();
3450
	$upgrader = new Theme_Upgrader( $skin );
3451
	$result   = $upgrader->bulk_upgrade( array( $stylesheet ) );
3452
3453
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
3454
		$status['debug'] = $skin->get_upgrade_messages();
3455
	}
3456
3457
	if ( is_wp_error( $skin->result ) ) {
3458
		$status['errorCode']    = $skin->result->get_error_code();
3459
		$status['errorMessage'] = $skin->result->get_error_message();
3460
		wp_send_json_error( $status );
3461
	} elseif ( $skin->get_errors()->get_error_code() ) {
3462
		$status['errorMessage'] = $skin->get_error_messages();
3463
		wp_send_json_error( $status );
3464
	} elseif ( is_array( $result ) && ! empty( $result[ $stylesheet ] ) ) {
3465
3466
		// Theme is already at the latest version.
3467
		if ( true === $result[ $stylesheet ] ) {
3468
			$status['errorMessage'] = $upgrader->strings['up_to_date'];
3469
			wp_send_json_error( $status );
3470
		}
3471
3472
		$theme = wp_get_theme( $stylesheet );
3473
		if ( $theme->get( 'Version' ) ) {
3474
			$status['newVersion'] = $theme->get( 'Version' );
3475
		}
3476
3477
		wp_send_json_success( $status );
3478
	} elseif ( false === $result ) {
3479
		global $wp_filesystem;
3480
3481
		$status['errorCode']    = 'unable_to_connect_to_filesystem';
3482
		$status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
3483
3484
		// Pass through the error from WP_Filesystem if one was raised.
3485
		if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
3486
			$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
3487
		}
3488
3489
		wp_send_json_error( $status );
3490
	}
3491
3492
	// An unhandled error occurred.
3493
	$status['errorMessage'] = __( 'Update failed.' );
3494
	wp_send_json_error( $status );
3495
}
3496
3497
/**
3498
 * Ajax handler for deleting a theme.
3499
 *
3500
 * @since 4.6.0
3501
 *
3502
 * @see delete_theme()
3503
 */
3504
function wp_ajax_delete_theme() {
3505
	check_ajax_referer( 'updates' );
3506
3507
	if ( empty( $_POST['slug'] ) ) {
3508
		wp_send_json_error( array(
3509
			'slug'         => '',
3510
			'errorCode'    => 'no_theme_specified',
3511
			'errorMessage' => __( 'No theme specified.' ),
3512
		) );
3513
	}
3514
3515
	$stylesheet = sanitize_key( wp_unslash( $_POST['slug'] ) );
0 ignored issues
show
It seems like wp_unslash($_POST['slug']) targeting wp_unslash() can also be of type array; however, sanitize_key() 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...
3516
	$status     = array(
3517
		'delete' => 'theme',
3518
		'slug'   => $stylesheet,
3519
	);
3520
3521
	if ( ! current_user_can( 'delete_themes' ) ) {
3522
		$status['errorMessage'] = __( 'Sorry, you are not allowed to delete themes on this site.' );
3523
		wp_send_json_error( $status );
3524
	}
3525
3526
	if ( ! wp_get_theme( $stylesheet )->exists() ) {
3527
		$status['errorMessage'] = __( 'The requested theme does not exist.' );
3528
		wp_send_json_error( $status );
3529
	}
3530
3531
	// Check filesystem credentials. `delete_theme()` will bail otherwise.
3532
	$url = wp_nonce_url( 'themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet );
3533
	ob_start();
3534
	$credentials = request_filesystem_credentials( $url );
3535
	ob_end_clean();
3536
	if ( false === $credentials || ! WP_Filesystem( $credentials ) ) {
0 ignored issues
show
$credentials is of type boolean, but the function expects a false|array.

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...
3537
		global $wp_filesystem;
3538
3539
		$status['errorCode']    = 'unable_to_connect_to_filesystem';
3540
		$status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
3541
3542
		// Pass through the error from WP_Filesystem if one was raised.
3543
		if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
3544
			$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
3545
		}
3546
3547
		wp_send_json_error( $status );
3548
	}
3549
3550
	include_once( ABSPATH . 'wp-admin/includes/theme.php' );
3551
3552
	$result = delete_theme( $stylesheet );
3553
3554 View Code Duplication
	if ( is_wp_error( $result ) ) {
3555
		$status['errorMessage'] = $result->get_error_message();
3556
		wp_send_json_error( $status );
3557
	} elseif ( false === $result ) {
3558
		$status['errorMessage'] = __( 'Theme could not be deleted.' );
3559
		wp_send_json_error( $status );
3560
	}
3561
3562
	wp_send_json_success( $status );
3563
}
3564
3565
/**
3566
 * Ajax handler for installing a plugin.
3567
 *
3568
 * @since 4.6.0
3569
 *
3570
 * @see Plugin_Upgrader
3571
 */
3572
function wp_ajax_install_plugin() {
3573
	check_ajax_referer( 'updates' );
3574
3575 View Code Duplication
	if ( empty( $_POST['slug'] ) ) {
3576
		wp_send_json_error( array(
3577
			'slug'         => '',
3578
			'errorCode'    => 'no_plugin_specified',
3579
			'errorMessage' => __( 'No plugin specified.' ),
3580
		) );
3581
	}
3582
3583
	$status = array(
3584
		'install' => 'plugin',
3585
		'slug'    => sanitize_key( wp_unslash( $_POST['slug'] ) ),
0 ignored issues
show
It seems like wp_unslash($_POST['slug']) targeting wp_unslash() can also be of type array; however, sanitize_key() 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...
3586
	);
3587
3588
	if ( ! current_user_can( 'install_plugins' ) ) {
3589
		$status['errorMessage'] = __( 'Sorry, you are not allowed to install plugins on this site.' );
3590
		wp_send_json_error( $status );
3591
	}
3592
3593
	include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
3594
	include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
3595
3596
	$api = plugins_api( 'plugin_information', array(
3597
		'slug'   => sanitize_key( wp_unslash( $_POST['slug'] ) ),
0 ignored issues
show
It seems like wp_unslash($_POST['slug']) targeting wp_unslash() can also be of type array; however, sanitize_key() 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...
3598
		'fields' => array(
3599
			'sections' => false,
3600
		),
3601
	) );
3602
3603
	if ( is_wp_error( $api ) ) {
3604
		$status['errorMessage'] = $api->get_error_message();
3605
		wp_send_json_error( $status );
3606
	}
3607
3608
	$status['pluginName'] = $api->name;
3609
3610
	$skin     = new WP_Ajax_Upgrader_Skin();
3611
	$upgrader = new Plugin_Upgrader( $skin );
3612
	$result   = $upgrader->install( $api->download_link );
3613
3614
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
3615
		$status['debug'] = $skin->get_upgrade_messages();
3616
	}
3617
3618 View Code Duplication
	if ( is_wp_error( $result ) ) {
3619
		$status['errorCode']    = $result->get_error_code();
3620
		$status['errorMessage'] = $result->get_error_message();
3621
		wp_send_json_error( $status );
3622
	} elseif ( is_wp_error( $skin->result ) ) {
3623
		$status['errorCode']    = $skin->result->get_error_code();
3624
		$status['errorMessage'] = $skin->result->get_error_message();
3625
		wp_send_json_error( $status );
3626
	} elseif ( $skin->get_errors()->get_error_code() ) {
3627
		$status['errorMessage'] = $skin->get_error_messages();
3628
		wp_send_json_error( $status );
3629
	} elseif ( is_null( $result ) ) {
3630
		global $wp_filesystem;
3631
3632
		$status['errorCode']    = 'unable_to_connect_to_filesystem';
3633
		$status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
3634
3635
		// Pass through the error from WP_Filesystem if one was raised.
3636
		if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
3637
			$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
3638
		}
3639
3640
		wp_send_json_error( $status );
3641
	}
3642
3643
	$install_status = install_plugin_install_status( $api );
3644
3645
	if ( current_user_can( 'activate_plugins' ) && is_plugin_inactive( $install_status['file'] ) ) {
3646
		$status['activateUrl'] = add_query_arg( array(
3647
			'_wpnonce' => wp_create_nonce( 'activate-plugin_' . $install_status['file'] ),
3648
			'action'   => 'activate',
3649
			'plugin'   => $install_status['file'],
3650
		), network_admin_url( 'plugins.php' ) );
3651
	}
3652
3653
	if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) {
3654
		$status['activateUrl'] = add_query_arg( array( 'networkwide' => 1 ), $status['activateUrl'] );
3655
	}
3656
3657
	wp_send_json_success( $status );
3658
}
3659
3660
/**
3661
 * Ajax handler for updating a plugin.
3662
 *
3663
 * @since 4.2.0
3664
 *
3665
 * @see Plugin_Upgrader
3666
 */
3667
function wp_ajax_update_plugin() {
3668
	check_ajax_referer( 'updates' );
3669
3670 View Code Duplication
	if ( empty( $_POST['plugin'] ) || empty( $_POST['slug'] ) ) {
3671
		wp_send_json_error( array(
3672
			'slug'         => '',
3673
			'errorCode'    => 'no_plugin_specified',
3674
			'errorMessage' => __( 'No plugin specified.' ),
3675
		) );
3676
	}
3677
3678
	$plugin = plugin_basename( sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) );
0 ignored issues
show
It seems like wp_unslash($_POST['plugin']) targeting wp_unslash() can also be of type array; however, sanitize_text_field() 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...
3679
3680
	$status = array(
3681
		'update'     => 'plugin',
3682
		'slug'       => sanitize_key( wp_unslash( $_POST['slug'] ) ),
0 ignored issues
show
It seems like wp_unslash($_POST['slug']) targeting wp_unslash() can also be of type array; however, sanitize_key() 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...
3683
		'oldVersion' => '',
3684
		'newVersion' => '',
3685
	);
3686
3687 View Code Duplication
	if ( ! current_user_can( 'update_plugins' ) || 0 !== validate_file( $plugin ) ) {
3688
		$status['errorMessage'] = __( 'Sorry, you are not allowed to update plugins for this site.' );
3689
		wp_send_json_error( $status );
3690
	}
3691
3692
	$plugin_data          = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
3693
	$status['plugin']     = $plugin;
3694
	$status['pluginName'] = $plugin_data['Name'];
3695
3696 View Code Duplication
	if ( $plugin_data['Version'] ) {
3697
		/* translators: %s: Plugin version */
3698
		$status['oldVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
3699
	}
3700
3701
	include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
3702
3703
	wp_update_plugins();
3704
3705
	$skin     = new WP_Ajax_Upgrader_Skin();
3706
	$upgrader = new Plugin_Upgrader( $skin );
3707
	$result   = $upgrader->bulk_upgrade( array( $plugin ) );
3708
3709
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
3710
		$status['debug'] = $skin->get_upgrade_messages();
3711
	}
3712
3713
	if ( is_wp_error( $skin->result ) ) {
3714
		$status['errorCode']    = $skin->result->get_error_code();
3715
		$status['errorMessage'] = $skin->result->get_error_message();
3716
		wp_send_json_error( $status );
3717
	} elseif ( $skin->get_errors()->get_error_code() ) {
3718
		$status['errorMessage'] = $skin->get_error_messages();
3719
		wp_send_json_error( $status );
3720
	} elseif ( is_array( $result ) && ! empty( $result[ $plugin ] ) ) {
3721
		$plugin_update_data = current( $result );
3722
3723
		/*
3724
		 * If the `update_plugins` site transient is empty (e.g. when you update
3725
		 * two plugins in quick succession before the transient repopulates),
3726
		 * this may be the return.
3727
		 *
3728
		 * Preferably something can be done to ensure `update_plugins` isn't empty.
3729
		 * For now, surface some sort of error here.
3730
		 */
3731
		if ( true === $plugin_update_data ) {
3732
			$status['errorMessage'] = __( 'Plugin update failed.' );
3733
			wp_send_json_error( $status );
3734
		}
3735
3736
		$plugin_data = get_plugins( '/' . $result[ $plugin ]['destination_name'] );
3737
		$plugin_data = reset( $plugin_data );
3738
3739 View Code Duplication
		if ( $plugin_data['Version'] ) {
3740
			/* translators: %s: Plugin version */
3741
			$status['newVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
3742
		}
3743
		wp_send_json_success( $status );
3744
	} elseif ( false === $result ) {
3745
		global $wp_filesystem;
3746
3747
		$status['errorCode']    = 'unable_to_connect_to_filesystem';
3748
		$status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
3749
3750
		// Pass through the error from WP_Filesystem if one was raised.
3751
		if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
3752
			$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
3753
		}
3754
3755
		wp_send_json_error( $status );
3756
	}
3757
3758
	// An unhandled error occurred.
3759
	$status['errorMessage'] = __( 'Plugin update failed.' );
3760
	wp_send_json_error( $status );
3761
}
3762
3763
/**
3764
 * Ajax handler for deleting a plugin.
3765
 *
3766
 * @since 4.6.0
3767
 *
3768
 * @see delete_plugins()
3769
 */
3770
function wp_ajax_delete_plugin() {
3771
	check_ajax_referer( 'updates' );
3772
3773 View Code Duplication
	if ( empty( $_POST['slug'] ) || empty( $_POST['plugin'] ) ) {
3774
		wp_send_json_error( array(
3775
			'slug'         => '',
3776
			'errorCode'    => 'no_plugin_specified',
3777
			'errorMessage' => __( 'No plugin specified.' ),
3778
		) );
3779
	}
3780
3781
	$plugin = plugin_basename( sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) );
0 ignored issues
show
It seems like wp_unslash($_POST['plugin']) targeting wp_unslash() can also be of type array; however, sanitize_text_field() 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...
3782
3783
	$status = array(
3784
		'delete' => 'plugin',
3785
		'slug'   => sanitize_key( wp_unslash( $_POST['slug'] ) ),
0 ignored issues
show
It seems like wp_unslash($_POST['slug']) targeting wp_unslash() can also be of type array; however, sanitize_key() 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...
3786
	);
3787
3788 View Code Duplication
	if ( ! current_user_can( 'delete_plugins' ) || 0 !== validate_file( $plugin ) ) {
3789
		$status['errorMessage'] = __( 'Sorry, you are not allowed to delete plugins for this site.' );
3790
		wp_send_json_error( $status );
3791
	}
3792
3793
	$plugin_data          = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
3794
	$status['plugin']     = $plugin;
3795
	$status['pluginName'] = $plugin_data['Name'];
3796
3797
	if ( is_plugin_active( $plugin ) ) {
3798
		$status['errorMessage'] = __( 'You cannot delete a plugin while it is active on the main site.' );
3799
		wp_send_json_error( $status );
3800
	}
3801
3802
	// Check filesystem credentials. `delete_plugins()` will bail otherwise.
3803
	$url = wp_nonce_url( 'plugins.php?action=delete-selected&verify-delete=1&checked[]=' . $plugin, 'bulk-plugins' );
3804
	ob_start();
3805
	$credentials = request_filesystem_credentials( $url );
3806
	ob_end_clean();
3807
	if ( false === $credentials || ! WP_Filesystem( $credentials ) ) {
0 ignored issues
show
$credentials is of type boolean, but the function expects a false|array.

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...
3808
		global $wp_filesystem;
3809
3810
		$status['errorCode']    = 'unable_to_connect_to_filesystem';
3811
		$status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
3812
3813
		// Pass through the error from WP_Filesystem if one was raised.
3814
		if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
3815
			$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
3816
		}
3817
3818
		wp_send_json_error( $status );
3819
	}
3820
3821
	$result = delete_plugins( array( $plugin ) );
3822
3823 View Code Duplication
	if ( is_wp_error( $result ) ) {
3824
		$status['errorMessage'] = $result->get_error_message();
3825
		wp_send_json_error( $status );
3826
	} elseif ( false === $result ) {
3827
		$status['errorMessage'] = __( 'Plugin could not be deleted.' );
3828
		wp_send_json_error( $status );
3829
	}
3830
3831
	wp_send_json_success( $status );
3832
}
3833
3834
/**
3835
 * Ajax handler for searching plugins.
3836
 *
3837
 * @since 4.6.0
3838
 *
3839
 * @global string $s Search term.
3840
 */
3841
function wp_ajax_search_plugins() {
3842
	check_ajax_referer( 'updates' );
3843
3844
	$pagenow = isset( $_POST['pagenow'] ) ? sanitize_key( $_POST['pagenow'] ) : '';
3845
	if ( 'plugins-network' === $pagenow || 'plugins' === $pagenow ) {
3846
		set_current_screen( $pagenow );
3847
	}
3848
3849
	/** @var WP_Plugins_List_Table $wp_list_table */
3850
	$wp_list_table = _get_list_table( 'WP_Plugins_List_Table', array(
3851
		'screen' => get_current_screen(),
3852
	) );
3853
3854
	$status = array();
3855
3856
	if ( ! $wp_list_table->ajax_user_can() ) {
3857
		$status['errorMessage'] = __( 'Sorry, you are not allowed to manage plugins for this site.' );
3858
		wp_send_json_error( $status );
3859
	}
3860
3861
	// Set the correct requester, so pagination works.
3862
	$_SERVER['REQUEST_URI'] = add_query_arg( array_diff_key( $_POST, array(
3863
		'_ajax_nonce' => null,
3864
		'action'      => null,
3865
	) ), network_admin_url( 'plugins.php', 'relative' ) );
3866
3867
	$GLOBALS['s'] = wp_unslash( $_POST['s'] );
3868
3869
	$wp_list_table->prepare_items();
3870
3871
	ob_start();
3872
	$wp_list_table->display();
3873
	$status['count'] = count( $wp_list_table->items );
3874
	$status['items'] = ob_get_clean();
3875
3876
	wp_send_json_success( $status );
3877
}
3878
3879
/**
3880
 * Ajax handler for searching plugins to install.
3881
 *
3882
 * @since 4.6.0
3883
 */
3884
function wp_ajax_search_install_plugins() {
3885
	check_ajax_referer( 'updates' );
3886
3887
	$pagenow = isset( $_POST['pagenow'] ) ? sanitize_key( $_POST['pagenow'] ) : '';
3888
	if ( 'plugin-install-network' === $pagenow || 'plugin-install' === $pagenow ) {
3889
		set_current_screen( $pagenow );
3890
	}
3891
3892
	/** @var WP_Plugin_Install_List_Table $wp_list_table */
3893
	$wp_list_table = _get_list_table( 'WP_Plugin_Install_List_Table', array(
3894
		'screen' => get_current_screen(),
3895
	) );
3896
3897
	$status = array();
3898
3899
	if ( ! $wp_list_table->ajax_user_can() ) {
3900
		$status['errorMessage'] = __( 'Sorry, you are not allowed to manage plugins for this site.' );
3901
		wp_send_json_error( $status );
3902
	}
3903
3904
	// Set the correct requester, so pagination works.
3905
	$_SERVER['REQUEST_URI'] = add_query_arg( array_diff_key( $_POST, array(
3906
		'_ajax_nonce' => null,
3907
		'action'      => null,
3908
	) ), network_admin_url( 'plugin-install.php', 'relative' ) );
3909
3910
	$wp_list_table->prepare_items();
3911
3912
	ob_start();
3913
	$wp_list_table->display();
3914
	$status['count'] = (int) $wp_list_table->get_pagination_arg( 'total_items' );
3915
	$status['items'] = ob_get_clean();
3916
3917
	wp_send_json_success( $status );
3918
}
3919