Completed
Push — add/8181-enhanced-google-analy... ( 4a1eee...8688b6 )
by
unknown
09:36
created

Jetpack_Sync_Module_Users::get_user()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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