Completed
Push — update/jpo-homepage-keep-state ( b17b7d...60dcc5 )
by
unknown
69:23 queued 60:34
created

Jetpack_Sync_Module_Users   C

Complexity

Total Complexity 79

Size/Duplication

Total Lines 462
Duplicated Lines 9.31 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 0
Metric Value
dl 43
loc 462
rs 5.442
c 0
b 0
f 0
wmc 79
lcom 2
cbo 3

33 Methods

Rating   Name   Duplication   Size   Complexity  
A init_full_sync_listeners() 0 3 1
A init_before_send() 0 10 1
A name() 0 3 1
A get_object_by_id() 0 7 3
A get_reassigned_network_user_id() 0 13 4
A save_user_role_handler() 0 20 3
B maybe_save_user_meta() 0 25 3
B save_user_cap_handler() 0 31 6
A enqueue_full_sync_actions() 0 5 1
A estimate_full_sync_actions() 0 13 2
A get_where_sql() 12 12 2
A get_full_sync_actions() 0 3 1
A get_initial_sync_user_config() 0 11 2
A expand_users() 0 5 1
A remove_user_from_blog_handler() 0 19 2
A is_add_new_user_to_blog() 0 3 1
A is_add_user_to_blog() 0 3 1
A is_create_user() 0 9 1
B init_listeners() 0 37 1
A sanitize_user_and_expand() 0 5 1
B get_user() 0 9 5
A sanitize_user() 0 13 4
A add_to_user() 0 16 4
A get_real_user_capabilities() 0 12 4
A expand_user() 0 8 2
A expand_save_user() 0 17 4
A expand_login_username() 0 6 1
A expand_logout_username() 0 15 4
A deleted_user_handler() 0 13 1
A edited_user_handler() 0 10 1
A user_register_handler() 21 21 3
A add_user_to_blog_handler() 10 10 2
B save_user_handler() 0 36 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Sync_Module_Users often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Sync_Module_Users, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
class Jetpack_Sync_Module_Users extends Jetpack_Sync_Module {
4
	const MAX_INITIAL_SYNC_USERS = 100;
5
6
	protected $previous_role = array();
7
8
	function name() {
9
		return 'users';
10
	}
11
12
	// this is here to support the backfill API
13
	public function get_object_by_id( $object_type, $id ) {
14
		if ( $object_type === 'user' && $user = get_user_by( 'id', intval( $id ) ) ) {
15
			return $this->sanitize_user_and_expand( $user );
16
		}
17
18
		return false;
19
	}
20
21
	public function init_listeners( $callable ) {
22
		// users
23
		add_action( 'user_register', array( $this, 'user_register_handler' ) );
24
		add_action( 'profile_update', array( $this, 'save_user_handler' ), 10, 2 );
25
		add_action( 'add_user_to_blog', array( $this, 'add_user_to_blog_handler' ) );
26
		add_action( 'jetpack_sync_add_user', $callable, 10, 2 );
27
		add_action( 'jetpack_sync_register_user', $callable, 10, 2 );
28
		add_action( 'jetpack_sync_save_user', $callable, 10, 2 );
29
30
		//Edit user info, see https://github.com/WordPress/WordPress/blob/c05f1dc805bddcc0e76fd90c4aaf2d9ea76dc0fb/wp-admin/user-edit.php#L126
31
		add_action( 'personal_options_update', array( $this, 'edited_user_handler' ) );
32
		add_action( 'edit_user_profile_update', array( $this, 'edited_user_handler' ) );
33
		add_action( 'jetpack_user_edited', $callable );
34
35
		add_action( 'jetpack_sync_user_locale', $callable, 10, 2 );
36
		add_action( 'jetpack_sync_user_locale_delete', $callable, 10, 1 );
37
38
		add_action( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 );
39
		add_action( 'jetpack_deleted_user', $callable, 10, 3 );
40
		add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 );
41
		add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 );
42
43
		// user roles
44
		add_action( 'add_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
45
		add_action( 'set_user_role', array( $this, 'save_user_role_handler' ), 10, 3 );
46
		add_action( 'remove_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
47
48
		// user capabilities
49
		add_action( 'added_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
50
		add_action( 'updated_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
51
		add_action( 'deleted_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
52
53
		// user authentication
54
		add_action( 'wp_login', $callable, 10, 2 );
55
		add_action( 'wp_logout', $callable, 10, 0 );
56
		add_action( 'wp_masterbar_logout', $callable, 10, 0 );
57
	}
58
59
	public function init_full_sync_listeners( $callable ) {
60
		add_action( 'jetpack_full_sync_users', $callable );
61
	}
62
63
	public function init_before_send() {
64
		add_filter( 'jetpack_sync_before_send_jetpack_sync_add_user', array( $this, 'expand_user' ) );
65
		add_filter( 'jetpack_sync_before_send_jetpack_sync_register_user', array( $this, 'expand_user' ) );
66
		add_filter( 'jetpack_sync_before_send_jetpack_sync_save_user', array( $this, 'expand_save_user' ) );
67
		add_filter( 'jetpack_sync_before_send_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
68
		add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
69
70
		// full sync
71
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
72
	}
73
74
	public function sanitize_user_and_expand( $user ) {
75
		$user = $this->get_user( $user );
76
		$user = $this->add_to_user( $user );
77
		return $this->sanitize_user( $user );
78
	}
79
80
	private function get_user( $user ) {
81
		if ( $user && ! is_object( $user ) && is_numeric( $user ) ) {
82
			$user = get_user_by( 'id', $user );
83
		}
84
		if ( $user instanceof WP_User ) {
0 ignored issues
show
Bug introduced by
The class WP_User does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
85
			return $user;
86
		}
87
		return null;
88
	}
89
90
	public function sanitize_user( $user ) {
91
		$user = $this->get_user( $user );
92
		// this create a new user object and stops the passing of the object by reference.
93
		$user = unserialize( serialize( $user ) );
94
95
		if ( is_object( $user ) && is_object( $user->data ) ) {
96
			unset( $user->data->user_pass );
97
		}
98
		if ( $user ) {
99
			$user->allcaps = $this->get_real_user_capabilities( $user );
100
		}
101
		return $user;
102
	}
103
104
	public function add_to_user( $user ) {
105
		if ( ! is_object( $user ) ) {
106
			return null;
107
		}
108
		$user->allowed_mime_types = get_allowed_mime_types( $user );
109
110
		if ( function_exists( 'get_user_locale' ) ) {
111
112
			// Only set the user locale if it is different from the site local
113
			if ( get_locale() !== get_user_locale( $user->ID ) ) {
114
				$user->locale = get_user_locale( $user->ID );
115
			}
116
		}
117
118
		return $user;
119
	}
120
121
	public function get_real_user_capabilities( $user ) {
122
		$user_capabilities = array();
123
		if ( is_wp_error( $user ) ) {
124
			return $user_capabilities;
125
		}
126
		foreach( Jetpack_Sync_Defaults::get_capabilities_whitelist() as $capability ) {
127
			if ( $user_has_capabilities = user_can( $user , $capability ) ) {
0 ignored issues
show
Unused Code introduced by
$user_has_capabilities is not used, you could remove the assignment.

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

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

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

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

Loading history...
128
				$user_capabilities[ $capability ] = true;
129
			}
130
		}
131
		return $user_capabilities;
132
	}
133
134
	public function expand_user( $args ) {
135
		list( $user ) = $args;
136
		if ( $user ) {
137
			return array( $this->add_to_user( $user ) );
138
		}
139
140
		return false;
141
	}
142
143
	public function expand_save_user( $args ) {
144
		$default_flags = array(
145
			'password_changed' => false,
146
			'role_changed'     => false,
147
			'previous_role'    => false,
148
		);
149
150
		$flags         = ( isset( $args[1] ) && is_array( $args[1] ) ) ? $args[1] : array();
151
		$expanded_user = $this->expand_user( $args );
152
153
		if ( $expanded_user ) {
154
			$flags           = wp_parse_args( $flags, $default_flags );
155
			$expanded_user[] = $flags;
156
		}
157
158
		return $expanded_user;
159
	}
160
161
	public function expand_login_username( $args ) {
162
		list( $login, $user ) = $args;
163
		$user = $this->sanitize_user( $user );
164
165
		return array( $login, $user );
166
	}
167
168
	public function expand_logout_username( $args, $user_id ) {
169
		$user  = get_userdata( $user_id );
170
		$user  = $this->sanitize_user( $user );
171
172
		$login = '';
173
		if ( is_object( $user ) && is_object( $user->data ) ) {
174
			$login = $user->data->user_login;
175
		}
176
		// if we don't have a user here lets not send anything.
177
		if ( empty( $login ) ) {
178
			return false;
179
		}
180
181
		return array( $login, $user );
182
	}
183
184
	public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) {
185
		$is_multisite = is_multisite();
186
		/**
187
		 * Fires when a user is deleted on a site
188
		 *
189
		 * @since 5.4.0
190
		 *
191
		 * @param int $deleted_user_id - ID of the deleted user
192
		 * @param int $reassigned_user_id - ID of the user the deleted user's posts is reassigned to (if any)
193
		 * @param bool $is_multisite - Whether this site is a multisite installation
194
		 */
195
		do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite );
196
	}
197
198
	public function edited_user_handler( $user_id ) {
199
		/**
200
		 * Fires when a user is edited on a site
201
		 *
202
		 * @since 5.4.0
203
		 *
204
		 * @param int $user_id - ID of the edited user
205
		 */
206
		do_action( 'jetpack_user_edited', $user_id );
207
	}
208
209 View Code Duplication
	function user_register_handler( $user_id, $old_user_data = null ) {
210
		// ensure we only sync users who are members of the current blog
211
		if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
212
			return;
213
		}
214
		$raw_user = get_user_by( 'id', $user_id );
215
		$user = $this->sanitize_user( $raw_user );
216
217
		if ( 'user_register' === current_filter() ) {
218
			/**
219
			 * Fires when a new user is registered on a site
220
			 *
221
			 * @since 4.9.0
222
			 *
223
			 * @param object The WP_User object
224
			 */
225
			do_action( 'jetpack_sync_register_user', $user );
226
227
			return;
228
		}
229
	}
230
231 View Code Duplication
	function add_user_to_blog_handler( $user_id, $old_user_data = null ) {
232
		// ensure we only sync users who are members of the current blog
233
		if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
234
			return;
235
		}
236
		$raw_user = get_user_by( 'id', $user_id );
237
		$user = $this->sanitize_user( $raw_user );
238
239
		do_action( 'jetpack_sync_add_user', $user );
240
	}
241
242
	function save_user_handler( $user_id, $old_user_data = null ) {
243
		// ensure we only sync users who are members of the current blog
244
		if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
245
			return;
246
		}
247
		$raw_user = get_user_by( 'id', $user_id );
248
		$user = $this->sanitize_user( $raw_user );
249
		$user_password_changed = false;
250
251
		// Older versions of WP don't pass the old_user_data in ->data
252
		if ( isset( $old_user_data->data ) ) {
253
			$old_user = $old_user_data->data;
254
		} else {
255
			$old_user = $old_user_data;
256
		}
257
258
		$role_changed = isset( $this->previous_role[ $user_id ] ) ? $this->previous_role[ $user_id ] : false;
259
260
		if ( $old_user !== null && $raw_user->user_pass !== $old_user->user_pass ) {
261
			$user_password_changed = true;
262
		}
263
264
		/**
265
		 * Fires when the client needs to sync an updated user
266
		 *
267
		 * @since 4.2.0
268
		 *
269
		 * @param object The WP_User object
270
		 * @param array state - New since 5.8.0
271
		 */
272
		do_action( 'jetpack_sync_save_user', $user, array(
273
			'password_changed' => $user_password_changed,
274
			'role_changed' => (bool) $role_changed,
275
			'previous_role' => $role_changed,
276
			) );
277
	}
278
279
	function save_user_role_handler( $user_id, $role, $old_roles = null ) {
280
		//The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both
281
		if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
282
			$this->previous_role[ $user_id ] = $old_roles;
283
			return;
284
		}
285
286
		$user = $this->sanitize_user( get_user_by( 'id', $user_id ) );
287
		/**
288
		 * Fires when the client needs to sync an updated user
289
		 *
290
		 * @since 4.2.0
291
		 *
292
		 * @param object The WP_User object
293
	 	 * @param array state
294
		 */
295
		do_action( 'jetpack_sync_save_user', $user, array(
296
			'role_changed' => true,
297
			'previous_role' => $old_roles ) );
298
	}
299
300
	function maybe_save_user_meta( $meta_id, $user_id, $meta_key, $value ) {
301
		if ( $meta_key === 'locale' ) {
302
			if ( current_filter() === 'deleted_user_meta' ) {
303
				/**
304
				 * Allow listeners to listen for user local delete changes
305
				 *
306
				 * @since 4.8.0
307
				 *
308
				 * @param int $user_id - The ID of the user whos locale is being deleted
309
				 */
310
				do_action( 'jetpack_sync_user_locale_delete', $user_id );
311
			} else {
312
				/**
313
				 * Allow listeners to listen for user local changes
314
				 *
315
				 * @since 4.8.0
316
				 *
317
				 * @param int $user_id - The ID of the user whos locale is being changed
318
				 * @param int $value - The value of the new locale
319
				 */
320
				do_action( 'jetpack_sync_user_locale', $user_id, $value );
321
			}
322
		}
323
		$this->save_user_cap_handler( $meta_id, $user_id, $meta_key, $value );
324
	}
325
326
	function save_user_cap_handler( $meta_id, $user_id, $meta_key, $capabilities ) {
327
		// if a user is currently being removed as a member of this blog, we don't fire the event
328
		if ( current_filter() === 'deleted_user_meta' ) {
329
			return;
330
		}
331
332
		// Since we are currently only caring about capabilities at this point don't need to save the user info at this save the user info at this point.
333
		if ( current_filter() === 'added_user_meta' ) {
334
			return;
335
		}
336
337
		//The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both
338
		if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
339
			return;
340
		}
341
		$user = get_user_by( 'id', $user_id );
342
		if ( $meta_key === $user->cap_key  ) {
343
344
			/**
345
			 * Fires when the client needs to sync an updated user
346
			 *
347
			 * @since 4.2.0
348
			 *
349
			 * @param object The Sanitized WP_User object
350
		     * @param array state Since 5.8
351
			 */
352
			do_action( 'jetpack_sync_save_user', $this->sanitize_user( $user ),
353
				array( 'capabilities_action' => current_filter(), 'capabilities' => $capabilities )
354
			);
355
		}
356
	}
357
358
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
359
		global $wpdb;
360
361
		return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_users', $wpdb->usermeta, 'user_id', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
362
	}
363
364
	public function estimate_full_sync_actions( $config ) {
365
		global $wpdb;
366
367
		$query = "SELECT count(*) FROM $wpdb->usermeta";
368
369
		if ( $where_sql = $this->get_where_sql( $config ) ) {
370
			$query .= ' WHERE ' . $where_sql;
371
		}
372
373
		$count = $wpdb->get_var( $query );
374
375
		return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
376
	}
377
378 View Code Duplication
	private function get_where_sql( $config ) {
379
		global $wpdb;
380
381
		$query = "meta_key = '{$wpdb->prefix}capabilities'";
382
383
		// config is a list of user IDs to sync
384
		if ( is_array( $config ) ) {
385
			$query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
386
		}
387
388
		return $query;
389
	}
390
391
	function get_full_sync_actions() {
392
		return array( 'jetpack_full_sync_users' );
393
	}
394
395
	function get_initial_sync_user_config() {
396
		global $wpdb;
397
398
		$user_ids = $wpdb->get_col( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}user_level' AND meta_value > 0 LIMIT " . ( self::MAX_INITIAL_SYNC_USERS + 1 ) );
399
400
		if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) {
401
			return $user_ids;
402
		} else {
403
			return false;
404
		}
405
	}
406
407
	public function expand_users( $args ) {
408
		$user_ids = $args[0];
409
410
		return array_map( array( $this, 'sanitize_user_and_expand' ), get_users( array( 'include' => $user_ids ) ) );
411
	}
412
413
	public function remove_user_from_blog_handler( $user_id, $blog_id ) {
414
		//User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114
415
		if ( $this->is_add_new_user_to_blog() ) {
416
			return;
417
		}
418
419
		$reassigned_user_id = $this->get_reassigned_network_user_id();
420
421
		//Note that we are in the context of the blog the user is removed from, see https://github.com/WordPress/WordPress/blob/473e1ba73bc5c18c72d7f288447503713d518790/wp-includes/ms-functions.php#L233
422
		/**
423
		 * Fires when a user is removed from a blog on a multisite installation
424
		 *
425
		 * @since 5.4.0
426
		 *
427
		 * @param int $user_id - ID of the removed user
428
		 * @param int $reassigned_user_id - ID of the user the removed user's posts is reassigned to (if any)
429
		 */
430
		do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id );
431
	}
432
433
	private function is_add_new_user_to_blog() {
434
		return Jetpack::is_function_in_backtrace( 'add_new_user_to_blog' );
435
	}
436
437
	private function is_add_user_to_blog() {
438
		return Jetpack::is_function_in_backtrace( 'add_user_to_blog' );
439
	}
440
441
	private function is_create_user() {
442
		$functions = array(
443
			'add_new_user_to_blog', // Used to suppress jetpack_sync_save_user in save_user_cap_handler when user registered on multi site
444
			'wp_create_user', // Used to suppress jetpack_sync_save_user in save_user_role_handler when user registered on multi site
445
			'wp_insert_user', // Used to suppress jetpack_sync_save_user in save_user_cap_handler and save_user_role_handler when user registered on single site
446
		);
447
448
		return Jetpack::is_function_in_backtrace( $functions );
449
	}
450
451
	private function get_reassigned_network_user_id() {
452
		$backtrace = debug_backtrace( false );
453
		foreach ( $backtrace as $call ) {
454
			if (
455
				'remove_user_from_blog' === $call['function'] &&
456
				3 === count( $call['args'] )
457
			) {
458
				return $call['args'][2];
459
			}
460
		}
461
462
		return false;
463
	}
464
}
465