Issues (2010)

Security Analysis    not enabled

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

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

wp-includes/capabilities.php (7 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
2
/**
3
 * Core User Role & Capabilities API
4
 *
5
 * @package WordPress
6
 * @subpackage Users
7
 */
8
9
/**
10
 * Map meta capabilities to primitive capabilities.
11
 *
12
 * This does not actually compare whether the user ID has the actual capability,
13
 * just what the capability or capabilities are. Meta capability list value can
14
 * be 'delete_user', 'edit_user', 'remove_user', 'promote_user', 'delete_post',
15
 * 'delete_page', 'edit_post', 'edit_page', 'read_post', or 'read_page'.
16
 *
17
 * @since 2.0.0
18
 *
19
 * @global array $post_type_meta_caps Used to get post type meta capabilities.
20
 *
21
 * @param string $cap       Capability name.
22
 * @param int    $user_id   User ID.
23
 * @param int    $object_id Optional. ID of the specific object to check against if `$cap` is a "meta" cap.
0 ignored issues
show
There is no parameter named $object_id. Was it maybe removed?

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

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

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

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

Loading history...
24
 *                          "Meta" capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used
25
 *                          by map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
26
 *                          'edit_others_posts', etc. The parameter is accessed via func_get_args().
27
 * @return array Actual capabilities for meta capability.
28
 */
29
function map_meta_cap( $cap, $user_id ) {
30
	$args = array_slice( func_get_args(), 2 );
31
	$caps = array();
32
33
	switch ( $cap ) {
34
	case 'remove_user':
35
		$caps[] = 'remove_users';
36
		break;
37
	case 'promote_user':
38
	case 'add_users':
39
		$caps[] = 'promote_users';
40
		break;
41
	case 'edit_user':
42
	case 'edit_users':
43
		// Allow user to edit itself
44
		if ( 'edit_user' == $cap && isset( $args[0] ) && $user_id == $args[0] )
45
			break;
46
47
		// In multisite the user must have manage_network_users caps. If editing a super admin, the user must be a super admin.
48
		if ( is_multisite() && ( ( ! is_super_admin( $user_id ) && 'edit_user' === $cap && is_super_admin( $args[0] ) ) || ! user_can( $user_id, 'manage_network_users' ) ) ) {
49
			$caps[] = 'do_not_allow';
50
		} else {
51
			$caps[] = 'edit_users'; // edit_user maps to edit_users.
52
		}
53
		break;
54
	case 'delete_post':
55 View Code Duplication
	case 'delete_page':
56
		$post = get_post( $args[0] );
57
		if ( ! $post ) {
58
			$caps[] = 'do_not_allow';
59
			break;
60
		}
61
62
		if ( 'revision' == $post->post_type ) {
63
			$post = get_post( $post->post_parent );
64
			if ( ! $post ) {
65
				$caps[] = 'do_not_allow';
66
				break;
67
			}
68
		}
69
70
		$post_type = get_post_type_object( $post->post_type );
71
		if ( ! $post_type ) {
72
			/* translators: 1: post type, 2: capability name */
73
			_doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
74
			$caps[] = 'edit_others_posts';
75
			break;
76
		}
77
78
		if ( ! $post_type->map_meta_cap ) {
79
			$caps[] = $post_type->cap->$cap;
80
			// Prior to 3.1 we would re-call map_meta_cap here.
81
			if ( 'delete_post' == $cap )
82
				$cap = $post_type->cap->$cap;
83
			break;
84
		}
85
86
		// If the post author is set and the user is the author...
87
		if ( $post->post_author && $user_id == $post->post_author ) {
88
			// If the post is published or scheduled...
89
			if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
90
				$caps[] = $post_type->cap->delete_published_posts;
91
			} elseif ( 'trash' == $post->post_status ) {
92
				$status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
93
				if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
94
					$caps[] = $post_type->cap->delete_published_posts;
95
				} else {
96
					$caps[] = $post_type->cap->delete_posts;
97
				}
98
			} else {
99
				// If the post is draft...
100
				$caps[] = $post_type->cap->delete_posts;
101
			}
102
		} else {
103
			// The user is trying to edit someone else's post.
104
			$caps[] = $post_type->cap->delete_others_posts;
105
			// The post is published or scheduled, extra cap required.
106
			if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
107
				$caps[] = $post_type->cap->delete_published_posts;
108
			} elseif ( 'private' == $post->post_status ) {
109
				$caps[] = $post_type->cap->delete_private_posts;
110
			}
111
		}
112
		break;
113
		// edit_post breaks down to edit_posts, edit_published_posts, or
114
		// edit_others_posts
115
	case 'edit_post':
116 View Code Duplication
	case 'edit_page':
117
		$post = get_post( $args[0] );
118
		if ( ! $post ) {
119
			$caps[] = 'do_not_allow';
120
			break;
121
		}
122
123
		if ( 'revision' == $post->post_type ) {
124
			$post = get_post( $post->post_parent );
125
			if ( ! $post ) {
126
				$caps[] = 'do_not_allow';
127
				break;
128
			}
129
		}
130
131
		$post_type = get_post_type_object( $post->post_type );
132
		if ( ! $post_type ) {
133
			/* translators: 1: post type, 2: capability name */
134
			_doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
135
			$caps[] = 'edit_others_posts';
136
			break;
137
		}
138
139
		if ( ! $post_type->map_meta_cap ) {
140
			$caps[] = $post_type->cap->$cap;
141
			// Prior to 3.1 we would re-call map_meta_cap here.
142
			if ( 'edit_post' == $cap )
143
				$cap = $post_type->cap->$cap;
144
			break;
145
		}
146
147
		// If the post author is set and the user is the author...
148
		if ( $post->post_author && $user_id == $post->post_author ) {
149
			// If the post is published or scheduled...
150
			if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
151
				$caps[] = $post_type->cap->edit_published_posts;
152
			} elseif ( 'trash' == $post->post_status ) {
153
				$status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
154
				if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
155
					$caps[] = $post_type->cap->edit_published_posts;
156
				} else {
157
					$caps[] = $post_type->cap->edit_posts;
158
				}
159
			} else {
160
				// If the post is draft...
161
				$caps[] = $post_type->cap->edit_posts;
162
			}
163
		} else {
164
			// The user is trying to edit someone else's post.
165
			$caps[] = $post_type->cap->edit_others_posts;
166
			// The post is published or scheduled, extra cap required.
167
			if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
168
				$caps[] = $post_type->cap->edit_published_posts;
169
			} elseif ( 'private' == $post->post_status ) {
170
				$caps[] = $post_type->cap->edit_private_posts;
171
			}
172
		}
173
		break;
174
	case 'read_post':
175
	case 'read_page':
176
		$post = get_post( $args[0] );
177
		if ( ! $post ) {
178
			$caps[] = 'do_not_allow';
179
			break;
180
		}
181
182
		if ( 'revision' == $post->post_type ) {
183
			$post = get_post( $post->post_parent );
184
			if ( ! $post ) {
185
				$caps[] = 'do_not_allow';
186
				break;
187
			}
188
		}
189
190
		$post_type = get_post_type_object( $post->post_type );
191
		if ( ! $post_type ) {
192
			/* translators: 1: post type, 2: capability name */
193
			_doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
194
			$caps[] = 'edit_others_posts';
195
			break;
196
		}
197
198
		if ( ! $post_type->map_meta_cap ) {
199
			$caps[] = $post_type->cap->$cap;
200
			// Prior to 3.1 we would re-call map_meta_cap here.
201
			if ( 'read_post' == $cap )
202
				$cap = $post_type->cap->$cap;
203
			break;
204
		}
205
206
		$status_obj = get_post_status_object( $post->post_status );
207
		if ( $status_obj->public ) {
208
			$caps[] = $post_type->cap->read;
209
			break;
210
		}
211
212
		if ( $post->post_author && $user_id == $post->post_author ) {
213
			$caps[] = $post_type->cap->read;
214
		} elseif ( $status_obj->private ) {
215
			$caps[] = $post_type->cap->read_private_posts;
216
		} else {
217
			$caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
218
		}
219
		break;
220
	case 'publish_post':
221
		$post = get_post( $args[0] );
222
		if ( ! $post ) {
223
			$caps[] = 'do_not_allow';
224
			break;
225
		}
226
227
		$post_type = get_post_type_object( $post->post_type );
228
		if ( ! $post_type ) {
229
			/* translators: 1: post type, 2: capability name */
230
			_doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
231
			$caps[] = 'edit_others_posts';
232
			break;
233
		}
234
235
		$caps[] = $post_type->cap->publish_posts;
236
		break;
237
	case 'edit_post_meta':
238
	case 'delete_post_meta':
239
	case 'add_post_meta':
240
		$post = get_post( $args[0] );
241
		if ( ! $post ) {
242
			$caps[] = 'do_not_allow';
243
			break;
244
		}
245
246
		$post_type = get_post_type( $post );
0 ignored issues
show
It seems like $post defined by get_post($args[0]) on line 240 can also be of type array; however, get_post_type() 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...
247
248
		$caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
249
250
		$meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false;
251
252
		if ( $meta_key && ( has_filter( "auth_post_meta_{$meta_key}" ) || has_filter( "auth_post_{$post_type}_meta_{$meta_key}" ) ) ) {
253
			/**
254
			 * Filters whether the user is allowed to add post meta to a post.
255
			 *
256
			 * The dynamic portion of the hook name, `$meta_key`, refers to the
257
			 * meta key passed to map_meta_cap().
258
			 *
259
			 * @since 3.3.0
260
			 *
261
			 * @param bool   $allowed  Whether the user can add the post meta. Default false.
262
			 * @param string $meta_key The meta key.
263
			 * @param int    $post_id  Post ID.
264
			 * @param int    $user_id  User ID.
265
			 * @param string $cap      Capability name.
266
			 * @param array  $caps     User capabilities.
267
			 */
268
			$allowed = apply_filters( "auth_post_meta_{$meta_key}", false, $meta_key, $post->ID, $user_id, $cap, $caps );
269
270
			/**
271
			 * Filters whether the user is allowed to add post meta to a post of a given type.
272
			 *
273
			 * The dynamic portions of the hook name, `$meta_key` and `$post_type`,
274
			 * refer to the meta key passed to map_meta_cap() and the post type, respectively.
275
			 *
276
			 * @since 4.6.0
277
			 *
278
			 * @param bool   $allowed  Whether the user can add the post meta. Default false.
279
			 * @param string $meta_key The meta key.
280
			 * @param int    $post_id  Post ID.
281
			 * @param int    $user_id  User ID.
282
			 * @param string $cap      Capability name.
283
			 * @param array  $caps     User capabilities.
284
			 */
285
			$allowed = apply_filters( "auth_post_{$post_type}_meta_{$meta_key}", $allowed, $meta_key, $post->ID, $user_id, $cap, $caps );
286
287
			if ( ! $allowed )
288
				$caps[] = $cap;
289
		} elseif ( $meta_key && is_protected_meta( $meta_key, 'post' ) ) {
290
			$caps[] = $cap;
291
		}
292
		break;
293
	case 'edit_comment':
294
		$comment = get_comment( $args[0] );
295
		if ( ! $comment ) {
296
			$caps[] = 'do_not_allow';
297
			break;
298
		}
299
300
		$post = get_post( $comment->comment_post_ID );
301
302
		/*
303
		 * If the post doesn't exist, we have an orphaned comment.
304
		 * Fall back to the edit_posts capability, instead.
305
		 */
306
		if ( $post ) {
307
			$caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
308
		} else {
309
			$caps = map_meta_cap( 'edit_posts', $user_id );
310
		}
311
		break;
312 View Code Duplication
	case 'unfiltered_upload':
313
		if ( defined('ALLOW_UNFILTERED_UPLOADS') && ALLOW_UNFILTERED_UPLOADS && ( !is_multisite() || is_super_admin( $user_id ) )  )
314
			$caps[] = $cap;
315
		else
316
			$caps[] = 'do_not_allow';
317
		break;
318 View Code Duplication
	case 'unfiltered_html' :
319
		// Disallow unfiltered_html for all users, even admins and super admins.
320
		if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML )
321
			$caps[] = 'do_not_allow';
322
		elseif ( is_multisite() && ! is_super_admin( $user_id ) )
323
			$caps[] = 'do_not_allow';
324
		else
325
			$caps[] = $cap;
326
		break;
327
	case 'edit_files':
328
	case 'edit_plugins':
329
	case 'edit_themes':
330
		// Disallow the file editors.
331
		if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT )
332
			$caps[] = 'do_not_allow';
333
		elseif ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
334
			$caps[] = 'do_not_allow';
335
		elseif ( is_multisite() && ! is_super_admin( $user_id ) )
336
			$caps[] = 'do_not_allow';
337
		else
338
			$caps[] = $cap;
339
		break;
340
	case 'update_plugins':
341
	case 'delete_plugins':
342
	case 'install_plugins':
343
	case 'upload_plugins':
344
	case 'update_themes':
345
	case 'delete_themes':
346
	case 'install_themes':
347
	case 'upload_themes':
348
	case 'update_core':
349
		// Disallow anything that creates, deletes, or updates core, plugin, or theme files.
350
		// Files in uploads are excepted.
351
		if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) {
352
			$caps[] = 'do_not_allow';
353
		} elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
354
			$caps[] = 'do_not_allow';
355
		} elseif ( 'upload_themes' === $cap ) {
356
			$caps[] = 'install_themes';
357
		} elseif ( 'upload_plugins' === $cap ) {
358
			$caps[] = 'install_plugins';
359
		} else {
360
			$caps[] = $cap;
361
		}
362
		break;
363
	case 'activate_plugins':
364
		$caps[] = $cap;
365
		if ( is_multisite() ) {
366
			// update_, install_, and delete_ are handled above with is_super_admin().
367
			$menu_perms = get_site_option( 'menu_items', array() );
368
			if ( empty( $menu_perms['plugins'] ) )
369
				$caps[] = 'manage_network_plugins';
370
		}
371
		break;
372
	case 'delete_user':
373
	case 'delete_users':
374
		// If multisite only super admins can delete users.
375
		if ( is_multisite() && ! is_super_admin( $user_id ) )
376
			$caps[] = 'do_not_allow';
377
		else
378
			$caps[] = 'delete_users'; // delete_user maps to delete_users.
379
		break;
380
	case 'create_users':
381
		if ( !is_multisite() )
382
			$caps[] = $cap;
383
		elseif ( is_super_admin( $user_id ) || get_site_option( 'add_new_users' ) )
384
			$caps[] = $cap;
385
		else
386
			$caps[] = 'do_not_allow';
387
		break;
388
	case 'manage_links' :
389
		if ( get_option( 'link_manager_enabled' ) )
390
			$caps[] = $cap;
391
		else
392
			$caps[] = 'do_not_allow';
393
		break;
394
	case 'customize' :
395
		$caps[] = 'edit_theme_options';
396
		break;
397
	case 'delete_site':
398
		$caps[] = 'manage_options';
399
		break;
400
	default:
401
		// Handle meta capabilities for custom post types.
402
		global $post_type_meta_caps;
403
		if ( isset( $post_type_meta_caps[ $cap ] ) ) {
404
			$args = array_merge( array( $post_type_meta_caps[ $cap ], $user_id ), $args );
405
			return call_user_func_array( 'map_meta_cap', $args );
406
		}
407
408
		// If no meta caps match, return the original cap.
409
		$caps[] = $cap;
410
	}
411
412
	/**
413
	 * Filters a user's capabilities depending on specific context and/or privilege.
414
	 *
415
	 * @since 2.8.0
416
	 *
417
	 * @param array  $caps    Returns the user's actual capabilities.
418
	 * @param string $cap     Capability name.
419
	 * @param int    $user_id The user ID.
420
	 * @param array  $args    Adds the context to the cap. Typically the object ID.
421
	 */
422
	return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );
423
}
424
425
/**
426
 * Whether the current user has a specific capability.
427
 *
428
 * While checking against particular roles in place of a capability is supported
429
 * in part, this practice is discouraged as it may produce unreliable results.
430
 *
431
 * Note: Will always return true if the current user is a super admin, unless specifically denied.
432
 *
433
 * @since 2.0.0
434
 *
435
 * @see WP_User::has_cap()
436
 * @see map_meta_cap()
437
 *
438
 * @param string $capability Capability name.
439
 * @param int    $object_id  Optional. ID of the specific object to check against if `$capability` is a "meta" cap.
0 ignored issues
show
There is no parameter named $object_id. Was it maybe removed?

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

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

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

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

Loading history...
440
 *                           "Meta" capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used
441
 *                           by map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
442
 *                           'edit_others_posts', etc. Accessed via func_get_args() and passed to WP_User::has_cap(),
443
 *                           then map_meta_cap().
444
 * @return bool Whether the current user has the given capability. If `$capability` is a meta cap and `$object_id` is
445
 *              passed, whether the current user has the given meta capability for the given object.
446
 */
447
function current_user_can( $capability ) {
448
	$current_user = wp_get_current_user();
449
450
	if ( empty( $current_user ) )
451
		return false;
452
453
	$args = array_slice( func_get_args(), 1 );
454
	$args = array_merge( array( $capability ), $args );
455
456
	return call_user_func_array( array( $current_user, 'has_cap' ), $args );
457
}
458
459
/**
460
 * Whether current user has a capability or role for a given site.
461
 *
462
 * @since 3.0.0
463
 *
464
 * @param int    $blog_id    Site ID.
465
 * @param string $capability Capability or role name.
466
 * @return bool
467
 */
468
function current_user_can_for_blog( $blog_id, $capability ) {
469
	$switched = is_multisite() ? switch_to_blog( $blog_id ) : false;
470
471
	$current_user = wp_get_current_user();
472
473
	if ( empty( $current_user ) ) {
474
		if ( $switched ) {
475
			restore_current_blog();
476
		}
477
		return false;
478
	}
479
480
	$args = array_slice( func_get_args(), 2 );
481
	$args = array_merge( array( $capability ), $args );
482
483
	$can = call_user_func_array( array( $current_user, 'has_cap' ), $args );
484
485
	if ( $switched ) {
486
		restore_current_blog();
487
	}
488
489
	return $can;
490
}
491
492
/**
493
 * Whether author of supplied post has capability or role.
494
 *
495
 * @since 2.9.0
496
 *
497
 * @param int|object $post Post ID or post object.
498
 * @param string $capability Capability or role name.
499
 * @return bool
500
 */
501 View Code Duplication
function author_can( $post, $capability ) {
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...
502
	if ( !$post = get_post($post) )
0 ignored issues
show
It seems like $post defined by get_post($post) on line 502 can also be of type object; however, get_post() 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...
503
		return false;
504
505
	$author = get_userdata( $post->post_author );
506
507
	if ( ! $author )
508
		return false;
509
510
	$args = array_slice( func_get_args(), 2 );
511
	$args = array_merge( array( $capability ), $args );
512
513
	return call_user_func_array( array( $author, 'has_cap' ), $args );
514
}
515
516
/**
517
 * Whether a particular user has capability or role.
518
 *
519
 * @since 3.1.0
520
 *
521
 * @param int|object $user User ID or object.
522
 * @param string $capability Capability or role name.
523
 * @return bool
524
 */
525 View Code Duplication
function user_can( $user, $capability ) {
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...
526
	if ( ! is_object( $user ) )
527
		$user = get_userdata( $user );
528
529
	if ( ! $user || ! $user->exists() )
530
		return false;
531
532
	$args = array_slice( func_get_args(), 2 );
533
	$args = array_merge( array( $capability ), $args );
534
535
	return call_user_func_array( array( $user, 'has_cap' ), $args );
536
}
537
538
/**
539
 * Retrieves the global WP_Roles instance and instantiates it if necessary.
540
 *
541
 * @since 4.3.0
542
 *
543
 * @global WP_Roles $wp_roles WP_Roles global instance.
544
 *
545
 * @return WP_Roles WP_Roles global instance if not already instantiated.
546
 */
547
function wp_roles() {
548
	global $wp_roles;
549
550
	if ( ! isset( $wp_roles ) ) {
551
		$wp_roles = new WP_Roles();
552
	}
553
	return $wp_roles;
554
}
555
556
/**
557
 * Retrieve role object.
558
 *
559
 * @since 2.0.0
560
 *
561
 * @param string $role Role name.
562
 * @return WP_Role|null WP_Role object if found, null if the role does not exist.
563
 */
564
function get_role( $role ) {
565
	return wp_roles()->get_role( $role );
566
}
567
568
/**
569
 * Add role, if it does not exist.
570
 *
571
 * @since 2.0.0
572
 *
573
 * @param string $role Role name.
574
 * @param string $display_name Display name for role.
575
 * @param array $capabilities List of capabilities, e.g. array( 'edit_posts' => true, 'delete_posts' => false );
576
 * @return WP_Role|null WP_Role object if role is added, null if already exists.
577
 */
578
function add_role( $role, $display_name, $capabilities = array() ) {
579
	if ( empty( $role ) ) {
580
		return;
581
	}
582
	return wp_roles()->add_role( $role, $display_name, $capabilities );
583
}
584
585
/**
586
 * Remove role, if it exists.
587
 *
588
 * @since 2.0.0
589
 *
590
 * @param string $role Role name.
591
 */
592
function remove_role( $role ) {
593
	wp_roles()->remove_role( $role );
594
}
595
596
/**
597
 * Retrieve a list of super admins.
598
 *
599
 * @since 3.0.0
600
 *
601
 * @global array $super_admins
602
 *
603
 * @return array List of super admin logins
604
 */
605
function get_super_admins() {
606
	global $super_admins;
607
608
	if ( isset($super_admins) )
609
		return $super_admins;
610
	else
611
		return get_site_option( 'site_admins', array('admin') );
612
}
613
614
/**
615
 * Determine if user is a site admin.
616
 *
617
 * @since 3.0.0
618
 *
619
 * @param int $user_id (Optional) The ID of a user. Defaults to the current user.
620
 * @return bool True if the user is a site admin.
621
 */
622
function is_super_admin( $user_id = false ) {
623
	if ( ! $user_id || $user_id == get_current_user_id() )
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_id of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
624
		$user = wp_get_current_user();
625
	else
626
		$user = get_userdata( $user_id );
627
628
	if ( ! $user || ! $user->exists() )
629
		return false;
630
631
	if ( is_multisite() ) {
632
		$super_admins = get_super_admins();
633
		if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) )
634
			return true;
635
	} else {
636
		if ( $user->has_cap('delete_users') )
637
			return true;
638
	}
639
640
	return false;
641
}
642
643
/**
644
 * Grants Super Admin privileges.
645
 *
646
 * @since 3.0.0
647
 *
648
 * @global array $super_admins
649
 *
650
 * @param int $user_id ID of the user to be granted Super Admin privileges.
651
 * @return bool True on success, false on failure. This can fail when the user is
652
 *              already a super admin or when the `$super_admins` global is defined.
653
 */
654
function grant_super_admin( $user_id ) {
655
	// If global super_admins override is defined, there is nothing to do here.
656
	if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) {
657
		return false;
658
	}
659
660
	/**
661
	 * Fires before the user is granted Super Admin privileges.
662
	 *
663
	 * @since 3.0.0
664
	 *
665
	 * @param int $user_id ID of the user that is about to be granted Super Admin privileges.
666
	 */
667
	do_action( 'grant_super_admin', $user_id );
668
669
	// Directly fetch site_admins instead of using get_super_admins()
670
	$super_admins = get_site_option( 'site_admins', array( 'admin' ) );
671
672
	$user = get_userdata( $user_id );
673 View Code Duplication
	if ( $user && ! in_array( $user->user_login, $super_admins ) ) {
674
		$super_admins[] = $user->user_login;
675
		update_site_option( 'site_admins' , $super_admins );
676
677
		/**
678
		 * Fires after the user is granted Super Admin privileges.
679
		 *
680
		 * @since 3.0.0
681
		 *
682
		 * @param int $user_id ID of the user that was granted Super Admin privileges.
683
		 */
684
		do_action( 'granted_super_admin', $user_id );
685
		return true;
686
	}
687
	return false;
688
}
689
690
/**
691
 * Revokes Super Admin privileges.
692
 *
693
 * @since 3.0.0
694
 *
695
 * @global array $super_admins
696
 *
697
 * @param int $user_id ID of the user Super Admin privileges to be revoked from.
698
 * @return bool True on success, false on failure. This can fail when the user's email
699
 *              is the network admin email or when the `$super_admins` global is defined.
700
 */
701
function revoke_super_admin( $user_id ) {
702
	// If global super_admins override is defined, there is nothing to do here.
703
	if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) {
704
		return false;
705
	}
706
707
	/**
708
	 * Fires before the user's Super Admin privileges are revoked.
709
	 *
710
	 * @since 3.0.0
711
	 *
712
	 * @param int $user_id ID of the user Super Admin privileges are being revoked from.
713
	 */
714
	do_action( 'revoke_super_admin', $user_id );
715
716
	// Directly fetch site_admins instead of using get_super_admins()
717
	$super_admins = get_site_option( 'site_admins', array( 'admin' ) );
718
719
	$user = get_userdata( $user_id );
720
	if ( $user && 0 !== strcasecmp( $user->user_email, get_site_option( 'admin_email' ) ) ) {
721 View Code Duplication
		if ( false !== ( $key = array_search( $user->user_login, $super_admins ) ) ) {
722
			unset( $super_admins[$key] );
723
			update_site_option( 'site_admins', $super_admins );
724
725
			/**
726
			 * Fires after the user's Super Admin privileges are revoked.
727
			 *
728
			 * @since 3.0.0
729
			 *
730
			 * @param int $user_id ID of the user Super Admin privileges were revoked from.
731
			 */
732
			do_action( 'revoked_super_admin', $user_id );
733
			return true;
734
		}
735
	}
736
	return false;
737
}
738