Completed
Push — update/sync-users-enqueue-orde... ( d17611...0c48e3 )
by
unknown
29:40
created

Jetpack_Sync_Module_Users::get_where_sql()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 12
loc 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
class Jetpack_Sync_Module_Users extends Jetpack_Sync_Module {
4
	const MAX_INITIAL_SYNC_USERS = 100;
5
6
	static $callable;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $callable.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
7
8
	// Stores things that happen inside wp_insert_user so we sync them after the wp_insert_user call
9
	static $calls_within_wp_insert_user = array();
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $calls_within_wp_insert_user.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
10
11
	function name() {
12
		return 'users';
13
	}
14
15
	// this is here to support the backfill API
16
	public function get_object_by_id( $object_type, $id ) {
17
		if ( $object_type === 'user' && $user = get_user_by( 'id', intval( $id ) ) ) {
18
			return $user;
19
		}
20
21
		return false;
22
	}
23
24
	public function init_listeners( $callable ) {
25
		self::$callable = $callable;
26
27
		add_action( 'user_register', array( $this, 'do_delayed_calls' ), 99 );
28
		add_action( 'profile_update', array( $this, 'do_delayed_calls' ), 99 );
29
30
		// users
31
		add_action( 'user_register', array( $this, 'save_user_handler' ) );
32
		add_action( 'profile_update', array( $this, 'save_user_handler' ), 10, 2 );
33
		add_action( 'add_user_to_blog', array( $this, 'save_user_handler' ) );
34
		add_action( 'jetpack_sync_add_user', $callable, 10, 2 );
35
		add_action( 'jetpack_sync_register_user', $callable, 10, 2 );
36
		add_action( 'jetpack_sync_save_user', array( $this, 'delay_if_within_wp_insert_user' ) );
37
38
		add_action( 'jetpack_sync_change_role_user', array( $this, 'delay_if_within_wp_insert_user' ), 10, 3 );
39
40
		add_action( 'jetpack_sync_user_locale', array( $this, 'delay_if_within_wp_insert_user' ), 10, 2 );
41
42
		add_action( 'jetpack_sync_user_locale_delete', $callable, 10, 1 );
43
44
		add_action( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 );
45
		add_action( 'jetpack_deleted_user', $callable, 10, 3 );
46
		add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 );
47
		add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 );
48
49
		// user roles
50
		add_action( 'add_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
51
		add_action( 'set_user_role', array( $this, 'save_user_role_handler' ), 10, 3 );
52
		add_action( 'remove_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
53
54
		// user capabilities
55
		add_action( 'added_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
56
		add_action( 'updated_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
57
		add_action( 'deleted_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
58
59
		// user authentication
60
		add_action( 'wp_login', $callable, 10, 2 );
61
		add_action( 'wp_logout', $callable, 10, 0 );
62
		add_action( 'wp_masterbar_logout', $callable, 10, 0 );
63
	}
64
65
	public function delay_if_within_wp_insert_user() {
66
		if ( ! Jetpack::is_function_in_backtrace( 'do_delayed_calls' ) ) {
67
			if ( Jetpack::is_function_in_backtrace( 'wp_insert_user' ) ) {
68
				self::$calls_within_wp_insert_user[ current_action() ][] = func_get_args();
69
70
				return;
71
			}
72
		}
73
		call_user_func_array( self::$callable, func_get_args() );
74
	}
75
76
	public function do_delayed_calls() {
77
		// We want the save user action to be synced first
78
		if ( isset( self::$calls_within_wp_insert_user['jetpack_sync_save_user'] ) ) {
79
			$jetpack_sync_save_user = self::$calls_within_wp_insert_user['jetpack_sync_save_user'];
80
			unset( self::$calls_within_wp_insert_user['jetpack_sync_save_user'] );
81
			self::$calls_within_wp_insert_user = array_merge( array( 'jetpack_sync_save_user' => $jetpack_sync_save_user), self::$calls_within_wp_insert_user );
82
		}
83
		foreach ( self::$calls_within_wp_insert_user as $action => $calls ) {
84
			foreach ( $calls as $arguments ) {
85
				if ( 'user_register' === current_filter() ) {
86
					if ( in_array( $action,
87
						array(
88
							'jetpack_sync_change_role_user',
89
							'jetpack_sync_user_locale',
90
						), true ) ) {
91
						break;
92
					}
93
				}
94
95
				//	do_action( $action, $arguments[0], $arguments[1], ... );
96
				call_user_func_array( 'do_action', array_merge( array( $action ), $arguments ) );
97
			}
98
		}
99
	}
100
101
	public function init_full_sync_listeners( $callable ) {
102
		add_action( 'jetpack_full_sync_users', $callable );
103
	}
104
105
	public function init_before_send() {
106
		add_filter( 'jetpack_sync_before_send_jetpack_sync_add_user', array( $this, 'expand_user' ) );
107
		add_filter( 'jetpack_sync_before_send_jetpack_sync_register_user', array( $this, 'expand_user' ) );
108
		add_filter( 'jetpack_sync_before_send_jetpack_sync_save_user', array( $this, 'expand_user' ), 10, 2 );
109
110
		add_filter( 'jetpack_sync_before_send_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
111
		add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
112
113
		// full sync
114
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
115
	}
116
117
	public function sanitize_user_and_expand( $user ) {
118
		$user = $this->get_user( $user );
119
		$user = $this->add_to_user( $user );
120
		return $this->sanitize_user( $user );
121
	}
122
123
	private function get_user( $user ) {
124
		error_log( 'get USEEEEEER');
125
		error_log( print_r($user,1));
126
		if ( $user && ! is_object( $user ) && is_numeric( $user ) ) {
127
			$user = get_user_by( 'id', $user );
128
		}
129
		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...
130
			return $user;
131
		}
132
		return null;
133
	}
134
135
	public function sanitize_user( $user ) {
136
		$user = $this->get_user( $user );
137
		// this create a new user object and stops the passing of the object by reference.
138
		$user = unserialize( serialize( $user ) );
139
140
		if ( is_object( $user ) && is_object( $user->data ) ) {
141
			unset( $user->data->user_pass );
142
			unset( $user->data->user_activation_key );
143
		}
144
		if ( $user ) {
145
			$user->allcaps = $this->get_real_user_capabilities( $user );
146
		}
147
		return $user;
148
	}
149
150
	public function add_to_user( $user ) {
151
		$user = $this->get_user( $user );
152
		if ( ! is_object( $user ) ) {
153
			return null;
154
		}
155
		$user->allowed_mime_types = get_allowed_mime_types( $user );
156
157
		if ( function_exists( 'get_user_locale' ) ) {
158
159
			// Only set the user locale if it is different from the site local
160
			if ( get_locale() !== get_user_locale( $user->ID ) ) {
161
				$user->locale = get_user_locale( $user->ID );
162
			}
163
		}
164
165
		return $user;
166
	}
167
168
	public function get_real_user_capabilities( $user ) {
169
		$user_capabilities = array();
170
		if ( is_wp_error( $user ) ) {
171
			return $user_capabilities;
172
		}
173
		foreach( Jetpack_Sync_Defaults::get_capabilities_whitelist() as $capability ) {
174
			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...
175
				$user_capabilities[ $capability ] = true;
176
			}
177
		}
178
		return $user_capabilities;
179
	}
180
181
	public function expand_user( $args ) {
182
		list( $user ) = $args;
183
184
		if ( $user ) {
185
			$user = $this->add_to_user( $user );
186
			$user = $this->sanitize_user( $user );
187
188
			return array( $user );
189
		}
190
191
		return false;
192
	}
193
194
	public function expand_login_username( $args ) {
195
		list( $login, $user ) = $args;
196
		$user = $this->sanitize_user( $user );
197
198
		return array( $login, $user );
199
	}
200
201
	public function expand_logout_username( $args, $user_id ) {
202
		$user  = get_userdata( $user_id );
203
		$user  = $this->sanitize_user( $user );
204
		
205
		$login = '';
206
		if ( is_object( $user ) && is_object( $user->data ) ) {
207
			$login = $user->data->user_login;
208
		}
209
		// if we don't have a user here lets not send anything.
210
		if ( empty( $login ) ) {
211
			return false;
212
		}
213
214
		return array( $login, $user );
215
	}
216
217
	public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) {
218
		$is_multisite = is_multisite();
219
		/**
220
		 * Fires when a user is deleted on a site
221
		 *
222
		 * @since 5.4.0
223
		 *
224
		 * @param int $deleted_user_id - ID of the deleted user
225
		 * @param int $reassigned_user_id - ID of the user the deleted user's posts is reassigned to (if any)
226
		 * @param bool $is_multisite - Whether this site is a multisite installation
227
		 */
228
		do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite );
229
	}
230
231
	public function edited_user_handler( $user_id ) {
232
		/**
233
		 * Fires when a user is edited on a site
234
		 *
235
		 * @since 5.4.0
236
		 *
237
		 * @param int $user_id - ID of the edited user
238
		 */
239
		do_action( 'jetpack_user_edited', $user_id );
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
248
		$user = $this->sanitize_user( get_user_by( 'id', $user_id ) );
249
250
		// Older versions of WP don't pass the old_user_data in ->data
251
		if ( isset( $old_user_data->data ) ) {
252
			$old_user = $old_user_data->data;
253
		} else {
254
			$old_user = $old_user_data;
255
		}
256
257
		if ( $old_user !== null ) {
258
			unset( $old_user->user_pass );
259
			if ( serialize( $old_user ) === serialize( $user->data ) ) {
260
				return;
261
			}
262
		}
263
264
		if ( 'user_register' === current_filter() ) {
265
			/**
266
			 * Fires when a new user is registered on a site
267
			 *
268
			 * @since 4.9.0
269
			 *
270
			 * @param object The WP_User object
271
			 */
272
			do_action( 'jetpack_sync_register_user', $user );
273
274
			return;
275
		}
276
		/* MU Sites add users instead of register them to sites */
277
		if ( 'add_user_to_blog' === current_filter() ) {
278
			/**
279
			 * Fires when a new user is added to a site. (WordPress Multisite)
280
			 *
281
			 * @since 4.9.0
282
			 *
283
			 * @param object The WP_User object
284
			 */
285
			do_action( 'jetpack_sync_add_user', $user );
286
287
			return;
288
		}
289
290
		/**
291
		 * Fires when the client needs to sync an updated user
292
		 *
293
		 * @since 4.2.0
294
		 *
295
		 * @param object The WP_User object
296
		 */
297
		do_action( 'jetpack_sync_save_user', $user );
298
	}
299
300
	function save_user_role_handler( $user_id, $role, $old_roles = null ) {
301
		/**
302
		 * Fires when the client needs to sync an updated user
303
		 *
304
		 * @since 4.2.0
305
		 *
306
		 * @param object The WP_User object
307
		 */
308
		do_action( 'jetpack_sync_change_role_user', $user_id, $role, $old_roles );
309
	}
310
311
	function maybe_save_user_meta( $meta_id, $user_id, $meta_key, $value ) {
312
		if ( $meta_key === 'locale' ) {
313
			if ( current_filter() === 'deleted_user_meta' ) {
314
				/**
315
				 * Allow listeners to listen for user local delete changes
316
				 *
317
				 * @since 4.8.0
318
				 *
319
				 * @param int $user_id - The ID of the user whos locale is being deleted
320
				 */
321
				do_action( 'jetpack_sync_user_locale_delete', $user_id );
322
			} else {
323
				/**
324
				 * Allow listeners to listen for user local changes
325
				 *
326
				 * @since 4.8.0
327
				 *
328
				 * @param int $user_id - The ID of the user whos locale is being changed
329
				 * @param int $value - The value of the new locale
330
				 */
331
				do_action( 'jetpack_sync_user_locale', $user_id, $value );
332
			}
333
		}
334
		$this->save_user_cap_handler( $meta_id, $user_id, $meta_key, $value );
335
	}
336
337
	function save_user_cap_handler( $meta_id, $user_id, $meta_key, $capabilities ) {
338
		// if a user is currently being removed as a member of this blog, we don't fire the event
339
		if ( current_filter() === 'deleted_user_meta'
340
		     &&
341
		     preg_match( '/capabilities|user_level/', $meta_key )
342
		     &&
343
		     ! is_user_member_of_blog( $user_id, get_current_blog_id() )
344
		) {
345
			return;
346
		}
347
348
		$user = get_user_by( 'id', $user_id );
349
		if ( $meta_key === $user->cap_key ) {
350
			/**
351
			 * Fires when the client needs to sync an updated user
352
			 *
353
			 * @since 4.2.0
354
			 *
355
			 * @param object The Sanitized WP_User object
356
			 */
357
			do_action( 'jetpack_sync_edit_user', $user );
358
		}
359
	}
360
361
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
362
		global $wpdb;
363
364
		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 );
365
	}
366
367
	public function estimate_full_sync_actions( $config ) {
368
		global $wpdb;
369
370
		$query = "SELECT count(*) FROM $wpdb->usermeta";
371
372
		if ( $where_sql = $this->get_where_sql( $config ) ) {
373
			$query .= ' WHERE ' . $where_sql;
374
		}
375
376
		$count = $wpdb->get_var( $query );
377
378
		return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
379
	}
380
381 View Code Duplication
	private function get_where_sql( $config ) {
382
		global $wpdb;
383
384
		$query = "meta_key = '{$wpdb->prefix}capabilities'";
385
386
		// config is a list of user IDs to sync
387
		if ( is_array( $config ) ) {
388
			$query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
389
		}
390
391
		return $query;
392
	}
393
394
	function get_full_sync_actions() {
395
		return array( 'jetpack_full_sync_users' );
396
	}
397
398
	function get_initial_sync_user_config() {
399
		global $wpdb;
400
401
		$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 ) );
402
403
		if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) {
404
			return $user_ids;
405
		} else {
406
			return false;
407
		}
408
	}
409
410
	public function expand_users( $args ) {
411
		$user_ids = $args[0];
412
413
		return array_map( array( $this, 'sanitize_user_and_expand' ), get_users( array( 'include' => $user_ids ) ) );
414
	}
415
416
	public function remove_user_from_blog_handler( $user_id, $blog_id ) {
417
		//User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114
418
		if ( $this->is_add_new_user_to_blog() ) {
419
			return;
420
		}
421
422
		$reassigned_user_id = $this->get_reassigned_network_user_id();
423
424
		//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
425
		/**
426
		 * Fires when a user is removed from a blog on a multisite installation
427
		 *
428
		 * @since 5.4.0
429
		 *
430
		 * @param int $user_id - ID of the removed user
431
		 * @param int $reassigned_user_id - ID of the user the removed user's posts is reassigned to (if any)
432
		 */
433
		do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id );
434
	}
435
436
	private function is_add_new_user_to_blog() {
437
		return Jetpack::is_function_in_backtrace( 'add_new_user_to_blog' );
438
	}
439
440
	private function get_reassigned_network_user_id() {
441
		$backtrace = debug_backtrace( false );
442
		foreach ( $backtrace as $call ) {
443
			if (
444
				'remove_user_from_blog' === $call['function'] &&
445
				3 === count( $call['args'] )
446
			) {
447
				return $call['args'][2];
448
			}
449
		}
450
451
		return false;
452
	}
453
}
454