Completed
Push — update/sync-save-user-locale ( 8cf80f )
by
unknown
40:16 queued 30:52
created

Jetpack_Sync_Module_Users   C

Complexity

Total Complexity 71

Size/Duplication

Total Lines 425
Duplicated Lines 10.12 %

Coupling/Cohesion

Components 3
Dependencies 3

Importance

Changes 0
Metric Value
dl 43
loc 425
rs 5.5904
c 0
b 0
f 0
wmc 71
lcom 3
cbo 3

32 Methods

Rating   Name   Duplication   Size   Complexity  
A init_full_sync_listeners() 0 3 1
A name() 0 3 1
A get_object_by_id() 0 7 3
A sanitize_user_and_expand() 0 5 1
B init_listeners() 0 37 1
A get_real_user_capabilities() 0 12 4
A init_before_send() 0 12 1
B get_user() 0 9 5
A sanitize_user() 0 10 3
A expand_user() 0 17 4
A expand_action() 0 10 2
A expand_save_user_action() 0 14 3
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 35 6
A save_user_role_handler() 0 20 3
A maybe_save_user_meta() 0 22 3
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
A get_reassigned_network_user_id() 0 13 4

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_action' ) );
65
		add_filter( 'jetpack_sync_before_send_jetpack_sync_register_user', array( $this, 'expand_action' ) );
66
67
		add_filter( 'jetpack_sync_before_send_jetpack_sync_save_user', array( $this, 'expand_save_user_action' ) );
68
69
		add_filter( 'jetpack_sync_before_send_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
70
		add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
71
72
		// full sync
73
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
74
	}
75
76
	private function get_user( $user ) {
77
		if ( $user && ! is_object( $user ) && is_numeric( $user ) ) {
78
			$user = get_user_by( 'id', $user );
79
		}
80
		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...
81
			return $user;
82
		}
83
		return null;
84
	}
85
86
	public function sanitize_user( $user ) {
87
		$user = $this->get_user( $user );
88
		// this create a new user object and stops the passing of the object by reference.
89
		$user = unserialize( serialize( $user ) );
90
91
		if ( is_object( $user ) && is_object( $user->data ) ) {
92
			unset( $user->data->user_pass );
93
		}
94
		return $user;
95
	}
96
97
	public function expand_user( $user ) {
98
		if ( ! is_object( $user ) ) {
99
			return null;
100
		}
101
		$user->allowed_mime_types = get_allowed_mime_types( $user );
102
		$user->allcaps = $this->get_real_user_capabilities( $user );
103
104
		if ( function_exists( 'get_user_locale' ) ) {
105
106
			// Only set the user locale if it is different from the site local
107
			if ( get_locale() !== get_user_locale( $user->ID ) ) {
108
				$user->locale = get_user_locale( $user->ID );
109
			}
110
		}
111
112
		return $user;
113
	}
114
115
	public function sanitize_user_and_expand( $user ) {
116
		$user = $this->get_user( $user );
117
		$user = $this->expand_user( $user );
118
		return $this->sanitize_user( $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_action( $args ) {
135
		// the first argument is always the user
136
		list( $user ) = $args;
137
		if ( $user ) {
138
			$args[0] = $this->sanitize_user_and_expand( $user );
139
			return $args;
140
		}
141
142
		return false;
143
	}
144
145
	public function expand_save_user_action( $args ) {
146
		$default_flags = array(
147
			'password_changed' => false,
148
			'role_changed'     => false,
149
			'previous_role'    => false,
150
			'locale_changed'   => false,
151
			'capabilities_changed' => false,
152
		);
153
154
		$flags         = ( isset( $args[1] ) && is_array( $args[1] ) ) ? $args[1] : array();
155
		$args[1]       = wp_parse_args( $flags, $default_flags );
156
157
		return $this->expand_action( $args );
158
	}
159
160
	public function expand_login_username( $args ) {
161
		list( $login, $user ) = $args;
162
		$user = $this->sanitize_user( $user );
163
164
		return array( $login, $user );
165
	}
166
167
	public function expand_logout_username( $args, $user_id ) {
168
		$user  = get_userdata( $user_id );
169
		$user  = $this->sanitize_user( $user );
170
171
		$login = '';
172
		if ( is_object( $user ) && is_object( $user->data ) ) {
173
			$login = $user->data->user_login;
174
		}
175
		// if we don't have a user here lets not send anything.
176
		if ( empty( $login ) ) {
177
			return false;
178
		}
179
180
		return array( $login, $user );
181
	}
182
183
	public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) {
184
		$is_multisite = is_multisite();
185
		/**
186
		 * Fires when a user is deleted on a site
187
		 *
188
		 * @since 5.4.0
189
		 *
190
		 * @param int $deleted_user_id - ID of the deleted user
191
		 * @param int $reassigned_user_id - ID of the user the deleted user's posts is reassigned to (if any)
192
		 * @param bool $is_multisite - Whether this site is a multisite installation
193
		 */
194
		do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite );
195
	}
196
197
	public function edited_user_handler( $user_id ) {
198
		/**
199
		 * Fires when a user is edited on a site
200
		 *
201
		 * @since 5.4.0
202
		 *
203
		 * @param int $user_id - ID of the edited user
204
		 */
205
		do_action( 'jetpack_user_edited', $user_id );
206
	}
207
208 View Code Duplication
	function user_register_handler( $user_id, $old_user_data = null ) {
209
		// ensure we only sync users who are members of the current blog
210
		if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
211
			return;
212
		}
213
		$raw_user = get_user_by( 'id', $user_id );
214
		$user = $this->sanitize_user( $raw_user );
215
216
		if ( 'user_register' === current_filter() ) {
217
			/**
218
			 * Fires when a new user is registered on a site
219
			 *
220
			 * @since 4.9.0
221
			 *
222
			 * @param object The WP_User object
223
			 */
224
			do_action( 'jetpack_sync_register_user', $user );
225
226
			return;
227
		}
228
	}
229
230 View Code Duplication
	function add_user_to_blog_handler( $user_id, $old_user_data = null ) {
231
		// ensure we only sync users who are members of the current blog
232
		if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
233
			return;
234
		}
235
		$raw_user = get_user_by( 'id', $user_id );
236
		$user = $this->sanitize_user( $raw_user );
237
238
		do_action( 'jetpack_sync_add_user', $user );
239
	}
240
241
	function save_user_handler( $user_id, $old_user_data = null ) {
242
		// ensure we only sync users who are members of the current blog
243
		if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
244
			return;
245
		}
246
		$user = get_user_by( 'id', $user_id );
247
		$user_password_changed = false;
248
249
		// Older versions of WP don't pass the old_user_data in ->data
250
		if ( isset( $old_user_data->data ) ) {
251
			$old_user = $old_user_data->data;
252
		} else {
253
			$old_user = $old_user_data;
254
		}
255
256
		$role_changed = isset( $this->previous_role[ $user_id ] ) ? $this->previous_role[ $user_id ] : false;
257
258
		if ( $old_user !== null && $user->user_pass !== $old_user->user_pass ) {
259
			$user_password_changed = true;
260
		}
261
262
		/**
263
		 * Fires when the client needs to sync an updated user
264
		 *
265
		 * @since 4.2.0
266
		 *
267
		 * @param object The WP_User object
268
		 * @param array state - New since 5.8.0
269
		 */
270
		do_action( 'jetpack_sync_save_user', $user, array(
271
			'password_changed' => $user_password_changed,
272
			'role_changed' => (bool) $role_changed,
273
			'previous_role' => $role_changed,
274
			) );
275
	}
276
277
	function save_user_role_handler( $user_id, $role, $old_roles = null ) {
278
		//The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both
279
		if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
280
			$this->previous_role[ $user_id ] = $old_roles;
281
			return;
282
		}
283
284
		$user = get_user_by( 'id', $user_id );
285
		/**
286
		 * Fires when the client needs to sync an updated user
287
		 *
288
		 * @since 4.2.0
289
		 *
290
		 * @param object The WP_User object
291
	 	 * @param array state
292
		 */
293
		do_action( 'jetpack_sync_save_user', $user, array(
294
			'role_changed' => true,
295
			'previous_role' => $old_roles ) );
296
	}
297
298
	function maybe_save_user_meta( $meta_id, $user_id, $meta_key, $value ) {
299
		$flags = array();
300
301
		if ( $meta_key === 'locale' ) {
302
			$flags[ 'locale_changed' ] = true;
303
		}
304
305
		$user = get_user_by( 'id', $user_id );
306
		if ( $meta_key === $user->cap_key  ) {
307
			$flags[ 'capabilities_changed' ] = true;
308
		}
309
310
		/**
311
		 * Fires when the client needs to sync an updated user
312
		 *
313
		 * @since 4.2.0
314
		 *
315
		 * @param object The Sanitized WP_User object
316
		 * @param array state Since 5.8
317
		 */
318
		do_action( 'jetpack_sync_save_user', $user, 	$flags );
319
	}
320
321
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
322
		global $wpdb;
323
324
		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 );
325
	}
326
327
	public function estimate_full_sync_actions( $config ) {
328
		global $wpdb;
329
330
		$query = "SELECT count(*) FROM $wpdb->usermeta";
331
332
		if ( $where_sql = $this->get_where_sql( $config ) ) {
333
			$query .= ' WHERE ' . $where_sql;
334
		}
335
336
		$count = $wpdb->get_var( $query );
337
338
		return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
339
	}
340
341 View Code Duplication
	private function get_where_sql( $config ) {
342
		global $wpdb;
343
344
		$query = "meta_key = '{$wpdb->prefix}capabilities'";
345
346
		// config is a list of user IDs to sync
347
		if ( is_array( $config ) ) {
348
			$query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
349
		}
350
351
		return $query;
352
	}
353
354
	function get_full_sync_actions() {
355
		return array( 'jetpack_full_sync_users' );
356
	}
357
358
	function get_initial_sync_user_config() {
359
		global $wpdb;
360
361
		$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 ) );
362
363
		if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) {
364
			return $user_ids;
365
		} else {
366
			return false;
367
		}
368
	}
369
370
	public function expand_users( $args ) {
371
		$user_ids = $args[0];
372
373
		return array_map( array( $this, 'sanitize_user_and_expand' ), get_users( array( 'include' => $user_ids ) ) );
374
	}
375
376
	public function remove_user_from_blog_handler( $user_id, $blog_id ) {
377
		//User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114
378
		if ( $this->is_add_new_user_to_blog() ) {
379
			return;
380
		}
381
382
		$reassigned_user_id = $this->get_reassigned_network_user_id();
383
384
		//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
385
		/**
386
		 * Fires when a user is removed from a blog on a multisite installation
387
		 *
388
		 * @since 5.4.0
389
		 *
390
		 * @param int $user_id - ID of the removed user
391
		 * @param int $reassigned_user_id - ID of the user the removed user's posts is reassigned to (if any)
392
		 */
393
		do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id );
394
	}
395
396
	private function is_add_new_user_to_blog() {
397
		return Jetpack::is_function_in_backtrace( 'add_new_user_to_blog' );
398
	}
399
400
	private function is_add_user_to_blog() {
401
		return Jetpack::is_function_in_backtrace( 'add_user_to_blog' );
402
	}
403
404
	private function is_create_user() {
405
		$functions = array(
406
			'add_new_user_to_blog', // Used to suppress jetpack_sync_save_user in save_user_cap_handler when user registered on multi site
407
			'wp_create_user', // Used to suppress jetpack_sync_save_user in save_user_role_handler when user registered on multi site
408
			'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
409
		);
410
411
		return Jetpack::is_function_in_backtrace( $functions );
412
	}
413
414
	private function get_reassigned_network_user_id() {
415
		$backtrace = debug_backtrace( false );
416
		foreach ( $backtrace as $call ) {
417
			if (
418
				'remove_user_from_blog' === $call['function'] &&
419
				3 === count( $call['args'] )
420
			) {
421
				return $call['args'][2];
422
			}
423
		}
424
425
		return false;
426
	}
427
}
428