Completed
Push — update/move-xmlrpc-sync-object... ( 3e74bf )
by Marin
06:18
created

Sender   F

Complexity

Total Complexity 80

Size/Duplication

Total Lines 782
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 11

Importance

Changes 0
Metric Value
dl 0
loc 782
rs 1.818
c 0
b 0
f 0
wmc 80
lcom 3
cbo 11

36 Methods

Rating   Name   Duplication   Size   Complexity  
A get_instance() 0 7 2
A __construct() 0 4 1
A maybe_clear_user_from_token() 0 5 2
A get_next_sync_time() 0 3 1
A set_next_sync_time() 0 3 1
A do_full_sync() 0 7 2
A continue_full_sync_enqueue() 0 13 4
A do_sync() 0 3 1
B do_sync_and_set_delays() 0 35 8
B get_items_to_send() 0 47 7
A fastcgi_finish_request() 0 5 3
D do_sync_for_queue() 0 95 15
A init() 0 8 2
A maybe_set_user_from_token() 0 11 5
A sync_object() 0 9 1
A register_jetpack_xmlrpc_methods() 0 7 2
A get_sync_queue() 0 3 1
A get_full_sync_queue() 0 3 1
A get_codec() 0 3 1
A set_codec() 0 7 2
A send_checksum() 0 4 1
A reset_sync_queue() 0 3 1
A reset_full_sync_queue() 0 3 1
A set_dequeue_max_bytes() 0 3 1
A set_upload_max_bytes() 0 3 1
A set_upload_max_rows() 0 3 1
A set_sync_wait_time() 0 3 1
A get_sync_wait_time() 0 3 1
A set_enqueue_wait_time() 0 3 1
A get_enqueue_wait_time() 0 3 1
A set_sync_wait_threshold() 0 3 1
A get_sync_wait_threshold() 0 3 1
A set_max_dequeue_time() 0 3 1
A set_defaults() 0 16 1
A reset_data() 0 14 3
A uninstall() 0 11 1

How to fix   Complexity   

Complex Class

Complex classes like Sender 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 Sender, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Sync sender.
4
 *
5
 * @package automattic/jetpack-sync
6
 */
7
8
namespace Automattic\Jetpack\Sync;
9
10
use Automattic\Jetpack\Constants;
11
12
/**
13
 * This class grabs pending actions from the queue and sends them
14
 */
15
class Sender {
16
	/**
17
	 * Name of the option that stores the time of the next sync.
18
	 *
19
	 * @access public
20
	 *
21
	 * @var string
22
	 */
23
	const NEXT_SYNC_TIME_OPTION_NAME = 'jetpack_next_sync_time';
24
25
	/**
26
	 * Sync timeout after a WPCOM error.
27
	 *
28
	 * @access public
29
	 *
30
	 * @var int
31
	 */
32
	const WPCOM_ERROR_SYNC_DELAY = 60;
33
34
	/**
35
	 * Sync timeout after a queue has been locked.
36
	 *
37
	 * @access public
38
	 *
39
	 * @var int
40
	 */
41
	const QUEUE_LOCKED_SYNC_DELAY = 10;
42
43
	/**
44
	 * Maximum bytes to checkout without exceeding the memory limit.
45
	 *
46
	 * @access private
47
	 *
48
	 * @var int
49
	 */
50
	private $dequeue_max_bytes;
51
52
	/**
53
	 * Maximum bytes in a single encoded item.
54
	 *
55
	 * @access private
56
	 *
57
	 * @var int
58
	 */
59
	private $upload_max_bytes;
60
61
	/**
62
	 * Maximum number of sync items in a single action.
63
	 *
64
	 * @access private
65
	 *
66
	 * @var int
67
	 */
68
	private $upload_max_rows;
69
70
	/**
71
	 * Maximum time for perfirming a checkout of items from the queue (in seconds).
72
	 *
73
	 * @access private
74
	 *
75
	 * @var int
76
	 */
77
	private $max_dequeue_time;
78
79
	/**
80
	 * How many seconds to wait after sending sync items after exceeding the sync wait threshold (in seconds).
81
	 *
82
	 * @access private
83
	 *
84
	 * @var int
85
	 */
86
	private $sync_wait_time;
87
88
	/**
89
	 * How much maximum time to wait for the checkout to finish (in seconds).
90
	 *
91
	 * @access private
92
	 *
93
	 * @var int
94
	 */
95
	private $sync_wait_threshold;
96
97
	/**
98
	 * How much maximum time to wait for the sync items to be queued for sending (in seconds).
99
	 *
100
	 * @access private
101
	 *
102
	 * @var int
103
	 */
104
	private $enqueue_wait_time;
105
106
	/**
107
	 * Incremental sync queue object.
108
	 *
109
	 * @access private
110
	 *
111
	 * @var Automattic\Jetpack\Sync\Queue
112
	 */
113
	private $sync_queue;
114
115
	/**
116
	 * Full sync queue object.
117
	 *
118
	 * @access private
119
	 *
120
	 * @var Automattic\Jetpack\Sync\Queue
121
	 */
122
	private $full_sync_queue;
123
124
	/**
125
	 * Codec object for encoding and decoding sync items.
126
	 *
127
	 * @access private
128
	 *
129
	 * @var Automattic\Jetpack\Sync\Codec_Interface
130
	 */
131
	private $codec;
132
133
	/**
134
	 * The current user before we change or clear it.
135
	 *
136
	 * @access private
137
	 *
138
	 * @var \WP_User
139
	 */
140
	private $old_user;
141
142
	/**
143
	 * Container for the singleton instance of this class.
144
	 *
145
	 * @access private
146
	 * @static
147
	 *
148
	 * @var Automattic\Jetpack\Sync\Sender
149
	 */
150
	private static $instance;
151
152
	/**
153
	 * Retrieve the singleton instance of this class.
154
	 *
155
	 * @access public
156
	 * @static
157
	 *
158
	 * @return Automattic\Jetpack\Sync\Sender
159
	 */
160
	public static function get_instance() {
161
		if ( null === self::$instance ) {
162
			self::$instance = new self();
0 ignored issues
show
Documentation Bug introduced by
It seems like new self() of type object<Automattic\Jetpack\Sync\Sender> is incompatible with the declared type object<Automattic\Jetpac...ic\Jetpack\Sync\Sender> of property $instance.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
163
		}
164
165
		return self::$instance;
166
	}
167
168
	/**
169
	 * Constructor.
170
	 * This is necessary because you can't use "new" when you declare instance properties >:(
171
	 *
172
	 * @access protected
173
	 * @static
174
	 */
175
	protected function __construct() {
176
		$this->set_defaults();
177
		$this->init();
178
	}
179
180
	/**
181
	 * Initialize the sender.
182
	 * Prepares the current user and initializes all sync modules.
183
	 *
184
	 * @access private
185
	 */
186
	private function init() {
187
		add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_set_user_from_token' ), 1 );
188
		add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_clear_user_from_token' ), 20 );
189
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'register_jetpack_xmlrpc_methods' ), 10, 3 );
190
		foreach ( Modules::get_modules() as $module ) {
0 ignored issues
show
Bug introduced by
The expression \Automattic\Jetpack\Sync\Modules::get_modules() of type array|null 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...
191
			$module->init_before_send();
192
		}
193
	}
194
195
	/**
196
	 * Detect if this is a XMLRPC request with a valid signature.
197
	 * If so, changes the user to the new one.
198
	 *
199
	 * @access public
200
	 */
201
	public function maybe_set_user_from_token() {
202
		$verified_user = \Jetpack::connection()->verify_xml_rpc_signature();
203
		if ( Constants::is_true( 'XMLRPC_REQUEST' ) &&
204
			! is_wp_error( $verified_user )
205
			&& $verified_user
206
		) {
207
			$old_user       = wp_get_current_user();
208
			$this->old_user = isset( $old_user->ID ) ? $old_user->ID : 0;
209
			wp_set_current_user( $verified_user['user_id'] );
210
		}
211
	}
212
213
	/**
214
	 * If we used to have a previous current user, revert back to it.
215
	 *
216
	 * @access public
217
	 */
218
	public function maybe_clear_user_from_token() {
219
		if ( isset( $this->old_user ) ) {
220
			wp_set_current_user( $this->old_user );
221
		}
222
	}
223
224
	/**
225
	 * Retrieve the next sync time.
226
	 *
227
	 * @access public
228
	 *
229
	 * @param string $queue_name Name of the queue.
230
	 * @return float Timestamp of the next sync.
231
	 */
232
	public function get_next_sync_time( $queue_name ) {
233
		return (float) get_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name, 0 );
234
	}
235
236
	/**
237
	 * Set the next sync time.
238
	 *
239
	 * @access public
240
	 *
241
	 * @param int    $time       Timestamp of the next sync.
242
	 * @param string $queue_name Name of the queue.
243
	 * @return boolean True if update was successful, false otherwise.
244
	 */
245
	public function set_next_sync_time( $time, $queue_name ) {
246
		return update_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name, $time, true );
247
	}
248
249
	/**
250
	 * Trigger a full sync.
251
	 *
252
	 * @access public
253
	 *
254
	 * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise.
255
	 */
256
	public function do_full_sync() {
257
		if ( ! Modules::get_module( 'full-sync' ) ) {
258
			return;
259
		}
260
		$this->continue_full_sync_enqueue();
261
		return $this->do_sync_and_set_delays( $this->full_sync_queue );
262
	}
263
264
	/**
265
	 * Enqueue the next sync items for sending.
266
	 * Will not be done if the current request is a WP import one.
267
	 * Will be delayed until the next sync time comes.
268
	 *
269
	 * @access private
270
	 */
271
	private function continue_full_sync_enqueue() {
272
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
273
			return false;
274
		}
275
276
		if ( $this->get_next_sync_time( 'full-sync-enqueue' ) > microtime( true ) ) {
277
			return false;
278
		}
279
280
		Modules::get_module( 'full-sync' )->continue_enqueuing();
281
282
		$this->set_next_sync_time( time() + $this->get_enqueue_wait_time(), 'full-sync-enqueue' );
283
	}
284
285
	/**
286
	 * Trigger incremental sync.
287
	 *
288
	 * @access public
289
	 *
290
	 * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise.
291
	 */
292
	public function do_sync() {
293
		return $this->do_sync_and_set_delays( $this->sync_queue );
294
	}
295
296
	/**
297
	 * Trigger sync for a certain sync queue.
298
	 * Responsible for setting next sync time.
299
	 * Will not be delayed if the current request is a WP import one.
300
	 * Will be delayed until the next sync time comes.
301
	 *
302
	 * @access public
303
	 *
304
	 * @param Automattic\Jetpack\Sync\Queue $queue Queue object.
305
	 * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise.
306
	 */
307
	public function do_sync_and_set_delays( $queue ) {
308
		// Don't sync if importing.
309
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
310
			return new \WP_Error( 'is_importing' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'is_importing'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
311
		}
312
313
		// Don't sync if we are throttled.
314
		if ( $this->get_next_sync_time( $queue->id ) > microtime( true ) ) {
315
			return new \WP_Error( 'sync_throttled' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'sync_throttled'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
316
		}
317
318
		$start_time = microtime( true );
319
320
		Settings::set_is_syncing( true );
321
322
		$sync_result = $this->do_sync_for_queue( $queue );
323
324
		Settings::set_is_syncing( false );
325
326
		$exceeded_sync_wait_threshold = ( microtime( true ) - $start_time ) > (float) $this->get_sync_wait_threshold();
327
328
		if ( is_wp_error( $sync_result ) ) {
329
			if ( 'unclosed_buffer' === $sync_result->get_error_code() ) {
0 ignored issues
show
Bug introduced by
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
330
				$this->set_next_sync_time( time() + self::QUEUE_LOCKED_SYNC_DELAY, $queue->id );
331
			}
332
			if ( 'wpcom_error' === $sync_result->get_error_code() ) {
0 ignored issues
show
Bug introduced by
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
333
				$this->set_next_sync_time( time() + self::WPCOM_ERROR_SYNC_DELAY, $queue->id );
334
			}
335
		} elseif ( $exceeded_sync_wait_threshold ) {
336
			// If we actually sent data and it took a while, wait before sending again.
337
			$this->set_next_sync_time( time() + $this->get_sync_wait_time(), $queue->id );
338
		}
339
340
		return $sync_result;
341
	}
342
343
	/**
344
	 * Retrieve the next sync items to send.
345
	 *
346
	 * @access public
347
	 *
348
	 * @param Automattic\Jetpack\Sync\Queue_Buffer $buffer Queue buffer object.
349
	 * @param boolean                              $encode Whether to encode the items.
350
	 * @return array Sync items to send.
351
	 */
352
	public function get_items_to_send( $buffer, $encode = true ) {
353
		// Track how long we've been processing so we can avoid request timeouts.
354
		$start_time    = microtime( true );
355
		$upload_size   = 0;
356
		$items_to_send = array();
357
		$items         = $buffer->get_items();
358
		// Set up current screen to avoid errors rendering content.
359
		require_once ABSPATH . 'wp-admin/includes/class-wp-screen.php';
360
		require_once ABSPATH . 'wp-admin/includes/screen.php';
361
		set_current_screen( 'sync' );
362
		$skipped_items_ids = array();
363
		/**
364
		 * We estimate the total encoded size as we go by encoding each item individually.
365
		 * This is expensive, but the only way to really know :/
366
		 */
367
		foreach ( $items as $key => $item ) {
368
			// Suspending cache addition help prevent overloading in memory cache of large sites.
369
			wp_suspend_cache_addition( true );
370
			/**
371
			 * Modify the data within an action before it is serialized and sent to the server
372
			 * For example, during full sync this expands Post ID's into full Post objects,
373
			 * so that we don't have to serialize the whole object into the queue.
374
			 *
375
			 * @since 4.2.0
376
			 *
377
			 * @param array The action parameters
378
			 * @param int The ID of the user who triggered the action
379
			 */
380
			$item[1] = apply_filters( 'jetpack_sync_before_send_' . $item[0], $item[1], $item[2] );
381
			wp_suspend_cache_addition( false );
382
			if ( false === $item[1] ) {
383
				$skipped_items_ids[] = $key;
384
				continue;
385
			}
386
			$encoded_item = $encode ? $this->codec->encode( $item ) : $item;
387
			$upload_size += strlen( $encoded_item );
388
			if ( $upload_size > $this->upload_max_bytes && count( $items_to_send ) > 0 ) {
389
				break;
390
			}
391
			$items_to_send[ $key ] = $encoded_item;
392
			if ( microtime( true ) - $start_time > $this->max_dequeue_time ) {
393
				break;
394
			}
395
		}
396
397
		return array( $items_to_send, $skipped_items_ids, $items, microtime( true ) - $start_time );
398
	}
399
400
	/**
401
	 * If supported, flush all response data to the client and finish the request.
402
	 * This allows for time consuming tasks to be performed without leaving the connection open.
403
	 *
404
	 * @access private
405
	 */
406
	private function fastcgi_finish_request() {
407
		if ( function_exists( 'fastcgi_finish_request' ) && version_compare( phpversion(), '7.0.16', '>=' ) ) {
408
			fastcgi_finish_request();
409
		}
410
	}
411
412
	/**
413
	 * Perform sync for a certain sync queue.
414
	 *
415
	 * @access public
416
	 *
417
	 * @param Automattic\Jetpack\Sync\Queue $queue Queue object.
418
	 * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise.
419
	 */
420
	public function do_sync_for_queue( $queue ) {
421
		do_action( 'jetpack_sync_before_send_queue_' . $queue->id );
422
		if ( $queue->size() === 0 ) {
423
			return new \WP_Error( 'empty_queue_' . $queue->id );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'empty_queue_' . $queue->id.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
424
		}
425
		/**
426
		 * Now that we're sure we are about to sync, try to ignore user abort
427
		 * so we can avoid getting into a bad state.
428
		 */
429
		if ( function_exists( 'ignore_user_abort' ) ) {
430
			ignore_user_abort( true );
431
		}
432
433
		/* Don't make the request block till we finish, if possible. */
434
		if ( Constants::is_true( 'REST_REQUEST' ) || Constants::is_true( 'XMLRPC_REQUEST' ) ) {
435
			$this->fastcgi_finish_request();
436
		}
437
438
		$checkout_start_time = microtime( true );
439
440
		$buffer = $queue->checkout_with_memory_limit( $this->dequeue_max_bytes, $this->upload_max_rows );
441
442
		if ( ! $buffer ) {
443
			// Buffer has no items.
444
			return new \WP_Error( 'empty_buffer' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'empty_buffer'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
445
		}
446
447
		if ( is_wp_error( $buffer ) ) {
448
			return $buffer;
449
		}
450
451
		$checkout_duration = microtime( true ) - $checkout_start_time;
452
453
		list( $items_to_send, $skipped_items_ids, $items, $preprocess_duration ) = $this->get_items_to_send( $buffer, true );
454
		if ( ! empty( $items_to_send ) ) {
455
			/**
456
			 * Fires when data is ready to send to the server.
457
			 * Return false or WP_Error to abort the sync (e.g. if there's an error)
458
			 * The items will be automatically re-sent later
459
			 *
460
			 * @since 4.2.0
461
			 *
462
			 * @param array $data The action buffer
463
			 * @param string $codec The codec name used to encode the data
464
			 * @param double $time The current time
465
			 * @param string $queue The queue used to send ('sync' or 'full_sync')
466
			 */
467
			Settings::set_is_sending( true );
468
			$processed_item_ids = apply_filters( 'jetpack_sync_send_data', $items_to_send, $this->codec->name(), microtime( true ), $queue->id, $checkout_duration, $preprocess_duration );
469
			Settings::set_is_sending( false );
470
		} else {
471
			$processed_item_ids = $skipped_items_ids;
472
			$skipped_items_ids  = array();
473
		}
474
475
		if ( ! $processed_item_ids || is_wp_error( $processed_item_ids ) ) {
476
			$checked_in_item_ids = $queue->checkin( $buffer );
477
			if ( is_wp_error( $checked_in_item_ids ) ) {
478
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
479
				error_log( 'Error checking in buffer: ' . $checked_in_item_ids->get_error_message() );
480
				$queue->force_checkin();
481
			}
482
			if ( is_wp_error( $processed_item_ids ) ) {
483
				return new \WP_Error( 'wpcom_error', $processed_item_ids->get_error_code() );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'wpcom_error'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
484
			}
485
			// Returning a wpcom_error is a sign to the caller that we should wait a while before syncing again.
486
			return new \WP_Error( 'wpcom_error', 'jetpack_sync_send_data_false' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'wpcom_error'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
487
		} else {
488
			// Detect if the last item ID was an error.
489
			$had_wp_error = is_wp_error( end( $processed_item_ids ) );
490
			if ( $had_wp_error ) {
491
				$wp_error = array_pop( $processed_item_ids );
492
			}
493
			// Also checkin any items that were skipped.
494
			if ( count( $skipped_items_ids ) > 0 ) {
495
				$processed_item_ids = array_merge( $processed_item_ids, $skipped_items_ids );
496
			}
497
			$processed_items = array_intersect_key( $items, array_flip( $processed_item_ids ) );
498
			/**
499
			 * Allows us to keep track of all the actions that have been sent.
500
			 * Allows us to calculate the progress of specific actions.
501
			 *
502
			 * @since 4.2.0
503
			 *
504
			 * @param array $processed_actions The actions that we send successfully.
505
			 */
506
			do_action( 'jetpack_sync_processed_actions', $processed_items );
507
			$queue->close( $buffer, $processed_item_ids );
508
			// Returning a WP_Error is a sign to the caller that we should wait a while before syncing again.
509
			if ( $had_wp_error ) {
510
				return new \WP_Error( 'wpcom_error', $wp_error->get_error_code() );
0 ignored issues
show
Bug introduced by
The variable $wp_error 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...
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'wpcom_error'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
511
			}
512
		}
513
		return true;
514
	}
515
516
	/**
517
	 * Returns any object that is able to be synced.
518
	 *
519
	 * @access public
520
	 *
521
	 * @param array $args the synchronized object parameters.
522
	 * @return string Encoded sync object.
523
	 */
524
	public function sync_object( $args ) {
525
		// For example: posts, post, 5.
526
		list( $module_name, $object_type, $id ) = $args;
527
528
		$sync_module = Modules::get_module( $module_name );
529
		$codec       = $this->get_codec();
530
531
		return $codec->encode( $sync_module->get_object_by_id( $object_type, $id ) );
532
	}
533
534
	/**
535
	 * Register additional sync XML-RPC methods available to Jetpack for authenticated users.
536
	 *
537
	 * @access public
538
	 * @since 7.8
539
	 *
540
	 * @param array    $jetpack_methods XML-RPC methods available to the Jetpack Server.
541
	 * @param array    $core_methods    Available core XML-RPC methods.
542
	 * @param \WP_User $user            Information about a given WordPress user.
543
	 * @return array Filtered XML-RPC methods.
544
	 */
545
	public function register_jetpack_xmlrpc_methods( $jetpack_methods, $core_methods, $user ) {
546
		if ( $user ) {
547
			$jetpack_methods['jetpack.syncObject'] = array( $this, 'sync_object' );
548
		}
549
550
		return $jetpack_methods;
551
	}
552
553
	/**
554
	 * Get the incremental sync queue object.
555
	 *
556
	 * @access public
557
	 *
558
	 * @return Automattic\Jetpack\Sync\Queue Queue object.
559
	 */
560
	public function get_sync_queue() {
561
		return $this->sync_queue;
562
	}
563
564
	/**
565
	 * Get the full sync queue object.
566
	 *
567
	 * @access public
568
	 *
569
	 * @return Automattic\Jetpack\Sync\Queue Queue object.
570
	 */
571
	public function get_full_sync_queue() {
572
		return $this->full_sync_queue;
573
	}
574
575
	/**
576
	 * Get the codec object.
577
	 *
578
	 * @access public
579
	 *
580
	 * @return Automattic\Jetpack\Sync\Codec_Interface Codec object.
581
	 */
582
	public function get_codec() {
583
		return $this->codec;
584
	}
585
586
	/**
587
	 * Determine the codec object.
588
	 * Use gzip deflate if supported.
589
	 *
590
	 * @access public
591
	 */
592
	public function set_codec() {
593
		if ( function_exists( 'gzinflate' ) ) {
594
			$this->codec = new JSON_Deflate_Array_Codec();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Automattic\Jetpack\...N_Deflate_Array_Codec() of type object<Automattic\Jetpac...ON_Deflate_Array_Codec> is incompatible with the declared type object<Automattic\Jetpac...k\Sync\Codec_Interface> of property $codec.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
595
		} else {
596
			$this->codec = new Simple_Codec();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Automattic\Jetpack\Sync\Simple_Codec() of type object<Automattic\Jetpack\Sync\Simple_Codec> is incompatible with the declared type object<Automattic\Jetpac...k\Sync\Codec_Interface> of property $codec.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
597
		}
598
	}
599
600
	/**
601
	 * Compute and send all the checksums.
602
	 *
603
	 * @access public
604
	 */
605
	public function send_checksum() {
606
		$store = new Replicastore();
607
		do_action( 'jetpack_sync_checksum', $store->checksum_all() );
608
	}
609
610
	/**
611
	 * Reset the incremental sync queue.
612
	 *
613
	 * @access public
614
	 */
615
	public function reset_sync_queue() {
616
		$this->sync_queue->reset();
617
	}
618
619
	/**
620
	 * Reset the full sync queue.
621
	 *
622
	 * @access public
623
	 */
624
	public function reset_full_sync_queue() {
625
		$this->full_sync_queue->reset();
626
	}
627
628
	/**
629
	 * Set the maximum bytes to checkout without exceeding the memory limit.
630
	 *
631
	 * @access public
632
	 *
633
	 * @param int $size Maximum bytes to checkout.
634
	 */
635
	public function set_dequeue_max_bytes( $size ) {
636
		$this->dequeue_max_bytes = $size;
637
	}
638
639
	/**
640
	 * Set the maximum bytes in a single encoded item.
641
	 *
642
	 * @access public
643
	 *
644
	 * @param int $max_bytes Maximum bytes in a single encoded item.
645
	 */
646
	public function set_upload_max_bytes( $max_bytes ) {
647
		$this->upload_max_bytes = $max_bytes;
648
	}
649
650
	/**
651
	 * Set the maximum number of sync items in a single action.
652
	 *
653
	 * @access public
654
	 *
655
	 * @param int $max_rows Maximum number of sync items.
656
	 */
657
	public function set_upload_max_rows( $max_rows ) {
658
		$this->upload_max_rows = $max_rows;
659
	}
660
661
	/**
662
	 * Set the sync wait time (in seconds).
663
	 *
664
	 * @access public
665
	 *
666
	 * @param int $seconds Sync wait time.
667
	 */
668
	public function set_sync_wait_time( $seconds ) {
669
		$this->sync_wait_time = $seconds;
670
	}
671
672
	/**
673
	 * Get current sync wait time (in seconds).
674
	 *
675
	 * @access public
676
	 *
677
	 * @return int Sync wait time.
678
	 */
679
	public function get_sync_wait_time() {
680
		return $this->sync_wait_time;
681
	}
682
683
	/**
684
	 * Set the enqueue wait time (in seconds).
685
	 *
686
	 * @access public
687
	 *
688
	 * @param int $seconds Enqueue wait time.
689
	 */
690
	public function set_enqueue_wait_time( $seconds ) {
691
		$this->enqueue_wait_time = $seconds;
692
	}
693
694
	/**
695
	 * Get current enqueue wait time (in seconds).
696
	 *
697
	 * @access public
698
	 *
699
	 * @return int Enqueue wait time.
700
	 */
701
	public function get_enqueue_wait_time() {
702
		return $this->enqueue_wait_time;
703
	}
704
705
	/**
706
	 * Set the sync wait threshold (in seconds).
707
	 *
708
	 * @access public
709
	 *
710
	 * @param int $seconds Sync wait threshold.
711
	 */
712
	public function set_sync_wait_threshold( $seconds ) {
713
		$this->sync_wait_threshold = $seconds;
714
	}
715
716
	/**
717
	 * Get current sync wait threshold (in seconds).
718
	 *
719
	 * @access public
720
	 *
721
	 * @return int Sync wait threshold.
722
	 */
723
	public function get_sync_wait_threshold() {
724
		return $this->sync_wait_threshold;
725
	}
726
727
	/**
728
	 * Set the maximum time for perfirming a checkout of items from the queue (in seconds).
729
	 *
730
	 * @access public
731
	 *
732
	 * @param int $seconds Maximum dequeue time.
733
	 */
734
	public function set_max_dequeue_time( $seconds ) {
735
		$this->max_dequeue_time = $seconds;
736
	}
737
738
	/**
739
	 * Initialize the sync queues, codec and set the default settings.
740
	 *
741
	 * @access public
742
	 */
743
	public function set_defaults() {
744
		$this->sync_queue      = new Queue( 'sync' );
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Automattic\Jetpack\Sync\Queue('sync') of type object<Automattic\Jetpack\Sync\Queue> is incompatible with the declared type object<Automattic\Jetpac...tic\Jetpack\Sync\Queue> of property $sync_queue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
745
		$this->full_sync_queue = new Queue( 'full_sync' );
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Automattic\Jetpack\Sync\Queue('full_sync') of type object<Automattic\Jetpack\Sync\Queue> is incompatible with the declared type object<Automattic\Jetpac...tic\Jetpack\Sync\Queue> of property $full_sync_queue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
746
		$this->set_codec();
747
748
		// Saved settings.
749
		Settings::set_importing( null );
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
750
		$settings = Settings::get_settings();
751
		$this->set_dequeue_max_bytes( $settings['dequeue_max_bytes'] );
752
		$this->set_upload_max_bytes( $settings['upload_max_bytes'] );
753
		$this->set_upload_max_rows( $settings['upload_max_rows'] );
754
		$this->set_sync_wait_time( $settings['sync_wait_time'] );
755
		$this->set_enqueue_wait_time( $settings['enqueue_wait_time'] );
756
		$this->set_sync_wait_threshold( $settings['sync_wait_threshold'] );
757
		$this->set_max_dequeue_time( Defaults::get_max_sync_execution_time() );
758
	}
759
760
	/**
761
	 * Reset sync queues, modules and settings.
762
	 *
763
	 * @access public
764
	 */
765
	public function reset_data() {
766
		$this->reset_sync_queue();
767
		$this->reset_full_sync_queue();
768
769
		foreach ( Modules::get_modules() as $module ) {
0 ignored issues
show
Bug introduced by
The expression \Automattic\Jetpack\Sync\Modules::get_modules() of type array|null 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...
770
			$module->reset_data();
771
		}
772
773
		foreach ( array( 'sync', 'full_sync', 'full-sync-enqueue' ) as $queue_name ) {
774
			delete_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name );
775
		}
776
777
		Settings::reset_data();
778
	}
779
780
	/**
781
	 * Perform cleanup at the event of plugin uninstallation.
782
	 *
783
	 * @access public
784
	 */
785
	public function uninstall() {
786
		// Lets delete all the other fun stuff like transient and option and the sync queue.
787
		$this->reset_data();
788
789
		// Delete the full sync status.
790
		delete_option( 'jetpack_full_sync_status' );
791
792
		// Clear the sync cron.
793
		wp_clear_scheduled_hook( 'jetpack_sync_cron' );
794
		wp_clear_scheduled_hook( 'jetpack_sync_full_cron' );
795
	}
796
}
797