Issues (4967)

Security Analysis    not enabled

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

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

src/wp-includes/user.php (13 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 API
4
 *
5
 * @package WordPress
6
 * @subpackage Users
7
 */
8
9
/**
10
 * Authenticates and logs a user in with 'remember' capability.
11
 *
12
 * The credentials is an array that has 'user_login', 'user_password', and
13
 * 'remember' indices. If the credentials is not given, then the log in form
14
 * will be assumed and used if set.
15
 *
16
 * The various authentication cookies will be set by this function and will be
17
 * set for a longer period depending on if the 'remember' credential is set to
18
 * true.
19
 *
20
 * @since 2.5.0
21
 *
22
 * @global string $auth_secure_cookie
23
 *
24
 * @param array       $credentials   Optional. User info in order to sign on.
25
 * @param string|bool $secure_cookie Optional. Whether to use secure cookie.
26
 * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
27
 */
28
function wp_signon( $credentials = array(), $secure_cookie = '' ) {
29
	if ( empty($credentials) ) {
30
		$credentials = array(); // Back-compat for plugins passing an empty string.
31
32
		if ( ! empty($_POST['log']) )
33
			$credentials['user_login'] = $_POST['log'];
34
		if ( ! empty($_POST['pwd']) )
35
			$credentials['user_password'] = $_POST['pwd'];
36
		if ( ! empty($_POST['rememberme']) )
37
			$credentials['remember'] = $_POST['rememberme'];
38
	}
39
40
	if ( !empty($credentials['remember']) )
41
		$credentials['remember'] = true;
42
	else
43
		$credentials['remember'] = false;
44
45
	/**
46
	 * Fires before the user is authenticated.
47
	 *
48
	 * The variables passed to the callbacks are passed by reference,
49
	 * and can be modified by callback functions.
50
	 *
51
	 * @since 1.5.1
52
	 *
53
	 * @todo Decide whether to deprecate the wp_authenticate action.
54
	 *
55
	 * @param string $user_login    Username, passed by reference.
56
	 * @param string $user_password User password, passed by reference.
57
	 */
58
	do_action_ref_array( 'wp_authenticate', array( &$credentials['user_login'], &$credentials['user_password'] ) );
59
60
	if ( '' === $secure_cookie )
61
		$secure_cookie = is_ssl();
62
63
	/**
64
	 * Filters whether to use a secure sign-on cookie.
65
	 *
66
	 * @since 3.1.0
67
	 *
68
	 * @param bool  $secure_cookie Whether to use a secure sign-on cookie.
69
	 * @param array $credentials {
70
 	 *     Array of entered sign-on data.
71
 	 *
72
 	 *     @type string $user_login    Username.
73
 	 *     @type string $user_password Password entered.
74
	 *     @type bool   $remember      Whether to 'remember' the user. Increases the time
75
	 *                                 that the cookie will be kept. Default false.
76
 	 * }
77
	 */
78
	$secure_cookie = apply_filters( 'secure_signon_cookie', $secure_cookie, $credentials );
79
80
	global $auth_secure_cookie; // XXX ugly hack to pass this to wp_authenticate_cookie
81
	$auth_secure_cookie = $secure_cookie;
82
83
	add_filter('authenticate', 'wp_authenticate_cookie', 30, 3);
84
85
	$user = wp_authenticate($credentials['user_login'], $credentials['user_password']);
86
87
	if ( is_wp_error($user) ) {
88
		if ( $user->get_error_codes() == array('empty_username', 'empty_password') ) {
0 ignored issues
show
The method get_error_codes does only exist in WP_Error, but not in WP_User.

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...
89
			$user = new WP_Error('', '');
90
		}
91
92
		return $user;
93
	}
94
95
	wp_set_auth_cookie($user->ID, $credentials['remember'], $secure_cookie);
96
	/**
97
	 * Fires after the user has successfully logged in.
98
	 *
99
	 * @since 1.5.0
100
	 *
101
	 * @param string  $user_login Username.
102
	 * @param WP_User $user       WP_User object of the logged-in user.
103
	 */
104
	do_action( 'wp_login', $user->user_login, $user );
105
	return $user;
106
}
107
108
/**
109
 * Authenticate a user, confirming the username and password are valid.
110
 *
111
 * @since 2.8.0
112
 *
113
 * @param WP_User|WP_Error|null $user     WP_User or WP_Error object from a previous callback. Default null.
114
 * @param string                $username Username for authentication.
115
 * @param string                $password Password for authentication.
116
 * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
117
 */
118 View Code Duplication
function wp_authenticate_username_password($user, $username, $password) {
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...
119
	if ( $user instanceof WP_User ) {
120
		return $user;
121
	}
122
123
	if ( empty($username) || empty($password) ) {
124
		if ( is_wp_error( $user ) )
125
			return $user;
126
127
		$error = new WP_Error();
128
129
		if ( empty($username) )
130
			$error->add('empty_username', __('<strong>ERROR</strong>: The username field is empty.'));
131
132
		if ( empty($password) )
133
			$error->add('empty_password', __('<strong>ERROR</strong>: The password field is empty.'));
134
135
		return $error;
136
	}
137
138
	$user = get_user_by('login', $username);
139
140
	if ( !$user ) {
141
		return new WP_Error( 'invalid_username',
142
			__( '<strong>ERROR</strong>: Invalid username.' ) .
143
			' <a href="' . wp_lostpassword_url() . '">' .
144
			__( 'Lost your password?' ) .
145
			'</a>'
146
		);
147
	}
148
149
	/**
150
	 * Filters whether the given user can be authenticated with the provided $password.
151
	 *
152
	 * @since 2.5.0
153
	 *
154
	 * @param WP_User|WP_Error $user     WP_User or WP_Error object if a previous
155
	 *                                   callback failed authentication.
156
	 * @param string           $password Password to check against the user.
157
	 */
158
	$user = apply_filters( 'wp_authenticate_user', $user, $password );
159
	if ( is_wp_error($user) )
160
		return $user;
161
162
	if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
163
		return new WP_Error( 'incorrect_password',
164
			sprintf(
165
				/* translators: %s: user name */
166
				__( '<strong>ERROR</strong>: The password you entered for the username %s is incorrect.' ),
167
				'<strong>' . $username . '</strong>'
168
			) .
169
			' <a href="' . wp_lostpassword_url() . '">' .
170
			__( 'Lost your password?' ) .
171
			'</a>'
172
		);
173
	}
174
175
	return $user;
176
}
177
178
/**
179
 * Authenticates a user using the email and password.
180
 *
181
 * @since 4.5.0
182
 *
183
 * @param WP_User|WP_Error|null $user     WP_User or WP_Error object if a previous
184
 *                                        callback failed authentication.
185
 * @param string                $email    Email address for authentication.
186
 * @param string                $password Password for authentication.
187
 * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
188
 */
189 View Code Duplication
function wp_authenticate_email_password( $user, $email, $password ) {
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...
190
	if ( $user instanceof WP_User ) {
191
		return $user;
192
	}
193
194
	if ( empty( $email ) || empty( $password ) ) {
195
		if ( is_wp_error( $user ) ) {
196
			return $user;
197
		}
198
199
		$error = new WP_Error();
200
201
		if ( empty( $email ) ) {
202
			$error->add( 'empty_username', __( '<strong>ERROR</strong>: The email field is empty.' ) ); // Uses 'empty_username' for back-compat with wp_signon()
203
		}
204
205
		if ( empty( $password ) ) {
206
			$error->add( 'empty_password', __( '<strong>ERROR</strong>: The password field is empty.' ) );
207
		}
208
209
		return $error;
210
	}
211
212
	if ( ! is_email( $email ) ) {
213
		return $user;
214
	}
215
216
	$user = get_user_by( 'email', $email );
217
218
	if ( ! $user ) {
219
		return new WP_Error( 'invalid_email',
220
			__( '<strong>ERROR</strong>: Invalid email address.' ) .
221
			' <a href="' . wp_lostpassword_url() . '">' .
222
			__( 'Lost your password?' ) .
223
			'</a>'
224
		);
225
	}
226
227
	/** This filter is documented in wp-includes/user.php */
228
	$user = apply_filters( 'wp_authenticate_user', $user, $password );
229
230
	if ( is_wp_error( $user ) ) {
231
		return $user;
232
	}
233
234
	if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
235
		return new WP_Error( 'incorrect_password',
236
			sprintf(
237
				/* translators: %s: email address */
238
				__( '<strong>ERROR</strong>: The password you entered for the email address %s is incorrect.' ),
239
				'<strong>' . $email . '</strong>'
240
			) .
241
			' <a href="' . wp_lostpassword_url() . '">' .
242
			__( 'Lost your password?' ) .
243
			'</a>'
244
		);
245
	}
246
247
	return $user;
248
}
249
250
/**
251
 * Authenticate the user using the WordPress auth cookie.
252
 *
253
 * @since 2.8.0
254
 *
255
 * @global string $auth_secure_cookie
256
 *
257
 * @param WP_User|WP_Error|null $user     WP_User or WP_Error object from a previous callback. Default null.
258
 * @param string                $username Username. If not empty, cancels the cookie authentication.
259
 * @param string                $password Password. If not empty, cancels the cookie authentication.
260
 * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
261
 */
262
function wp_authenticate_cookie($user, $username, $password) {
263
	if ( $user instanceof WP_User ) {
264
		return $user;
265
	}
266
267
	if ( empty($username) && empty($password) ) {
268
		$user_id = wp_validate_auth_cookie();
269
		if ( $user_id )
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_id of type false|integer is loosely compared to true; 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...
270
			return new WP_User($user_id);
271
272
		global $auth_secure_cookie;
273
274
		if ( $auth_secure_cookie )
275
			$auth_cookie = SECURE_AUTH_COOKIE;
276
		else
277
			$auth_cookie = AUTH_COOKIE;
278
279
		if ( !empty($_COOKIE[$auth_cookie]) )
280
			return new WP_Error('expired_session', __('Please log in again.'));
281
282
		// If the cookie is not set, be silent.
283
	}
284
285
	return $user;
286
}
287
288
/**
289
 * For Multisite blogs, check if the authenticated user has been marked as a
290
 * spammer, or if the user's primary blog has been marked as spam.
291
 *
292
 * @since 3.7.0
293
 *
294
 * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
295
 * @return WP_User|WP_Error WP_User on success, WP_Error if the user is considered a spammer.
296
 */
297
function wp_authenticate_spam_check( $user ) {
298
	if ( $user instanceof WP_User && is_multisite() ) {
299
		/**
300
		 * Filters whether the user has been marked as a spammer.
301
		 *
302
		 * @since 3.7.0
303
		 *
304
		 * @param bool    $spammed Whether the user is considered a spammer.
305
		 * @param WP_User $user    User to check against.
306
		 */
307
		$spammed = apply_filters( 'check_is_user_spammed', is_user_spammy( $user ), $user );
308
309
		if ( $spammed )
310
			return new WP_Error( 'spammer_account', __( '<strong>ERROR</strong>: Your account has been marked as a spammer.' ) );
311
	}
312
	return $user;
313
}
314
315
/**
316
 * Validates the logged-in cookie.
317
 *
318
 * Checks the logged-in cookie if the previous auth cookie could not be
319
 * validated and parsed.
320
 *
321
 * This is a callback for the {@see 'determine_current_user'} filter, rather than API.
322
 *
323
 * @since 3.9.0
324
 *
325
 * @param int|bool $user_id The user ID (or false) as received from the
326
 *                       determine_current_user filter.
327
 * @return int|false User ID if validated, false otherwise. If a user ID from
0 ignored issues
show
Should the return type not be integer|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
328
 *                   an earlier filter callback is received, that value is returned.
329
 */
330
function wp_validate_logged_in_cookie( $user_id ) {
331
	if ( $user_id ) {
332
		return $user_id;
333
	}
334
335
	if ( is_blog_admin() || is_network_admin() || empty( $_COOKIE[LOGGED_IN_COOKIE] ) ) {
336
		return false;
337
	}
338
339
	return wp_validate_auth_cookie( $_COOKIE[LOGGED_IN_COOKIE], 'logged_in' );
340
}
341
342
/**
343
 * Number of posts user has written.
344
 *
345
 * @since 3.0.0
346
 * @since 4.1.0 Added `$post_type` argument.
347
 * @since 4.3.0 Added `$public_only` argument. Added the ability to pass an array
348
 *              of post types to `$post_type`.
349
 *
350
 * @global wpdb $wpdb WordPress database abstraction object.
351
 *
352
 * @param int          $userid      User ID.
353
 * @param array|string $post_type   Optional. Single post type or array of post types to count the number of posts for. Default 'post'.
354
 * @param bool         $public_only Optional. Whether to only return counts for public posts. Default false.
355
 * @return string Number of posts the user has written in this post type.
356
 */
357
function count_user_posts( $userid, $post_type = 'post', $public_only = false ) {
358
	global $wpdb;
359
360
	$where = get_posts_by_author_sql( $post_type, true, $userid, $public_only );
361
362
	$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
363
364
	/**
365
	 * Filters the number of posts a user has written.
366
	 *
367
	 * @since 2.7.0
368
	 * @since 4.1.0 Added `$post_type` argument.
369
	 * @since 4.3.1 Added `$public_only` argument.
370
	 *
371
	 * @param int          $count       The user's post count.
372
	 * @param int          $userid      User ID.
373
	 * @param string|array $post_type   Single post type or array of post types to count the number of posts for.
374
	 * @param bool         $public_only Whether to limit counted posts to public posts.
375
	 */
376
	return apply_filters( 'get_usernumposts', $count, $userid, $post_type, $public_only );
377
}
378
379
/**
380
 * Number of posts written by a list of users.
381
 *
382
 * @since 3.0.0
383
 *
384
 * @global wpdb $wpdb WordPress database abstraction object.
385
 *
386
 * @param array        $users       Array of user IDs.
387
 * @param string|array $post_type   Optional. Single post type or array of post types to check. Defaults to 'post'.
388
 * @param bool         $public_only Optional. Only return counts for public posts.  Defaults to false.
389
 * @return array Amount of posts each user has written.
390
 */
391
function count_many_users_posts( $users, $post_type = 'post', $public_only = false ) {
392
	global $wpdb;
393
394
	$count = array();
395
	if ( empty( $users ) || ! is_array( $users ) )
396
		return $count;
397
398
	$userlist = implode( ',', array_map( 'absint', $users ) );
399
	$where = get_posts_by_author_sql( $post_type, true, null, $public_only );
400
401
	$result = $wpdb->get_results( "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author", ARRAY_N );
402
	foreach ( $result as $row ) {
403
		$count[ $row[0] ] = $row[1];
404
	}
405
406
	foreach ( $users as $id ) {
407
		if ( ! isset( $count[ $id ] ) )
408
			$count[ $id ] = 0;
409
	}
410
411
	return $count;
412
}
413
414
//
415
// User option functions
416
//
417
418
/**
419
 * Get the current user's ID
420
 *
421
 * @since MU
422
 *
423
 * @return int The current user's ID, or 0 if no user is logged in.
424
 */
425
function get_current_user_id() {
426
	if ( ! function_exists( 'wp_get_current_user' ) )
427
		return 0;
428
	$user = wp_get_current_user();
429
	return ( isset( $user->ID ) ? (int) $user->ID : 0 );
430
}
431
432
/**
433
 * Retrieve user option that can be either per Site or per Network.
434
 *
435
 * If the user ID is not given, then the current user will be used instead. If
436
 * the user ID is given, then the user data will be retrieved. The filter for
437
 * the result, will also pass the original option name and finally the user data
438
 * object as the third parameter.
439
 *
440
 * The option will first check for the per site name and then the per Network name.
441
 *
442
 * @since 2.0.0
443
 *
444
 * @global wpdb $wpdb WordPress database abstraction object.
445
 *
446
 * @param string $option     User option name.
447
 * @param int    $user       Optional. User ID.
448
 * @param string $deprecated Use get_option() to check for an option in the options table.
449
 * @return mixed User option value on success, false on failure.
450
 */
451
function get_user_option( $option, $user = 0, $deprecated = '' ) {
452
	global $wpdb;
453
454
	if ( !empty( $deprecated ) )
455
		_deprecated_argument( __FUNCTION__, '3.0.0' );
456
457
	if ( empty( $user ) )
458
		$user = get_current_user_id();
459
460
	if ( ! $user = get_userdata( $user ) )
461
		return false;
462
463
	$prefix = $wpdb->get_blog_prefix();
464
	if ( $user->has_prop( $prefix . $option ) ) // Blog specific
465
		$result = $user->get( $prefix . $option );
466
	elseif ( $user->has_prop( $option ) ) // User specific and cross-blog
467
		$result = $user->get( $option );
468
	else
469
		$result = false;
470
471
	/**
472
	 * Filters a specific user option value.
473
	 *
474
	 * The dynamic portion of the hook name, `$option`, refers to the user option name.
475
	 *
476
	 * @since 2.5.0
477
	 *
478
	 * @param mixed   $result Value for the user's option.
479
	 * @param string  $option Name of the option being retrieved.
480
	 * @param WP_User $user   WP_User object of the user whose option is being retrieved.
481
	 */
482
	return apply_filters( "get_user_option_{$option}", $result, $option, $user );
483
}
484
485
/**
486
 * Update user option with global blog capability.
487
 *
488
 * User options are just like user metadata except that they have support for
489
 * global blog options. If the 'global' parameter is false, which it is by default
490
 * it will prepend the WordPress table prefix to the option name.
491
 *
492
 * Deletes the user option if $newvalue is empty.
493
 *
494
 * @since 2.0.0
495
 *
496
 * @global wpdb $wpdb WordPress database abstraction object.
497
 *
498
 * @param int    $user_id     User ID.
499
 * @param string $option_name User option name.
500
 * @param mixed  $newvalue    User option value.
501
 * @param bool   $global      Optional. Whether option name is global or blog specific.
502
 *                            Default false (blog specific).
503
 * @return int|bool User meta ID if the option didn't exist, true on successful update,
504
 *                  false on failure.
505
 */
506
function update_user_option( $user_id, $option_name, $newvalue, $global = false ) {
507
	global $wpdb;
508
509
	if ( !$global )
510
		$option_name = $wpdb->get_blog_prefix() . $option_name;
511
512
	return update_user_meta( $user_id, $option_name, $newvalue );
513
}
514
515
/**
516
 * Delete user option with global blog capability.
517
 *
518
 * User options are just like user metadata except that they have support for
519
 * global blog options. If the 'global' parameter is false, which it is by default
520
 * it will prepend the WordPress table prefix to the option name.
521
 *
522
 * @since 3.0.0
523
 *
524
 * @global wpdb $wpdb WordPress database abstraction object.
525
 *
526
 * @param int    $user_id     User ID
527
 * @param string $option_name User option name.
528
 * @param bool   $global      Optional. Whether option name is global or blog specific.
529
 *                            Default false (blog specific).
530
 * @return bool True on success, false on failure.
531
 */
532
function delete_user_option( $user_id, $option_name, $global = false ) {
533
	global $wpdb;
534
535
	if ( !$global )
536
		$option_name = $wpdb->get_blog_prefix() . $option_name;
537
	return delete_user_meta( $user_id, $option_name );
538
}
539
540
/**
541
 * Retrieve list of users matching criteria.
542
 *
543
 * @since 3.1.0
544
 *
545
 * @see WP_User_Query
546
 *
547
 * @param array $args Optional. Arguments to retrieve users. See WP_User_Query::prepare_query().
548
 *                    for more information on accepted arguments.
549
 * @return array List of users.
550
 */
551
function get_users( $args = array() ) {
552
553
	$args = wp_parse_args( $args );
554
	$args['count_total'] = false;
555
556
	$user_search = new WP_User_Query($args);
557
558
	return (array) $user_search->get_results();
559
}
560
561
/**
562
 * Get the sites a user belongs to.
563
 *
564
 * @since 3.0.0
565
 * @since 4.7.0 Converted to use get_sites().
566
 *
567
 * @global wpdb $wpdb WordPress database abstraction object.
568
 *
569
 * @param int  $user_id User ID
570
 * @param bool $all     Whether to retrieve all sites, or only sites that are not
571
 *                      marked as deleted, archived, or spam.
572
 * @return array A list of the user's sites. An empty array if the user doesn't exist
573
 *               or belongs to no sites.
574
 */
575
function get_blogs_of_user( $user_id, $all = false ) {
576
	global $wpdb;
577
578
	$user_id = (int) $user_id;
579
580
	// Logged out users can't have sites
581
	if ( empty( $user_id ) )
582
		return array();
583
584
	/**
585
	 * Filters the list of a user's sites before it is populated.
586
	 *
587
	 * Passing a non-null value to the filter will effectively short circuit
588
	 * get_blogs_of_user(), returning that value instead.
589
	 *
590
	 * @since 4.6.0
591
	 *
592
	 * @param null|array $sites   An array of site objects of which the user is a member.
593
	 * @param int        $user_id User ID.
594
	 * @param bool       $all     Whether the returned array should contain all sites, including
595
	 *                            those marked 'deleted', 'archived', or 'spam'. Default false.
596
	 */
597
	$sites = apply_filters( 'pre_get_blogs_of_user', null, $user_id, $all );
598
599
	if ( null !== $sites ) {
600
		return $sites;
601
	}
602
603
	$keys = get_user_meta( $user_id );
604
	if ( empty( $keys ) )
605
		return array();
606
607
	if ( ! is_multisite() ) {
608
		$site_id = get_current_blog_id();
609
		$sites = array( $site_id => new stdClass );
610
		$sites[ $site_id ]->userblog_id = $site_id;
611
		$sites[ $site_id ]->blogname = get_option('blogname');
612
		$sites[ $site_id ]->domain = '';
613
		$sites[ $site_id ]->path = '';
614
		$sites[ $site_id ]->site_id = 1;
615
		$sites[ $site_id ]->siteurl = get_option('siteurl');
616
		$sites[ $site_id ]->archived = 0;
617
		$sites[ $site_id ]->spam = 0;
618
		$sites[ $site_id ]->deleted = 0;
619
		return $sites;
620
	}
621
622
	$site_ids = array();
623
624
	if ( isset( $keys[ $wpdb->base_prefix . 'capabilities' ] ) && defined( 'MULTISITE' ) ) {
625
		$site_ids[] = 1;
626
		unset( $keys[ $wpdb->base_prefix . 'capabilities' ] );
627
	}
628
629
	$keys = array_keys( $keys );
630
631
	foreach ( $keys as $key ) {
632
		if ( 'capabilities' !== substr( $key, -12 ) )
633
			continue;
634
		if ( $wpdb->base_prefix && 0 !== strpos( $key, $wpdb->base_prefix ) )
635
			continue;
636
		$site_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key );
637
		if ( ! is_numeric( $site_id ) )
638
			continue;
639
640
		$site_ids[] = (int) $site_id;
641
	}
642
643
	$sites = array();
644
645
	if ( ! empty( $site_ids ) ) {
646
		$args = array(
647
			'number'   => '',
648
			'site__in' => $site_ids,
649
		);
650
		if ( ! $all ) {
651
			$args['archived'] = 0;
652
			$args['spam']     = 0;
653
			$args['deleted']  = 0;
654
		}
655
656
		$_sites = get_sites( $args );
657
658
		foreach ( $_sites as $site ) {
0 ignored issues
show
The expression $_sites of type array|integer 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...
659
			$sites[ $site->id ] = (object) array(
660
				'userblog_id' => $site->id,
661
				'blogname'    => $site->blogname,
662
				'domain'      => $site->domain,
663
				'path'        => $site->path,
664
				'site_id'     => $site->network_id,
665
				'siteurl'     => $site->siteurl,
666
				'archived'    => $site->archived,
667
				'mature'      => $site->mature,
668
				'spam'        => $site->spam,
669
				'deleted'     => $site->deleted,
670
			);
671
		}
672
	}
673
674
	/**
675
	 * Filters the list of sites a user belongs to.
676
	 *
677
	 * @since MU
678
	 *
679
	 * @param array $sites   An array of site objects belonging to the user.
680
	 * @param int   $user_id User ID.
681
	 * @param bool  $all     Whether the returned sites array should contain all sites, including
682
	 *                       those marked 'deleted', 'archived', or 'spam'. Default false.
683
	 */
684
	return apply_filters( 'get_blogs_of_user', $sites, $user_id, $all );
685
}
686
687
/**
688
 * Find out whether a user is a member of a given blog.
689
 *
690
 * @since MU 1.1
691
 *
692
 * @param int $user_id Optional. The unique ID of the user. Defaults to the current user.
693
 * @param int $blog_id Optional. ID of the blog to check. Defaults to the current site.
694
 * @return bool
695
 */
696
function is_user_member_of_blog( $user_id = 0, $blog_id = 0 ) {
697
	global $wpdb;
698
699
	$user_id = (int) $user_id;
700
	$blog_id = (int) $blog_id;
701
702
	if ( empty( $user_id ) ) {
703
		$user_id = get_current_user_id();
704
	}
705
706
	// Technically not needed, but does save calls to get_site and get_user_meta
707
	// in the event that the function is called when a user isn't logged in
708
	if ( empty( $user_id ) ) {
709
		return false;
710
	} else {
711
		$user = get_userdata( $user_id );
712
		if ( ! $user instanceof WP_User ) {
713
			return false;
714
		}
715
	}
716
717
	if ( ! is_multisite() ) {
718
		return true;
719
	}
720
721
	if ( empty( $blog_id ) ) {
722
		$blog_id = get_current_blog_id();
723
	}
724
725
	$blog = get_site( $blog_id );
726
727
	if ( ! $blog || ! isset( $blog->domain ) || $blog->archived || $blog->spam || $blog->deleted ) {
728
		return false;
729
	}
730
731
	$keys = get_user_meta( $user_id );
732
	if ( empty( $keys ) ) {
733
		return false;
734
	}
735
736
	// no underscore before capabilities in $base_capabilities_key
737
	$base_capabilities_key = $wpdb->base_prefix . 'capabilities';
738
	$site_capabilities_key = $wpdb->base_prefix . $blog_id . '_capabilities';
739
740
	if ( isset( $keys[ $base_capabilities_key ] ) && $blog_id == 1 ) {
741
		return true;
742
	}
743
744
	if ( isset( $keys[ $site_capabilities_key ] ) ) {
0 ignored issues
show
This if statement, and the following return statement can be replaced with return isset($keys[$site_capabilities_key]);.
Loading history...
745
		return true;
746
	}
747
748
	return false;
749
}
750
751
/**
752
 * Add meta data field to a user.
753
 *
754
 * Post meta data is called "Custom Fields" on the Administration Screens.
755
 *
756
 * @since 3.0.0
757
 * @link https://codex.wordpress.org/Function_Reference/add_user_meta
758
 *
759
 * @param int    $user_id    User ID.
760
 * @param string $meta_key   Metadata name.
761
 * @param mixed  $meta_value Metadata value.
762
 * @param bool   $unique     Optional, default is false. Whether the same key should not be added.
763
 * @return int|false Meta ID on success, false on failure.
764
 */
765
function add_user_meta($user_id, $meta_key, $meta_value, $unique = false) {
766
	return add_metadata('user', $user_id, $meta_key, $meta_value, $unique);
767
}
768
769
/**
770
 * Remove metadata matching criteria from a user.
771
 *
772
 * You can match based on the key, or key and value. Removing based on key and
773
 * value, will keep from removing duplicate metadata with the same key. It also
774
 * allows removing all metadata matching key, if needed.
775
 *
776
 * @since 3.0.0
777
 * @link https://codex.wordpress.org/Function_Reference/delete_user_meta
778
 *
779
 * @param int    $user_id    User ID
780
 * @param string $meta_key   Metadata name.
781
 * @param mixed  $meta_value Optional. Metadata value.
782
 * @return bool True on success, false on failure.
783
 */
784
function delete_user_meta($user_id, $meta_key, $meta_value = '') {
785
	return delete_metadata('user', $user_id, $meta_key, $meta_value);
786
}
787
788
/**
789
 * Retrieve user meta field for a user.
790
 *
791
 * @since 3.0.0
792
 * @link https://codex.wordpress.org/Function_Reference/get_user_meta
793
 *
794
 * @param int    $user_id User ID.
795
 * @param string $key     Optional. The meta key to retrieve. By default, returns data for all keys.
796
 * @param bool   $single  Whether to return a single value.
797
 * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true.
798
 */
799
function get_user_meta($user_id, $key = '', $single = false) {
800
	return get_metadata('user', $user_id, $key, $single);
801
}
802
803
/**
804
 * Update user meta field based on user ID.
805
 *
806
 * Use the $prev_value parameter to differentiate between meta fields with the
807
 * same key and user ID.
808
 *
809
 * If the meta field for the user does not exist, it will be added.
810
 *
811
 * @since 3.0.0
812
 * @link https://codex.wordpress.org/Function_Reference/update_user_meta
813
 *
814
 * @param int    $user_id    User ID.
815
 * @param string $meta_key   Metadata key.
816
 * @param mixed  $meta_value Metadata value.
817
 * @param mixed  $prev_value Optional. Previous value to check before removing.
818
 * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
819
 */
820
function update_user_meta($user_id, $meta_key, $meta_value, $prev_value = '') {
821
	return update_metadata('user', $user_id, $meta_key, $meta_value, $prev_value);
822
}
823
824
/**
825
 * Count number of users who have each of the user roles.
826
 *
827
 * Assumes there are neither duplicated nor orphaned capabilities meta_values.
828
 * Assumes role names are unique phrases. Same assumption made by WP_User_Query::prepare_query()
829
 * Using $strategy = 'time' this is CPU-intensive and should handle around 10^7 users.
830
 * Using $strategy = 'memory' this is memory-intensive and should handle around 10^5 users, but see WP Bug #12257.
831
 *
832
 * @since 3.0.0
833
 * @since 4.4.0 The number of users with no role is now included in the `none` element.
834
 *
835
 * @global wpdb $wpdb WordPress database abstraction object.
836
 *
837
 * @param string $strategy 'time' or 'memory'
838
 * @return array Includes a grand total and an array of counts indexed by role strings.
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array<string,integer|array<*,integer>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
839
 */
840
function count_users($strategy = 'time') {
841
	global $wpdb;
842
843
	// Initialize
844
	$id = get_current_blog_id();
845
	$blog_prefix = $wpdb->get_blog_prefix($id);
846
	$result = array();
847
848
	if ( 'time' == $strategy ) {
849
		$avail_roles = wp_roles()->get_names();
850
851
		// Build a CPU-intensive query that will return concise information.
852
		$select_count = array();
853
		foreach ( $avail_roles as $this_role => $name ) {
854
			$select_count[] = $wpdb->prepare( "COUNT(NULLIF(`meta_value` LIKE %s, false))", '%' . $wpdb->esc_like( '"' . $this_role . '"' ) . '%');
855
		}
856
		$select_count[] = "COUNT(NULLIF(`meta_value` = 'a:0:{}', false))";
857
		$select_count = implode(', ', $select_count);
858
859
		// Add the meta_value index to the selection list, then run the query.
860
		$row = $wpdb->get_row( "
861
			SELECT {$select_count}, COUNT(*)
862
			FROM {$wpdb->usermeta}
863
			INNER JOIN {$wpdb->users} ON user_id = ID
864
			WHERE meta_key = '{$blog_prefix}capabilities'
865
		", ARRAY_N );
866
867
		// Run the previous loop again to associate results with role names.
868
		$col = 0;
869
		$role_counts = array();
870
		foreach ( $avail_roles as $this_role => $name ) {
871
			$count = (int) $row[$col++];
872
			if ($count > 0) {
873
				$role_counts[$this_role] = $count;
874
			}
875
		}
876
877
		$role_counts['none'] = (int) $row[$col++];
878
879
		// Get the meta_value index from the end of the result set.
880
		$total_users = (int) $row[$col];
881
882
		$result['total_users'] = $total_users;
883
		$result['avail_roles'] =& $role_counts;
884
	} else {
885
		$avail_roles = array(
886
			'none' => 0,
887
		);
888
889
		$users_of_blog = $wpdb->get_col( "
890
			SELECT meta_value
891
			FROM {$wpdb->usermeta}
892
			INNER JOIN {$wpdb->users} ON user_id = ID
893
			WHERE meta_key = '{$blog_prefix}capabilities'
894
		" );
895
896
		foreach ( $users_of_blog as $caps_meta ) {
897
			$b_roles = maybe_unserialize($caps_meta);
898
			if ( ! is_array( $b_roles ) )
899
				continue;
900
			if ( empty( $b_roles ) ) {
901
				$avail_roles['none']++;
902
			}
903
			foreach ( $b_roles as $b_role => $val ) {
904
				if ( isset($avail_roles[$b_role]) ) {
905
					$avail_roles[$b_role]++;
906
				} else {
907
					$avail_roles[$b_role] = 1;
908
				}
909
			}
910
		}
911
912
		$result['total_users'] = count( $users_of_blog );
913
		$result['avail_roles'] =& $avail_roles;
914
	}
915
916
	if ( is_multisite() ) {
917
		$result['avail_roles']['none'] = 0;
918
	}
919
920
	return $result;
921
}
922
923
//
924
// Private helper functions
925
//
926
927
/**
928
 * Set up global user vars.
929
 *
930
 * Used by wp_set_current_user() for back compat. Might be deprecated in the future.
931
 *
932
 * @since 2.0.4
933
 *
934
 * @global string $user_login    The user username for logging in
935
 * @global object $userdata      User data.
936
 * @global int    $user_level    The level of the user
937
 * @global int    $user_ID       The ID of the user
938
 * @global string $user_email    The email address of the user
939
 * @global string $user_url      The url in the user's profile
940
 * @global string $user_identity The display name of the user
941
 *
942
 * @param int $for_user_id Optional. User ID to set up global data.
943
 */
944
function setup_userdata($for_user_id = '') {
945
	global $user_login, $userdata, $user_level, $user_ID, $user_email, $user_url, $user_identity;
946
947
	if ( '' == $for_user_id )
948
		$for_user_id = get_current_user_id();
949
	$user = get_userdata( $for_user_id );
950
951
	if ( ! $user ) {
952
		$user_ID = 0;
953
		$user_level = 0;
954
		$userdata = null;
955
		$user_login = $user_email = $user_url = $user_identity = '';
956
		return;
957
	}
958
959
	$user_ID    = (int) $user->ID;
960
	$user_level = (int) $user->user_level;
961
	$userdata   = $user;
962
	$user_login = $user->user_login;
963
	$user_email = $user->user_email;
964
	$user_url   = $user->user_url;
965
	$user_identity = $user->display_name;
966
}
967
968
/**
969
 * Create dropdown HTML content of users.
970
 *
971
 * The content can either be displayed, which it is by default or retrieved by
972
 * setting the 'echo' argument. The 'include' and 'exclude' arguments do not
973
 * need to be used; all users will be displayed in that case. Only one can be
974
 * used, either 'include' or 'exclude', but not both.
975
 *
976
 * The available arguments are as follows:
977
 *
978
 * @since 2.3.0
979
 * @since 4.5.0 Added the 'display_name_with_login' value for 'show'.
980
 * @since 4.7.0 Added the `$role`, `$role__in`, and `$role__not_in` parameters.
981
 *
982
 * @param array|string $args {
983
 *     Optional. Array or string of arguments to generate a drop-down of users.
984
 *     See WP_User_Query::prepare_query() for additional available arguments.
985
 *
986
 *     @type string       $show_option_all         Text to show as the drop-down default (all).
987
 *                                                 Default empty.
988
 *     @type string       $show_option_none        Text to show as the drop-down default when no
989
 *                                                 users were found. Default empty.
990
 *     @type int|string   $option_none_value       Value to use for $show_option_non when no users
991
 *                                                 were found. Default -1.
992
 *     @type string       $hide_if_only_one_author Whether to skip generating the drop-down
993
 *                                                 if only one user was found. Default empty.
994
 *     @type string       $orderby                 Field to order found users by. Accepts user fields.
995
 *                                                 Default 'display_name'.
996
 *     @type string       $order                   Whether to order users in ascending or descending
997
 *                                                 order. Accepts 'ASC' (ascending) or 'DESC' (descending).
998
 *                                                 Default 'ASC'.
999
 *     @type array|string $include                 Array or comma-separated list of user IDs to include.
1000
 *                                                 Default empty.
1001
 *     @type array|string $exclude                 Array or comma-separated list of user IDs to exclude.
1002
 *                                                 Default empty.
1003
 *     @type bool|int     $multi                   Whether to skip the ID attribute on the 'select' element.
1004
 *                                                 Accepts 1|true or 0|false. Default 0|false.
1005
 *     @type string       $show                    User data to display. If the selected item is empty
1006
 *                                                 then the 'user_login' will be displayed in parentheses.
1007
 *                                                 Accepts any user field, or 'display_name_with_login' to show
1008
 *                                                 the display name with user_login in parentheses.
1009
 *                                                 Default 'display_name'.
1010
 *     @type int|bool     $echo                    Whether to echo or return the drop-down. Accepts 1|true (echo)
1011
 *                                                 or 0|false (return). Default 1|true.
1012
 *     @type int          $selected                Which user ID should be selected. Default 0.
1013
 *     @type bool         $include_selected        Whether to always include the selected user ID in the drop-
1014
 *                                                 down. Default false.
1015
 *     @type string       $name                    Name attribute of select element. Default 'user'.
1016
 *     @type string       $id                      ID attribute of the select element. Default is the value of $name.
1017
 *     @type string       $class                   Class attribute of the select element. Default empty.
1018
 *     @type int          $blog_id                 ID of blog (Multisite only). Default is ID of the current blog.
1019
 *     @type string       $who                     Which type of users to query. Accepts only an empty string or
1020
 *                                                 'authors'. Default empty.
1021
 *     @type string|array $role                    An array or a comma-separated list of role names that users must
1022
 *                                                 match to be included in results. Note that this is an inclusive
1023
 *                                                 list: users must match *each* role. Default empty.
1024
 *     @type array        $role__in                An array of role names. Matched users must have at least one of
1025
 *                                                 these roles. Default empty array.
1026
 *     @type array        $role__not_in            An array of role names to exclude. Users matching one or more of
1027
 *                                                 these roles will not be included in results. Default empty array.
1028
 * }
1029
 * @return string String of HTML content.
1030
 */
1031
function wp_dropdown_users( $args = '' ) {
1032
	$defaults = array(
1033
		'show_option_all' => '', 'show_option_none' => '', 'hide_if_only_one_author' => '',
1034
		'orderby' => 'display_name', 'order' => 'ASC',
1035
		'include' => '', 'exclude' => '', 'multi' => 0,
1036
		'show' => 'display_name', 'echo' => 1,
1037
		'selected' => 0, 'name' => 'user', 'class' => '', 'id' => '',
1038
		'blog_id' => get_current_blog_id(), 'who' => '', 'include_selected' => false,
1039
		'option_none_value' => -1,
1040
		'role' => '',
1041
		'role__in' => array(),
1042
		'role__not_in' => array(),
1043
	);
1044
1045
	$defaults['selected'] = is_author() ? get_query_var( 'author' ) : 0;
1046
1047
	$r = wp_parse_args( $args, $defaults );
1048
1049
	$query_args = wp_array_slice_assoc( $r, array( 'blog_id', 'include', 'exclude', 'orderby', 'order', 'who', 'role', 'role__in', 'role__not_in' ) );
1050
1051
	$fields = array( 'ID', 'user_login' );
1052
1053
	$show = ! empty( $r['show'] ) ? $r['show'] : 'display_name';
1054
	if ( 'display_name_with_login' === $show ) {
1055
		$fields[] = 'display_name';
1056
	} else {
1057
		$fields[] = $show;
1058
	}
1059
1060
	$query_args['fields'] = $fields;
1061
1062
	$show_option_all = $r['show_option_all'];
1063
	$show_option_none = $r['show_option_none'];
1064
	$option_none_value = $r['option_none_value'];
1065
1066
	/**
1067
	 * Filters the query arguments for the list of users in the dropdown.
1068
	 *
1069
	 * @since 4.4.0
1070
	 *
1071
	 * @param array $query_args The query arguments for get_users().
1072
	 * @param array $r          The arguments passed to wp_dropdown_users() combined with the defaults.
1073
	 */
1074
	$query_args = apply_filters( 'wp_dropdown_users_args', $query_args, $r );
1075
1076
	$users = get_users( $query_args );
1077
1078
	$output = '';
1079
	if ( ! empty( $users ) && ( empty( $r['hide_if_only_one_author'] ) || count( $users ) > 1 ) ) {
1080
		$name = esc_attr( $r['name'] );
1081
		if ( $r['multi'] && ! $r['id'] ) {
1082
			$id = '';
1083
		} else {
1084
			$id = $r['id'] ? " id='" . esc_attr( $r['id'] ) . "'" : " id='$name'";
1085
		}
1086
		$output = "<select name='{$name}'{$id} class='" . $r['class'] . "'>\n";
1087
1088
		if ( $show_option_all ) {
1089
			$output .= "\t<option value='0'>$show_option_all</option>\n";
1090
		}
1091
1092
		if ( $show_option_none ) {
1093
			$_selected = selected( $option_none_value, $r['selected'], false );
1094
			$output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$_selected>$show_option_none</option>\n";
1095
		}
1096
1097
		if ( $r['include_selected'] && ( $r['selected'] > 0 ) ) {
1098
			$found_selected = false;
1099
			$r['selected'] = (int) $r['selected'];
1100
			foreach ( (array) $users as $user ) {
1101
				$user->ID = (int) $user->ID;
1102
				if ( $user->ID === $r['selected'] ) {
1103
					$found_selected = true;
1104
				}
1105
			}
1106
1107
			if ( ! $found_selected ) {
1108
				$users[] = get_userdata( $r['selected'] );
1109
			}
1110
		}
1111
1112
		foreach ( (array) $users as $user ) {
1113
			if ( 'display_name_with_login' === $show ) {
1114
				/* translators: 1: display name, 2: user_login */
1115
				$display = sprintf( _x( '%1$s (%2$s)', 'user dropdown' ), $user->display_name, $user->user_login );
1116
			} elseif ( ! empty( $user->$show ) ) {
1117
				$display = $user->$show;
1118
			} else {
1119
				$display = '(' . $user->user_login . ')';
1120
			}
1121
1122
			$_selected = selected( $user->ID, $r['selected'], false );
1123
			$output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n";
1124
		}
1125
1126
		$output .= "</select>";
1127
	}
1128
1129
	/**
1130
	 * Filters the wp_dropdown_users() HTML output.
1131
	 *
1132
	 * @since 2.3.0
1133
	 *
1134
	 * @param string $output HTML output generated by wp_dropdown_users().
1135
	 */
1136
	$html = apply_filters( 'wp_dropdown_users', $output );
1137
1138
	if ( $r['echo'] ) {
1139
		echo $html;
1140
	}
1141
	return $html;
1142
}
1143
1144
/**
1145
 * Sanitize user field based on context.
1146
 *
1147
 * Possible context values are:  'raw', 'edit', 'db', 'display', 'attribute' and 'js'. The
1148
 * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display'
1149
 * when calling filters.
1150
 *
1151
 * @since 2.3.0
1152
 *
1153
 * @param string $field   The user Object field name.
1154
 * @param mixed  $value   The user Object value.
1155
 * @param int    $user_id User ID.
1156
 * @param string $context How to sanitize user fields. Looks for 'raw', 'edit', 'db', 'display',
1157
 *                        'attribute' and 'js'.
1158
 * @return mixed Sanitized value.
1159
 */
1160
function sanitize_user_field($field, $value, $user_id, $context) {
1161
	$int_fields = array('ID');
1162
	if ( in_array($field, $int_fields) )
1163
		$value = (int) $value;
1164
1165
	if ( 'raw' == $context )
1166
		return $value;
1167
1168
	if ( !is_string($value) && !is_numeric($value) )
1169
		return $value;
1170
1171
	$prefixed = false !== strpos( $field, 'user_' );
1172
1173
	if ( 'edit' == $context ) {
1174 View Code Duplication
		if ( $prefixed ) {
1175
1176
			/** This filter is documented in wp-includes/post.php */
1177
			$value = apply_filters( "edit_{$field}", $value, $user_id );
1178
		} else {
1179
1180
			/**
1181
			 * Filters a user field value in the 'edit' context.
1182
			 *
1183
			 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
1184
			 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
1185
			 *
1186
			 * @since 2.9.0
1187
			 *
1188
			 * @param mixed $value   Value of the prefixed user field.
1189
			 * @param int   $user_id User ID.
1190
			 */
1191
			$value = apply_filters( "edit_user_{$field}", $value, $user_id );
1192
		}
1193
1194
		if ( 'description' == $field )
1195
			$value = esc_html( $value ); // textarea_escaped?
1196
		else
1197
			$value = esc_attr($value);
1198
	} elseif ( 'db' == $context ) {
1199 View Code Duplication
		if ( $prefixed ) {
1200
			/** This filter is documented in wp-includes/post.php */
1201
			$value = apply_filters( "pre_{$field}", $value );
1202
		} else {
1203
1204
			/**
1205
			 * Filters the value of a user field in the 'db' context.
1206
			 *
1207
			 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
1208
			 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
1209
 			 *
1210
			 * @since 2.9.0
1211
			 *
1212
			 * @param mixed $value Value of the prefixed user field.
1213
			 */
1214
			$value = apply_filters( "pre_user_{$field}", $value );
1215
		}
1216 View Code Duplication
	} else {
1217
		// Use display filters by default.
1218
		if ( $prefixed ) {
1219
1220
			/** This filter is documented in wp-includes/post.php */
1221
			$value = apply_filters( "{$field}", $value, $user_id, $context );
1222
		} else {
1223
1224
			/**
1225
			 * Filters the value of a user field in a standard context.
1226
			 *
1227
			 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
1228
			 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
1229
			 *
1230
			 * @since 2.9.0
1231
			 *
1232
			 * @param mixed  $value   The user object value to sanitize.
1233
			 * @param int    $user_id User ID.
1234
			 * @param string $context The context to filter within.
1235
			 */
1236
			$value = apply_filters( "user_{$field}", $value, $user_id, $context );
1237
		}
1238
	}
1239
1240
	if ( 'user_url' == $field )
1241
		$value = esc_url($value);
1242
1243 View Code Duplication
	if ( 'attribute' == $context ) {
1244
		$value = esc_attr( $value );
1245
	} elseif ( 'js' == $context ) {
1246
		$value = esc_js( $value );
1247
	}
1248
	return $value;
1249
}
1250
1251
/**
1252
 * Update all user caches
1253
 *
1254
 * @since 3.0.0
1255
 *
1256
 * @param object|WP_User $user User object to be cached
1257
 * @return bool|null Returns false on failure.
0 ignored issues
show
Consider making the return type a bit more specific; maybe use false|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1258
 */
1259
function update_user_caches( $user ) {
1260
	if ( $user instanceof WP_User ) {
1261
		if ( ! $user->exists() ) {
1262
			return false;
1263
		}
1264
1265
		$user = $user->data;
1266
	}
1267
1268
	wp_cache_add($user->ID, $user, 'users');
1269
	wp_cache_add($user->user_login, $user->ID, 'userlogins');
1270
	wp_cache_add($user->user_email, $user->ID, 'useremail');
1271
	wp_cache_add($user->user_nicename, $user->ID, 'userslugs');
1272
}
1273
1274
/**
1275
 * Clean all user caches
1276
 *
1277
 * @since 3.0.0
1278
 * @since 4.4.0 'clean_user_cache' action was added.
1279
 *
1280
 * @param WP_User|int $user User object or ID to be cleaned from the cache
1281
 */
1282
function clean_user_cache( $user ) {
1283
	if ( is_numeric( $user ) )
1284
		$user = new WP_User( $user );
1285
1286
	if ( ! $user->exists() )
1287
		return;
1288
1289
	wp_cache_delete( $user->ID, 'users' );
1290
	wp_cache_delete( $user->user_login, 'userlogins' );
1291
	wp_cache_delete( $user->user_email, 'useremail' );
1292
	wp_cache_delete( $user->user_nicename, 'userslugs' );
1293
1294
	/**
1295
	 * Fires immediately after the given user's cache is cleaned.
1296
	 *
1297
	 * @since 4.4.0
1298
	 *
1299
	 * @param int     $user_id User ID.
1300
	 * @param WP_User $user    User object.
1301
	 */
1302
	do_action( 'clean_user_cache', $user->ID, $user );
1303
}
1304
1305
/**
1306
 * Checks whether the given username exists.
1307
 *
1308
 * @since 2.0.0
1309
 *
1310
 * @param string $username Username.
1311
 * @return int|false The user's ID on success, and false on failure.
1312
 */
1313
function username_exists( $username ) {
1314
	if ( $user = get_user_by( 'login', $username ) ) {
1315
		return $user->ID;
1316
	}
1317
	return false;
1318
}
1319
1320
/**
1321
 * Checks whether the given email exists.
1322
 *
1323
 * @since 2.1.0
1324
 *
1325
 * @param string $email Email.
1326
 * @return int|false The user's ID on success, and false on failure.
1327
 */
1328
function email_exists( $email ) {
1329
	if ( $user = get_user_by( 'email', $email) ) {
1330
		return $user->ID;
1331
	}
1332
	return false;
1333
}
1334
1335
/**
1336
 * Checks whether a username is valid.
1337
 *
1338
 * @since 2.0.1
1339
 * @since 4.4.0 Empty sanitized usernames are now considered invalid
1340
 *
1341
 * @param string $username Username.
1342
 * @return bool Whether username given is valid
1343
 */
1344
function validate_username( $username ) {
1345
	$sanitized = sanitize_user( $username, true );
1346
	$valid = ( $sanitized == $username && ! empty( $sanitized ) );
1347
1348
	/**
1349
	 * Filters whether the provided username is valid or not.
1350
	 *
1351
	 * @since 2.0.1
1352
	 *
1353
	 * @param bool   $valid    Whether given username is valid.
1354
	 * @param string $username Username to check.
1355
	 */
1356
	return apply_filters( 'validate_username', $valid, $username );
1357
}
1358
1359
/**
1360
 * Insert a user into the database.
1361
 *
1362
 * Most of the `$userdata` array fields have filters associated with the values. Exceptions are
1363
 * 'ID', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl',
1364
 * 'user_registered', and 'role'. The filters have the prefix 'pre_user_' followed by the field
1365
 * name. An example using 'description' would have the filter called, 'pre_user_description' that
1366
 * can be hooked into.
1367
 *
1368
 * @since 2.0.0
1369
 * @since 3.6.0 The `aim`, `jabber`, and `yim` fields were removed as default user contact
1370
 *              methods for new installs. See wp_get_user_contact_methods().
1371
 * @since 4.7.0 The user's locale can be passed to `$userdata`.
1372
 *
1373
 * @global wpdb $wpdb WordPress database abstraction object.
1374
 *
1375
 * @param array|object|WP_User $userdata {
1376
 *     An array, object, or WP_User object of user data arguments.
1377
 *
1378
 *     @type int         $ID                   User ID. If supplied, the user will be updated.
1379
 *     @type string      $user_pass            The plain-text user password.
1380
 *     @type string      $user_login           The user's login username.
1381
 *     @type string      $user_nicename        The URL-friendly user name.
1382
 *     @type string      $user_url             The user URL.
1383
 *     @type string      $user_email           The user email address.
1384
 *     @type string      $display_name         The user's display name.
1385
 *                                             Default is the user's username.
1386
 *     @type string      $nickname             The user's nickname.
1387
 *                                             Default is the user's username.
1388
 *     @type string      $first_name           The user's first name. For new users, will be used
1389
 *                                             to build the first part of the user's display name
1390
 *                                             if `$display_name` is not specified.
1391
 *     @type string      $last_name            The user's last name. For new users, will be used
1392
 *                                             to build the second part of the user's display name
1393
 *                                             if `$display_name` is not specified.
1394
 *     @type string      $description          The user's biographical description.
1395
 *     @type string|bool $rich_editing         Whether to enable the rich-editor for the user.
1396
 *                                             False if not empty.
1397
 *     @type string|bool $comment_shortcuts    Whether to enable comment moderation keyboard
1398
 *                                             shortcuts for the user. Default false.
1399
 *     @type string      $admin_color          Admin color scheme for the user. Default 'fresh'.
1400
 *     @type bool        $use_ssl              Whether the user should always access the admin over
1401
 *                                             https. Default false.
1402
 *     @type string      $user_registered      Date the user registered. Format is 'Y-m-d H:i:s'.
1403
 *     @type string|bool $show_admin_bar_front Whether to display the Admin Bar for the user on the
1404
 *                                             site's front end. Default true.
1405
 *     @type string      $role                 User's role.
1406
 *     @type string      $locale               User's locale. Default empty.
1407
 * }
1408
 * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
1409
 *                      be created.
1410
 */
1411
function wp_insert_user( $userdata ) {
1412
	global $wpdb;
1413
1414 View Code Duplication
	if ( $userdata instanceof stdClass ) {
1415
		$userdata = get_object_vars( $userdata );
1416
	} elseif ( $userdata instanceof WP_User ) {
1417
		$userdata = $userdata->to_array();
1418
	}
1419
1420
	// Are we updating or creating?
1421
	if ( ! empty( $userdata['ID'] ) ) {
1422
		$ID = (int) $userdata['ID'];
1423
		$update = true;
1424
		$old_user_data = get_userdata( $ID );
1425
1426
		if ( ! $old_user_data ) {
1427
			return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
1428
		}
1429
1430
		// hashed in wp_update_user(), plaintext if called directly
1431
		$user_pass = ! empty( $userdata['user_pass'] ) ? $userdata['user_pass'] : $old_user_data->user_pass;
1432
	} else {
1433
		$update = false;
1434
		// Hash the password
1435
		$user_pass = wp_hash_password( $userdata['user_pass'] );
1436
	}
1437
1438
	$sanitized_user_login = sanitize_user( $userdata['user_login'], true );
1439
1440
	/**
1441
	 * Filters a username after it has been sanitized.
1442
	 *
1443
	 * This filter is called before the user is created or updated.
1444
	 *
1445
	 * @since 2.0.3
1446
	 *
1447
	 * @param string $sanitized_user_login Username after it has been sanitized.
1448
	 */
1449
	$pre_user_login = apply_filters( 'pre_user_login', $sanitized_user_login );
1450
1451
	//Remove any non-printable chars from the login string to see if we have ended up with an empty username
1452
	$user_login = trim( $pre_user_login );
1453
1454
	// user_login must be between 0 and 60 characters.
1455
	if ( empty( $user_login ) ) {
1456
		return new WP_Error('empty_user_login', __('Cannot create a user with an empty login name.') );
1457
	} elseif ( mb_strlen( $user_login ) > 60 ) {
1458
		return new WP_Error( 'user_login_too_long', __( 'Username may not be longer than 60 characters.' ) );
1459
	}
1460
1461
	if ( ! $update && username_exists( $user_login ) ) {
1462
		return new WP_Error( 'existing_user_login', __( 'Sorry, that username already exists!' ) );
1463
	}
1464
1465
	/**
1466
	 * Filters the list of blacklisted usernames.
1467
	 *
1468
	 * @since 4.4.0
1469
	 *
1470
	 * @param array $usernames Array of blacklisted usernames.
1471
	 */
1472
	$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
1473
1474 View Code Duplication
	if ( in_array( strtolower( $user_login ), array_map( 'strtolower', $illegal_logins ) ) ) {
1475
		return new WP_Error( 'invalid_username', __( 'Sorry, that username is not allowed.' ) );
1476
	}
1477
1478
	/*
1479
	 * If a nicename is provided, remove unsafe user characters before using it.
1480
	 * Otherwise build a nicename from the user_login.
1481
	 */
1482
	if ( ! empty( $userdata['user_nicename'] ) ) {
1483
		$user_nicename = sanitize_user( $userdata['user_nicename'], true );
1484
		if ( mb_strlen( $user_nicename ) > 50 ) {
1485
			return new WP_Error( 'user_nicename_too_long', __( 'Nicename may not be longer than 50 characters.' ) );
1486
		}
1487
	} else {
1488
		$user_nicename = mb_substr( $user_login, 0, 50 );
1489
	}
1490
1491
	$user_nicename = sanitize_title( $user_nicename );
1492
1493
	// Store values to save in user meta.
1494
	$meta = array();
1495
1496
	/**
1497
	 * Filters a user's nicename before the user is created or updated.
1498
	 *
1499
	 * @since 2.0.3
1500
	 *
1501
	 * @param string $user_nicename The user's nicename.
1502
	 */
1503
	$user_nicename = apply_filters( 'pre_user_nicename', $user_nicename );
1504
1505
	$raw_user_url = empty( $userdata['user_url'] ) ? '' : $userdata['user_url'];
1506
1507
	/**
1508
	 * Filters a user's URL before the user is created or updated.
1509
	 *
1510
	 * @since 2.0.3
1511
	 *
1512
	 * @param string $raw_user_url The user's URL.
1513
	 */
1514
	$user_url = apply_filters( 'pre_user_url', $raw_user_url );
1515
1516
	$raw_user_email = empty( $userdata['user_email'] ) ? '' : $userdata['user_email'];
1517
1518
	/**
1519
	 * Filters a user's email before the user is created or updated.
1520
	 *
1521
	 * @since 2.0.3
1522
	 *
1523
	 * @param string $raw_user_email The user's email.
1524
	 */
1525
	$user_email = apply_filters( 'pre_user_email', $raw_user_email );
1526
1527
	/*
1528
	 * If there is no update, just check for `email_exists`. If there is an update,
1529
	 * check if current email and new email are the same, or not, and check `email_exists`
1530
	 * accordingly.
1531
	 */
1532
	if ( ( ! $update || ( ! empty( $old_user_data ) && 0 !== strcasecmp( $user_email, $old_user_data->user_email ) ) )
1533
		&& ! defined( 'WP_IMPORTING' )
1534
		&& email_exists( $user_email )
1535
	) {
1536
		return new WP_Error( 'existing_user_email', __( 'Sorry, that email address is already used!' ) );
1537
	}
1538
	$nickname = empty( $userdata['nickname'] ) ? $user_login : $userdata['nickname'];
1539
1540
	/**
1541
	 * Filters a user's nickname before the user is created or updated.
1542
	 *
1543
	 * @since 2.0.3
1544
	 *
1545
	 * @param string $nickname The user's nickname.
1546
	 */
1547
	$meta['nickname'] = apply_filters( 'pre_user_nickname', $nickname );
1548
1549
	$first_name = empty( $userdata['first_name'] ) ? '' : $userdata['first_name'];
1550
1551
	/**
1552
	 * Filters a user's first name before the user is created or updated.
1553
	 *
1554
	 * @since 2.0.3
1555
	 *
1556
	 * @param string $first_name The user's first name.
1557
	 */
1558
	$meta['first_name'] = apply_filters( 'pre_user_first_name', $first_name );
1559
1560
	$last_name = empty( $userdata['last_name'] ) ? '' : $userdata['last_name'];
1561
1562
	/**
1563
	 * Filters a user's last name before the user is created or updated.
1564
	 *
1565
	 * @since 2.0.3
1566
	 *
1567
	 * @param string $last_name The user's last name.
1568
	 */
1569
	$meta['last_name'] = apply_filters( 'pre_user_last_name', $last_name );
1570
1571
	if ( empty( $userdata['display_name'] ) ) {
1572
		if ( $update ) {
1573
			$display_name = $user_login;
1574
		} elseif ( $meta['first_name'] && $meta['last_name'] ) {
1575
			/* translators: 1: first name, 2: last name */
1576
			$display_name = sprintf( _x( '%1$s %2$s', 'Display name based on first name and last name' ), $meta['first_name'], $meta['last_name'] );
1577
		} elseif ( $meta['first_name'] ) {
1578
			$display_name = $meta['first_name'];
1579
		} elseif ( $meta['last_name'] ) {
1580
			$display_name = $meta['last_name'];
1581
		} else {
1582
			$display_name = $user_login;
1583
		}
1584
	} else {
1585
		$display_name = $userdata['display_name'];
1586
	}
1587
1588
	/**
1589
	 * Filters a user's display name before the user is created or updated.
1590
	 *
1591
	 * @since 2.0.3
1592
	 *
1593
	 * @param string $display_name The user's display name.
1594
	 */
1595
	$display_name = apply_filters( 'pre_user_display_name', $display_name );
1596
1597
	$description = empty( $userdata['description'] ) ? '' : $userdata['description'];
1598
1599
	/**
1600
	 * Filters a user's description before the user is created or updated.
1601
	 *
1602
	 * @since 2.0.3
1603
	 *
1604
	 * @param string $description The user's description.
1605
	 */
1606
	$meta['description'] = apply_filters( 'pre_user_description', $description );
1607
1608
	$meta['rich_editing'] = empty( $userdata['rich_editing'] ) ? 'true' : $userdata['rich_editing'];
1609
1610
	$meta['comment_shortcuts'] = empty( $userdata['comment_shortcuts'] ) || 'false' === $userdata['comment_shortcuts'] ? 'false' : 'true';
1611
1612
	$admin_color = empty( $userdata['admin_color'] ) ? 'fresh' : $userdata['admin_color'];
1613
	$meta['admin_color'] = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $admin_color );
1614
1615
	$meta['use_ssl'] = empty( $userdata['use_ssl'] ) ? 0 : $userdata['use_ssl'];
1616
1617
	$user_registered = empty( $userdata['user_registered'] ) ? gmdate( 'Y-m-d H:i:s' ) : $userdata['user_registered'];
1618
1619
	$meta['show_admin_bar_front'] = empty( $userdata['show_admin_bar_front'] ) ? 'true' : $userdata['show_admin_bar_front'];
1620
1621
	$meta['locale'] = isset( $userdata['locale'] ) ? $userdata['locale'] : '';
1622
1623
	$user_nicename_check = $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1" , $user_nicename, $user_login));
1624
1625
	if ( $user_nicename_check ) {
1626
		$suffix = 2;
1627
		while ($user_nicename_check) {
1628
			// user_nicename allows 50 chars. Subtract one for a hyphen, plus the length of the suffix.
1629
			$base_length = 49 - mb_strlen( $suffix );
1630
			$alt_user_nicename = mb_substr( $user_nicename, 0, $base_length ) . "-$suffix";
1631
			$user_nicename_check = $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1" , $alt_user_nicename, $user_login));
1632
			$suffix++;
1633
		}
1634
		$user_nicename = $alt_user_nicename;
0 ignored issues
show
The variable $alt_user_nicename 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...
1635
	}
1636
1637
	$compacted = compact( 'user_pass', 'user_email', 'user_url', 'user_nicename', 'display_name', 'user_registered' );
1638
	$data = wp_unslash( $compacted );
1639
1640
	if ( $update ) {
1641
		if ( $user_email !== $old_user_data->user_email ) {
0 ignored issues
show
The variable $old_user_data 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...
1642
			$data['user_activation_key'] = '';
1643
		}
1644
		$wpdb->update( $wpdb->users, $data, compact( 'ID' ) );
1645
		$user_id = (int) $ID;
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...
1646
	} else {
1647
		$wpdb->insert( $wpdb->users, $data + compact( 'user_login' ) );
1648
		$user_id = (int) $wpdb->insert_id;
1649
	}
1650
1651
	$user = new WP_User( $user_id );
1652
1653
	/**
1654
 	 * Filters a user's meta values and keys before the user is created or updated.
1655
 	 *
1656
 	 * Does not include contact methods. These are added using `wp_get_user_contact_methods( $user )`.
1657
 	 *
1658
 	 * @since 4.4.0
1659
 	 *
1660
 	 * @param array $meta {
1661
 	 *     Default meta values and keys for the user.
1662
 	 *
1663
 	 *     @type string   $nickname             The user's nickname. Default is the user's username.
1664
	 *     @type string   $first_name           The user's first name.
1665
	 *     @type string   $last_name            The user's last name.
1666
	 *     @type string   $description          The user's description.
1667
	 *     @type bool     $rich_editing         Whether to enable the rich-editor for the user. False if not empty.
1668
	 *     @type bool     $comment_shortcuts    Whether to enable keyboard shortcuts for the user. Default false.
1669
	 *     @type string   $admin_color          The color scheme for a user's admin screen. Default 'fresh'.
1670
	 *     @type int|bool $use_ssl              Whether to force SSL on the user's admin area. 0|false if SSL is
1671
	 *                                          not forced.
1672
	 *     @type bool     $show_admin_bar_front Whether to show the admin bar on the front end for the user.
1673
	 *                                          Default true.
1674
 	 * }
1675
	 * @param WP_User $user   User object.
1676
	 * @param bool    $update Whether the user is being updated rather than created.
1677
 	 */
1678
	$meta = apply_filters( 'insert_user_meta', $meta, $user, $update );
1679
1680
	// Update user meta.
1681
	foreach ( $meta as $key => $value ) {
1682
		update_user_meta( $user_id, $key, $value );
1683
	}
1684
1685
	foreach ( wp_get_user_contact_methods( $user ) as $key => $value ) {
1686
		if ( isset( $userdata[ $key ] ) ) {
1687
			update_user_meta( $user_id, $key, $userdata[ $key ] );
1688
		}
1689
	}
1690
1691
	if ( isset( $userdata['role'] ) ) {
1692
		$user->set_role( $userdata['role'] );
1693
	} elseif ( ! $update ) {
1694
		$user->set_role(get_option('default_role'));
1695
	}
1696
	wp_cache_delete( $user_id, 'users' );
1697
	wp_cache_delete( $user_login, 'userlogins' );
1698
1699
	if ( $update ) {
1700
		/**
1701
		 * Fires immediately after an existing user is updated.
1702
		 *
1703
		 * @since 2.0.0
1704
		 *
1705
		 * @param int    $user_id       User ID.
1706
		 * @param object $old_user_data Object containing user's data prior to update.
1707
		 */
1708
		do_action( 'profile_update', $user_id, $old_user_data );
1709
	} else {
1710
		/**
1711
		 * Fires immediately after a new user is registered.
1712
		 *
1713
		 * @since 1.5.0
1714
		 *
1715
		 * @param int $user_id User ID.
1716
		 */
1717
		do_action( 'user_register', $user_id );
1718
	}
1719
1720
	return $user_id;
1721
}
1722
1723
/**
1724
 * Update a user in the database.
1725
 *
1726
 * It is possible to update a user's password by specifying the 'user_pass'
1727
 * value in the $userdata parameter array.
1728
 *
1729
 * If current user's password is being updated, then the cookies will be
1730
 * cleared.
1731
 *
1732
 * @since 2.0.0
1733
 *
1734
 * @see wp_insert_user() For what fields can be set in $userdata.
1735
 *
1736
 * @param mixed $userdata An array of user data or a user object of type stdClass or WP_User.
1737
 * @return int|WP_Error The updated user's ID or a WP_Error object if the user could not be updated.
1738
 */
1739
function wp_update_user($userdata) {
1740 View Code Duplication
	if ( $userdata instanceof stdClass ) {
1741
		$userdata = get_object_vars( $userdata );
1742
	} elseif ( $userdata instanceof WP_User ) {
1743
		$userdata = $userdata->to_array();
1744
	}
1745
1746
	$ID = isset( $userdata['ID'] ) ? (int) $userdata['ID'] : 0;
1747
	if ( ! $ID ) {
1748
		return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
1749
	}
1750
1751
	// First, get all of the original fields
1752
	$user_obj = get_userdata( $ID );
1753
	if ( ! $user_obj ) {
1754
		return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
1755
	}
1756
1757
	$user = $user_obj->to_array();
1758
1759
	// Add additional custom fields
1760
	foreach ( _get_additional_user_keys( $user_obj ) as $key ) {
1761
		$user[ $key ] = get_user_meta( $ID, $key, true );
1762
	}
1763
1764
	// Escape data pulled from DB.
1765
	$user = add_magic_quotes( $user );
1766
1767
	if ( ! empty( $userdata['user_pass'] ) && $userdata['user_pass'] !== $user_obj->user_pass ) {
1768
		// If password is changing, hash it now
1769
		$plaintext_pass = $userdata['user_pass'];
1770
		$userdata['user_pass'] = wp_hash_password( $userdata['user_pass'] );
1771
1772
		/**
1773
		 * Filters whether to send the password change email.
1774
		 *
1775
		 * @since 4.3.0
1776
		 *
1777
		 * @see wp_insert_user() For `$user` and `$userdata` fields.
1778
		 *
1779
		 * @param bool  $send     Whether to send the email.
1780
		 * @param array $user     The original user array.
1781
		 * @param array $userdata The updated user array.
1782
		 *
1783
		 */
1784
		$send_password_change_email = apply_filters( 'send_password_change_email', true, $user, $userdata );
1785
	}
1786
1787
	if ( isset( $userdata['user_email'] ) && $user['user_email'] !== $userdata['user_email'] ) {
1788
		/**
1789
		 * Filters whether to send the email change email.
1790
		 *
1791
		 * @since 4.3.0
1792
		 *
1793
		 * @see wp_insert_user() For `$user` and `$userdata` fields.
1794
		 *
1795
		 * @param bool  $send     Whether to send the email.
1796
		 * @param array $user     The original user array.
1797
		 * @param array $userdata The updated user array.
1798
		 *
1799
		 */
1800
		$send_email_change_email = apply_filters( 'send_email_change_email', true, $user, $userdata );
1801
	}
1802
1803
	wp_cache_delete( $user['user_email'], 'useremail' );
1804
	wp_cache_delete( $user['user_nicename'], 'userslugs' );
1805
1806
	// Merge old and new fields with new fields overwriting old ones.
1807
	$userdata = array_merge( $user, $userdata );
1808
	$user_id = wp_insert_user( $userdata );
1809
1810
	if ( ! is_wp_error( $user_id ) ) {
1811
1812
		$blog_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
1813
1814
		$switched_locale = false;
1815
		if ( ! empty( $send_password_change_email ) || ! empty( $send_email_change_email ) ) {
1816
			$switched_locale = switch_to_locale( get_user_locale( $user_id ) );
1817
		}
1818
1819 View Code Duplication
		if ( ! empty( $send_password_change_email ) ) {
1820
			/* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
1821
			$pass_change_text = __( 'Hi ###USERNAME###,
1822
1823
This notice confirms that your password was changed on ###SITENAME###.
1824
1825
If you did not change your password, please contact the Site Administrator at
1826
###ADMIN_EMAIL###
1827
1828
This email has been sent to ###EMAIL###
1829
1830
Regards,
1831
All at ###SITENAME###
1832
###SITEURL###' );
1833
1834
			$pass_change_email = array(
1835
				'to'      => $user['user_email'],
1836
				/* translators: User password change notification email subject. 1: Site name */
1837
				'subject' => __( '[%s] Notice of Password Change' ),
1838
				'message' => $pass_change_text,
1839
				'headers' => '',
1840
			);
1841
1842
			/**
1843
			 * Filters the contents of the email sent when the user's password is changed.
1844
			 *
1845
			 * @since 4.3.0
1846
			 *
1847
			 * @param array $pass_change_email {
1848
			 *            Used to build wp_mail().
1849
			 *            @type string $to      The intended recipients. Add emails in a comma separated string.
1850
			 *            @type string $subject The subject of the email.
1851
			 *            @type string $message The content of the email.
1852
			 *                The following strings have a special meaning and will get replaced dynamically:
1853
			 *                - ###USERNAME###    The current user's username.
1854
			 *                - ###ADMIN_EMAIL### The admin email in case this was unexpected.
1855
			 *                - ###EMAIL###       The old email.
1856
			 *                - ###SITENAME###    The name of the site.
1857
			 *                - ###SITEURL###     The URL to the site.
1858
			 *            @type string $headers Headers. Add headers in a newline (\r\n) separated string.
1859
			 *        }
1860
			 * @param array $user     The original user array.
1861
			 * @param array $userdata The updated user array.
1862
			 *
1863
			 */
1864
			$pass_change_email = apply_filters( 'password_change_email', $pass_change_email, $user, $userdata );
1865
1866
			$pass_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $pass_change_email['message'] );
1867
			$pass_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $pass_change_email['message'] );
1868
			$pass_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $pass_change_email['message'] );
1869
			$pass_change_email['message'] = str_replace( '###SITENAME###', $blog_name, $pass_change_email['message'] );
1870
			$pass_change_email['message'] = str_replace( '###SITEURL###', home_url(), $pass_change_email['message'] );
1871
1872
			wp_mail( $pass_change_email['to'], sprintf( $pass_change_email['subject'], $blog_name ), $pass_change_email['message'], $pass_change_email['headers'] );
1873
		}
1874
1875 View Code Duplication
		if ( ! empty( $send_email_change_email ) ) {
1876
			/* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
1877
			$email_change_text = __( 'Hi ###USERNAME###,
1878
1879
This notice confirms that your email was changed on ###SITENAME###.
1880
1881
If you did not change your email, please contact the Site Administrator at
1882
###ADMIN_EMAIL###
1883
1884
This email has been sent to ###EMAIL###
1885
1886
Regards,
1887
All at ###SITENAME###
1888
###SITEURL###' );
1889
1890
			$email_change_email = array(
1891
				'to'      => $user['user_email'],
1892
				/* translators: User email change notification email subject. 1: Site name */
1893
				'subject' => __( '[%s] Notice of Email Change' ),
1894
				'message' => $email_change_text,
1895
				'headers' => '',
1896
			);
1897
1898
			/**
1899
			 * Filters the contents of the email sent when the user's email is changed.
1900
			 *
1901
			 * @since 4.3.0
1902
			 *
1903
			 * @param array $email_change_email {
1904
			 *            Used to build wp_mail().
1905
			 *            @type string $to      The intended recipients.
1906
			 *            @type string $subject The subject of the email.
1907
			 *            @type string $message The content of the email.
1908
			 *                The following strings have a special meaning and will get replaced dynamically:
1909
			 *                - ###USERNAME###    The current user's username.
1910
			 *                - ###ADMIN_EMAIL### The admin email in case this was unexpected.
1911
			 *                - ###EMAIL###       The old email.
1912
			 *                - ###SITENAME###    The name of the site.
1913
			 *                - ###SITEURL###     The URL to the site.
1914
			 *            @type string $headers Headers.
1915
			 *        }
1916
			 * @param array $user The original user array.
1917
			 * @param array $userdata The updated user array.
1918
			 */
1919
			$email_change_email = apply_filters( 'email_change_email', $email_change_email, $user, $userdata );
1920
1921
			$email_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $email_change_email['message'] );
1922
			$email_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $email_change_email['message'] );
1923
			$email_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $email_change_email['message'] );
1924
			$email_change_email['message'] = str_replace( '###SITENAME###', $blog_name, $email_change_email['message'] );
1925
			$email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] );
1926
1927
			wp_mail( $email_change_email['to'], sprintf( $email_change_email['subject'], $blog_name ), $email_change_email['message'], $email_change_email['headers'] );
1928
		}
1929
1930
		if ( $switched_locale ) {
1931
			restore_previous_locale();
1932
		}
1933
	}
1934
1935
	// Update the cookies if the password changed.
1936
	$current_user = wp_get_current_user();
1937
	if ( $current_user->ID == $ID ) {
1938
		if ( isset($plaintext_pass) ) {
1939
			wp_clear_auth_cookie();
1940
1941
			// Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
1942
			// If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
1943
			$logged_in_cookie    = wp_parse_auth_cookie( '', 'logged_in' );
1944
			/** This filter is documented in wp-includes/pluggable.php */
1945
			$default_cookie_life = apply_filters( 'auth_cookie_expiration', ( 2 * DAY_IN_SECONDS ), $ID, false );
1946
			$remember            = ( ( $logged_in_cookie['expiration'] - time() ) > $default_cookie_life );
1947
1948
			wp_set_auth_cookie( $ID, $remember );
1949
		}
1950
	}
1951
1952
	return $user_id;
1953
}
1954
1955
/**
1956
 * A simpler way of inserting a user into the database.
1957
 *
1958
 * Creates a new user with just the username, password, and email. For more
1959
 * complex user creation use wp_insert_user() to specify more information.
1960
 *
1961
 * @since 2.0.0
1962
 * @see wp_insert_user() More complete way to create a new user
1963
 *
1964
 * @param string $username The user's username.
1965
 * @param string $password The user's password.
1966
 * @param string $email    Optional. The user's email. Default empty.
1967
 * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
1968
 *                      be created.
1969
 */
1970
function wp_create_user($username, $password, $email = '') {
1971
	$user_login = wp_slash( $username );
1972
	$user_email = wp_slash( $email    );
1973
	$user_pass = $password;
1974
1975
	$userdata = compact('user_login', 'user_email', 'user_pass');
1976
	return wp_insert_user($userdata);
1977
}
1978
1979
/**
1980
 * Returns a list of meta keys to be (maybe) populated in wp_update_user().
1981
 *
1982
 * The list of keys returned via this function are dependent on the presence
1983
 * of those keys in the user meta data to be set.
1984
 *
1985
 * @since 3.3.0
1986
 * @access private
1987
 *
1988
 * @param WP_User $user WP_User instance.
1989
 * @return array List of user keys to be populated in wp_update_user().
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array<string|integer>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1990
 */
1991
function _get_additional_user_keys( $user ) {
1992
	$keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front', 'locale' );
1993
	return array_merge( $keys, array_keys( wp_get_user_contact_methods( $user ) ) );
1994
}
1995
1996
/**
1997
 * Set up the user contact methods.
1998
 *
1999
 * Default contact methods were removed in 3.6. A filter dictates contact methods.
2000
 *
2001
 * @since 3.7.0
2002
 *
2003
 * @param WP_User $user Optional. WP_User object.
2004
 * @return array Array of contact methods and their labels.
2005
 */
2006
function wp_get_user_contact_methods( $user = null ) {
2007
	$methods = array();
2008
	if ( get_site_option( 'initial_db_version' ) < 23588 ) {
2009
		$methods = array(
2010
			'aim'    => __( 'AIM' ),
2011
			'yim'    => __( 'Yahoo IM' ),
2012
			'jabber' => __( 'Jabber / Google Talk' )
2013
		);
2014
	}
2015
2016
	/**
2017
	 * Filters the user contact methods.
2018
	 *
2019
	 * @since 2.9.0
2020
	 *
2021
	 * @param array   $methods Array of contact methods and their labels.
2022
 	 * @param WP_User $user    WP_User object.
2023
	 */
2024
	return apply_filters( 'user_contactmethods', $methods, $user );
2025
}
2026
2027
/**
2028
 * The old private function for setting up user contact methods.
2029
 *
2030
 * Use wp_get_user_contact_methods() instead.
2031
 *
2032
 * @since 2.9.0
2033
 * @access private
2034
 *
2035
 * @param WP_User $user Optional. WP_User object. Default null.
2036
 * @return array Array of contact methods and their labels.
2037
 */
2038
function _wp_get_user_contactmethods( $user = null ) {
2039
	return wp_get_user_contact_methods( $user );
2040
}
2041
2042
/**
2043
 * Gets the text suggesting how to create strong passwords.
2044
 *
2045
 * @since 4.1.0
2046
 *
2047
 * @return string The password hint text.
2048
 */
2049
function wp_get_password_hint() {
2050
	$hint = __( 'Hint: The password should be at least twelve characters long. To make it stronger, use upper and lower case letters, numbers, and symbols like ! " ? $ % ^ &amp; ).' );
2051
2052
	/**
2053
	 * Filters the text describing the site's password complexity policy.
2054
	 *
2055
	 * @since 4.1.0
2056
	 *
2057
	 * @param string $hint The password hint text.
2058
	 */
2059
	return apply_filters( 'password_hint', $hint );
2060
}
2061
2062
/**
2063
 * Creates, stores, then returns a password reset key for user.
2064
 *
2065
 * @since 4.4.0
2066
 *
2067
 * @global wpdb         $wpdb      WordPress database abstraction object.
2068
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework.
2069
 *
2070
 * @param WP_User $user User to retrieve password reset key for.
2071
 *
2072
 * @return string|WP_Error Password reset key on success. WP_Error on error.
2073
 */
2074
function get_password_reset_key( $user ) {
2075
	global $wpdb, $wp_hasher;
2076
2077
	/**
2078
	 * Fires before a new password is retrieved.
2079
	 *
2080
	 * Use the {@see 'retrieve_password'} hook instead.
2081
	 *
2082
	 * @since 1.5.0
2083
	 * @deprecated 1.5.1 Misspelled. Use 'retrieve_password' hook instead.
2084
	 *
2085
	 * @param string $user_login The user login name.
2086
	 */
2087
	do_action( 'retreive_password', $user->user_login );
2088
2089
	/**
2090
	 * Fires before a new password is retrieved.
2091
	 *
2092
	 * @since 1.5.1
2093
	 *
2094
	 * @param string $user_login The user login name.
2095
	 */
2096
	do_action( 'retrieve_password', $user->user_login );
2097
2098
	$allow = true;
2099
	if ( is_multisite() && is_user_spammy( $user ) ) {
2100
		$allow = false;
2101
	}
2102
2103
	/**
2104
	 * Filters whether to allow a password to be reset.
2105
	 *
2106
	 * @since 2.7.0
2107
	 *
2108
	 * @param bool $allow         Whether to allow the password to be reset. Default true.
2109
	 * @param int  $user_data->ID The ID of the user attempting to reset a password.
2110
	 */
2111
	$allow = apply_filters( 'allow_password_reset', $allow, $user->ID );
2112
2113
	if ( ! $allow ) {
2114
		return new WP_Error( 'no_password_reset', __( 'Password reset is not allowed for this user' ) );
2115
	} elseif ( is_wp_error( $allow ) ) {
2116
		return $allow;
2117
	}
2118
2119
	// Generate something random for a password reset key.
2120
	$key = wp_generate_password( 20, false );
2121
2122
	/**
2123
	 * Fires when a password reset key is generated.
2124
	 *
2125
	 * @since 2.5.0
2126
	 *
2127
	 * @param string $user_login The username for the user.
2128
	 * @param string $key        The generated password reset key.
2129
	 */
2130
	do_action( 'retrieve_password_key', $user->user_login, $key );
2131
2132
	// Now insert the key, hashed, into the DB.
2133 View Code Duplication
	if ( empty( $wp_hasher ) ) {
2134
		require_once ABSPATH . WPINC . '/class-phpass.php';
2135
		$wp_hasher = new PasswordHash( 8, true );
2136
	}
2137
	$hashed = time() . ':' . $wp_hasher->HashPassword( $key );
2138
	$key_saved = $wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user->user_login ) );
2139
	if ( false === $key_saved ) {
2140
		return new WP_Error( 'no_password_key_update', __( 'Could not save password reset key to database.' ) );
2141
	}
2142
2143
	return $key;
2144
}
2145
2146
/**
2147
 * Retrieves a user row based on password reset key and login
2148
 *
2149
 * A key is considered 'expired' if it exactly matches the value of the
2150
 * user_activation_key field, rather than being matched after going through the
2151
 * hashing process. This field is now hashed; old values are no longer accepted
2152
 * but have a different WP_Error code so good user feedback can be provided.
2153
 *
2154
 * @since 3.1.0
2155
 *
2156
 * @global wpdb         $wpdb      WordPress database object for queries.
2157
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
2158
 *
2159
 * @param string $key       Hash to validate sending user's password.
2160
 * @param string $login     The user login.
2161
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
2162
 */
2163
function check_password_reset_key($key, $login) {
2164
	global $wpdb, $wp_hasher;
2165
2166
	$key = preg_replace('/[^a-z0-9]/i', '', $key);
2167
2168
	if ( empty( $key ) || !is_string( $key ) )
2169
		return new WP_Error('invalid_key', __('Invalid key'));
2170
2171
	if ( empty($login) || !is_string($login) )
2172
		return new WP_Error('invalid_key', __('Invalid key'));
2173
2174
	$row = $wpdb->get_row( $wpdb->prepare( "SELECT ID, user_activation_key FROM $wpdb->users WHERE user_login = %s", $login ) );
2175
	if ( ! $row )
2176
		return new WP_Error('invalid_key', __('Invalid key'));
2177
2178 View Code Duplication
	if ( empty( $wp_hasher ) ) {
2179
		require_once ABSPATH . WPINC . '/class-phpass.php';
2180
		$wp_hasher = new PasswordHash( 8, true );
2181
	}
2182
2183
	/**
2184
	 * Filters the expiration time of password reset keys.
2185
	 *
2186
	 * @since 4.3.0
2187
	 *
2188
	 * @param int $expiration The expiration time in seconds.
2189
	 */
2190
	$expiration_duration = apply_filters( 'password_reset_expiration', DAY_IN_SECONDS );
2191
2192
	if ( false !== strpos( $row->user_activation_key, ':' ) ) {
2193
		list( $pass_request_time, $pass_key ) = explode( ':', $row->user_activation_key, 2 );
2194
		$expiration_time = $pass_request_time + $expiration_duration;
2195
	} else {
2196
		$pass_key = $row->user_activation_key;
2197
		$expiration_time = false;
2198
	}
2199
2200
	if ( ! $pass_key ) {
2201
		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
2202
	}
2203
2204
	$hash_is_correct = $wp_hasher->CheckPassword( $key, $pass_key );
2205
2206
	if ( $hash_is_correct && $expiration_time && time() < $expiration_time ) {
2207
		return get_userdata( $row->ID );
2208
	} elseif ( $hash_is_correct && $expiration_time ) {
2209
		// Key has an expiration time that's passed
2210
		return new WP_Error( 'expired_key', __( 'Invalid key' ) );
2211
	}
2212
2213
	if ( hash_equals( $row->user_activation_key, $key ) || ( $hash_is_correct && ! $expiration_time ) ) {
2214
		$return = new WP_Error( 'expired_key', __( 'Invalid key' ) );
2215
		$user_id = $row->ID;
2216
2217
		/**
2218
		 * Filters the return value of check_password_reset_key() when an
2219
		 * old-style key is used.
2220
		 *
2221
		 * @since 3.7.0 Previously plain-text keys were stored in the database.
2222
		 * @since 4.3.0 Previously key hashes were stored without an expiration time.
2223
		 *
2224
		 * @param WP_Error $return  A WP_Error object denoting an expired key.
2225
		 *                          Return a WP_User object to validate the key.
2226
		 * @param int      $user_id The matched user ID.
2227
		 */
2228
		return apply_filters( 'password_reset_key_expired', $return, $user_id );
2229
	}
2230
2231
	return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
2232
}
2233
2234
/**
2235
 * Handles resetting the user's password.
2236
 *
2237
 * @since 2.5.0
2238
 *
2239
 * @param object $user     The user
2240
 * @param string $new_pass New password for the user in plaintext
2241
 */
2242
function reset_password( $user, $new_pass ) {
2243
	/**
2244
	 * Fires before the user's password is reset.
2245
	 *
2246
	 * @since 1.5.0
2247
	 *
2248
	 * @param object $user     The user.
2249
	 * @param string $new_pass New user password.
2250
	 */
2251
	do_action( 'password_reset', $user, $new_pass );
2252
2253
	wp_set_password( $new_pass, $user->ID );
2254
	update_user_option( $user->ID, 'default_password_nag', false, true );
2255
2256
	/**
2257
	 * Fires after the user's password is reset.
2258
	 *
2259
	 * @since 4.4.0
2260
	 *
2261
	 * @param object $user     The user.
2262
	 * @param string $new_pass New user password.
2263
	 */
2264
	do_action( 'after_password_reset', $user, $new_pass );
2265
}
2266
2267
/**
2268
 * Handles registering a new user.
2269
 *
2270
 * @since 2.5.0
2271
 *
2272
 * @param string $user_login User's username for logging in
2273
 * @param string $user_email User's email address to send password and add
2274
 * @return int|WP_Error Either user's ID or error on failure.
2275
 */
2276
function register_new_user( $user_login, $user_email ) {
2277
	$errors = new WP_Error();
2278
2279
	$sanitized_user_login = sanitize_user( $user_login );
2280
	/**
2281
	 * Filters the email address of a user being registered.
2282
	 *
2283
	 * @since 2.1.0
2284
	 *
2285
	 * @param string $user_email The email address of the new user.
2286
	 */
2287
	$user_email = apply_filters( 'user_registration_email', $user_email );
2288
2289
	// Check the username
2290
	if ( $sanitized_user_login == '' ) {
2291
		$errors->add( 'empty_username', __( '<strong>ERROR</strong>: Please enter a username.' ) );
2292
	} elseif ( ! validate_username( $user_login ) ) {
2293
		$errors->add( 'invalid_username', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
2294
		$sanitized_user_login = '';
2295
	} elseif ( username_exists( $sanitized_user_login ) ) {
2296
		$errors->add( 'username_exists', __( '<strong>ERROR</strong>: This username is already registered. Please choose another one.' ) );
2297
2298
	} else {
2299
		/** This filter is documented in wp-includes/user.php */
2300
		$illegal_user_logins = array_map( 'strtolower', (array) apply_filters( 'illegal_user_logins', array() ) );
2301
		if ( in_array( strtolower( $sanitized_user_login ), $illegal_user_logins ) ) {
2302
			$errors->add( 'invalid_username', __( '<strong>ERROR</strong>: Sorry, that username is not allowed.' ) );
2303
		}
2304
	}
2305
2306
	// Check the email address
2307
	if ( $user_email == '' ) {
2308
		$errors->add( 'empty_email', __( '<strong>ERROR</strong>: Please type your email address.' ) );
2309
	} elseif ( ! is_email( $user_email ) ) {
2310
		$errors->add( 'invalid_email', __( '<strong>ERROR</strong>: The email address isn&#8217;t correct.' ) );
2311
		$user_email = '';
2312
	} elseif ( email_exists( $user_email ) ) {
2313
		$errors->add( 'email_exists', __( '<strong>ERROR</strong>: This email is already registered, please choose another one.' ) );
2314
	}
2315
2316
	/**
2317
	 * Fires when submitting registration form data, before the user is created.
2318
	 *
2319
	 * @since 2.1.0
2320
	 *
2321
	 * @param string   $sanitized_user_login The submitted username after being sanitized.
2322
	 * @param string   $user_email           The submitted email.
2323
	 * @param WP_Error $errors               Contains any errors with submitted username and email,
2324
	 *                                       e.g., an empty field, an invalid username or email,
2325
	 *                                       or an existing username or email.
2326
	 */
2327
	do_action( 'register_post', $sanitized_user_login, $user_email, $errors );
2328
2329
	/**
2330
	 * Filters the errors encountered when a new user is being registered.
2331
	 *
2332
	 * The filtered WP_Error object may, for example, contain errors for an invalid
2333
	 * or existing username or email address. A WP_Error object should always returned,
2334
	 * but may or may not contain errors.
2335
	 *
2336
	 * If any errors are present in $errors, this will abort the user's registration.
2337
	 *
2338
	 * @since 2.1.0
2339
	 *
2340
	 * @param WP_Error $errors               A WP_Error object containing any errors encountered
2341
	 *                                       during registration.
2342
	 * @param string   $sanitized_user_login User's username after it has been sanitized.
2343
	 * @param string   $user_email           User's email.
2344
	 */
2345
	$errors = apply_filters( 'registration_errors', $errors, $sanitized_user_login, $user_email );
2346
2347
	if ( $errors->get_error_code() )
2348
		return $errors;
2349
2350
	$user_pass = wp_generate_password( 12, false );
2351
	$user_id = wp_create_user( $sanitized_user_login, $user_pass, $user_email );
2352
	if ( ! $user_id || is_wp_error( $user_id ) ) {
2353
		$errors->add( 'registerfail', sprintf( __( '<strong>ERROR</strong>: Couldn&#8217;t register you&hellip; please contact the <a href="mailto:%s">webmaster</a> !' ), get_option( 'admin_email' ) ) );
2354
		return $errors;
2355
	}
2356
2357
	update_user_option( $user_id, 'default_password_nag', true, true ); //Set up the Password change nag.
2358
2359
	/**
2360
	 * Fires after a new user registration has been recorded.
2361
	 *
2362
	 * @since 4.4.0
2363
	 *
2364
	 * @param int $user_id ID of the newly registered user.
2365
	 */
2366
	do_action( 'register_new_user', $user_id );
2367
2368
	return $user_id;
2369
}
2370
2371
/**
2372
 * Initiates email notifications related to the creation of new users.
2373
 *
2374
 * Notifications are sent both to the site admin and to the newly created user.
2375
 *
2376
 * @since 4.4.0
2377
 * @since 4.6.0 Converted the `$notify` parameter to accept 'user' for sending
2378
 *              notifications only to the user created.
2379
 *
2380
 * @param int    $user_id ID of the newly created user.
2381
 * @param string $notify  Optional. Type of notification that should happen. Accepts 'admin'
2382
 *                        or an empty string (admin only), 'user', or 'both' (admin and user).
2383
 *                        Default 'both'.
2384
 */
2385
function wp_send_new_user_notifications( $user_id, $notify = 'both' ) {
2386
	wp_new_user_notification( $user_id, null, $notify );
2387
}
2388
2389
/**
2390
 * Retrieve the current session token from the logged_in cookie.
2391
 *
2392
 * @since 4.0.0
2393
 *
2394
 * @return string Token.
2395
 */
2396
function wp_get_session_token() {
2397
	$cookie = wp_parse_auth_cookie( '', 'logged_in' );
2398
	return ! empty( $cookie['token'] ) ? $cookie['token'] : '';
2399
}
2400
2401
/**
2402
 * Retrieve a list of sessions for the current user.
2403
 *
2404
 * @since 4.0.0
2405
 * @return array Array of sessions.
2406
 */
2407
function wp_get_all_sessions() {
2408
	$manager = WP_Session_Tokens::get_instance( get_current_user_id() );
2409
	return $manager->get_all();
2410
}
2411
2412
/**
2413
 * Remove the current session token from the database.
2414
 *
2415
 * @since 4.0.0
2416
 */
2417
function wp_destroy_current_session() {
2418
	$token = wp_get_session_token();
2419
	if ( $token ) {
2420
		$manager = WP_Session_Tokens::get_instance( get_current_user_id() );
2421
		$manager->destroy( $token );
2422
	}
2423
}
2424
2425
/**
2426
 * Remove all but the current session token for the current user for the database.
2427
 *
2428
 * @since 4.0.0
2429
 */
2430
function wp_destroy_other_sessions() {
2431
	$token = wp_get_session_token();
2432
	if ( $token ) {
2433
		$manager = WP_Session_Tokens::get_instance( get_current_user_id() );
2434
		$manager->destroy_others( $token );
2435
	}
2436
}
2437
2438
/**
2439
 * Remove all session tokens for the current user from the database.
2440
 *
2441
 * @since 4.0.0
2442
 */
2443
function wp_destroy_all_sessions() {
2444
	$manager = WP_Session_Tokens::get_instance( get_current_user_id() );
2445
	$manager->destroy_all();
2446
}
2447
2448
/**
2449
 * Get the user IDs of all users with no role on this site.
2450
 *
2451
 * This function returns an empty array when used on Multisite.
2452
 *
2453
 * @since 4.4.0
2454
 *
2455
 * @return array Array of user IDs.
2456
 */
2457
function wp_get_users_with_no_role() {
2458
	global $wpdb;
2459
2460
	if ( is_multisite() ) {
2461
		return array();
2462
	}
2463
2464
	$prefix = $wpdb->get_blog_prefix();
2465
	$regex  = implode( '|', array_keys( wp_roles()->get_names() ) );
2466
	$regex  = preg_replace( '/[^a-zA-Z_\|-]/', '', $regex );
2467
	$users  = $wpdb->get_col( $wpdb->prepare( "
2468
		SELECT user_id
2469
		FROM $wpdb->usermeta
2470
		WHERE meta_key = '{$prefix}capabilities'
2471
		AND meta_value NOT REGEXP %s
2472
	", $regex ) );
2473
2474
	return $users;
2475
}
2476
2477
/**
2478
 * Retrieves the current user object.
2479
 *
2480
 * Will set the current user, if the current user is not set. The current user
2481
 * will be set to the logged-in person. If no user is logged-in, then it will
2482
 * set the current user to 0, which is invalid and won't have any permissions.
2483
 *
2484
 * This function is used by the pluggable functions wp_get_current_user() and
2485
 * get_currentuserinfo(), the latter of which is deprecated but used for backward
2486
 * compatibility.
2487
 *
2488
 * @since 4.5.0
2489
 * @access private
2490
 *
2491
 * @see wp_get_current_user()
2492
 * @global WP_User $current_user Checks if the current user is set.
2493
 *
2494
 * @return WP_User Current WP_User instance.
2495
 */
2496
function _wp_get_current_user() {
2497
	global $current_user;
2498
2499
	if ( ! empty( $current_user ) ) {
2500
		if ( $current_user instanceof WP_User ) {
2501
			return $current_user;
2502
		}
2503
2504
		// Upgrade stdClass to WP_User
2505
		if ( is_object( $current_user ) && isset( $current_user->ID ) ) {
2506
			$cur_id = $current_user->ID;
2507
			$current_user = null;
2508
			wp_set_current_user( $cur_id );
2509
			return $current_user;
2510
		}
2511
2512
		// $current_user has a junk value. Force to WP_User with ID 0.
2513
		$current_user = null;
2514
		wp_set_current_user( 0 );
2515
		return $current_user;
2516
	}
2517
2518
	if ( defined('XMLRPC_REQUEST') && XMLRPC_REQUEST ) {
2519
		wp_set_current_user( 0 );
2520
		return $current_user;
2521
	}
2522
2523
	/**
2524
	 * Filters the current user.
2525
	 *
2526
	 * The default filters use this to determine the current user from the
2527
	 * request's cookies, if available.
2528
	 *
2529
	 * Returning a value of false will effectively short-circuit setting
2530
	 * the current user.
2531
	 *
2532
	 * @since 3.9.0
2533
	 *
2534
	 * @param int|bool $user_id User ID if one has been determined, false otherwise.
2535
	 */
2536
	$user_id = apply_filters( 'determine_current_user', false );
2537
	if ( ! $user_id ) {
2538
		wp_set_current_user( 0 );
2539
		return $current_user;
2540
	}
2541
2542
	wp_set_current_user( $user_id );
2543
2544
	return $current_user;
2545
}
2546