Completed
Push — update/endpoint-pull-immediate... ( b750f8...46e01a )
by
unknown
06:53
created

Jetpack_JSON_API_Sync_Checkout_Endpoint::result()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 4
nop 0
dl 0
loc 20
rs 8.9777
c 0
b 0
f 0
1
<?php
2
3
use Automattic\Jetpack\Sync\Actions;
4
use Automattic\Jetpack\Sync\Modules;
5
use Automattic\Jetpack\Sync\Queue;
6
use Automattic\Jetpack\Sync\Queue_Buffer;
7
use Automattic\Jetpack\Sync\Replicastore;
8
use Automattic\Jetpack\Sync\Sender;
9
use Automattic\Jetpack\Sync\Settings;
10
11
// POST /sites/%s/sync
12
class Jetpack_JSON_API_Sync_Endpoint extends Jetpack_JSON_API_Endpoint {
13
	protected $needed_capabilities = 'manage_options';
14
15
	protected function validate_call( $_blog_id, $capability, $check_manage_active = true ) {
16
		return parent::validate_call( $_blog_id, $capability, false );
17
	}
18
19
	protected function result() {
20
		$args = $this->input();
21
		$modules = null;
22
23
		// convert list of modules in comma-delimited format into an array
24
		// of "$modulename => true"
25
		if ( isset( $args['modules'] ) && ! empty( $args['modules'] ) ) {
26
			$modules = array_map( '__return_true', array_flip( array_map( 'trim', explode( ',', $args['modules'] ) ) ) );
27
		}
28
29 View Code Duplication
		foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
30
			if ( 'users' === $module_name && isset( $args[ $module_name ] ) && 'initial' === $args[ $module_name ] ) {
31
				$modules[ 'users' ] = 'initial';
32
			} elseif ( isset( $args[ $module_name ] ) ) {
33
				$ids = explode( ',', $args[ $module_name ] );
34
				if ( count( $ids ) > 0 ) {
35
					$modules[ $module_name ] = $ids;
36
				}
37
			}
38
		}
39
40
		if ( empty( $modules ) ) {
41
			$modules = null;
42
		}
43
		return array( 'scheduled' => Actions::do_full_sync( $modules ) );
44
	}
45
46
	protected function validate_queue( $query ) {
47
		if ( ! isset( $query ) ) {
48
			return new WP_Error( 'invalid_queue', 'Queue name is required', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_queue'.

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...
49
		}
50
51 View Code Duplication
		if ( ! in_array( $query, array( 'sync', 'full_sync', 'immediate' ) ) ) {
52
			return new WP_Error( 'invalid_queue', 'Queue name should be sync or full_sync', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_queue'.

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...
53
		}
54
		return $query;
55
	}
56
}
57
58
// GET /sites/%s/sync/status
59
class Jetpack_JSON_API_Sync_Status_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
60
	protected function result() {
61
		$args   = $this->query_args();
62
		$fields = isset( $args['fields'] ) ? $args['fields'] : array();
63
		return Actions::get_sync_status( $fields );
64
	}
65
}
66
67
// GET /sites/%s/data-check
68
class Jetpack_JSON_API_Sync_Check_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
69
	protected function result() {
70
		$store = new Replicastore();
71
		return $store->checksum_all();
72
	}
73
}
74
75
// GET /sites/%s/data-histogram
76
class Jetpack_JSON_API_Sync_Histogram_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
77
	protected function result() {
78
		$args = $this->query_args();
79
80
		if ( isset( $args['columns'] ) ) {
81
			$columns = array_map( 'trim', explode( ',', $args['columns'] ) );
82
		} else {
83
			$columns = null; // go with defaults
84
		}
85
86
		$store = new Replicastore();
87
88
		if ( ! isset( $args['strip_non_ascii'] ) ) {
89
			$args['strip_non_ascii'] = true;
90
		}
91
		$histogram = $store->checksum_histogram( $args['object_type'], $args['buckets'], $args['start_id'], $args['end_id'], $columns, $args['strip_non_ascii'], $args['shared_salt'] );
92
93
		return array( 'histogram' => $histogram, 'type' => $store->get_checksum_type() );
94
	}
95
}
96
97
// POST /sites/%s/sync/settings
98
class Jetpack_JSON_API_Sync_Modify_Settings_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
99
	protected function result() {
100
		$args = $this->input();
101
102
		$sync_settings = Settings::get_settings();
103
104
		foreach ( $args as $key => $value ) {
105
			if ( $value !== false ) {
106
				if ( is_numeric( $value ) ) {
107
					$value = (int) $value;
108
				}
109
110
				// special case for sending empty arrays - a string with value 'empty'
111
				if ( $value === 'empty' ) {
112
					$value = array();
113
				}
114
115
				$sync_settings[ $key ] = $value;
116
			}
117
		}
118
119
		Settings::update_settings( $sync_settings );
120
121
		// re-fetch so we see what's really being stored
122
		return Settings::get_settings();
123
	}
124
}
125
126
// GET /sites/%s/sync/settings
127
class Jetpack_JSON_API_Sync_Get_Settings_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
128
	protected function result() {
129
130
		return Settings::get_settings();
131
	}
132
}
133
134
// GET /sites/%s/sync/object
135
class Jetpack_JSON_API_Sync_Object extends Jetpack_JSON_API_Sync_Endpoint {
136
	protected function result() {
137
		$args = $this->query_args();
138
139
		$module_name = $args['module_name'];
140
141
		if ( ! $sync_module = Modules::get_module( $module_name ) ) {
142
			return new WP_Error( 'invalid_module', 'You specified an invalid sync module' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_module'.

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...
143
		}
144
145
		$object_type = $args['object_type'];
146
		$object_ids  = $args['object_ids'];
147
148
		$codec = Sender::get_instance()->get_codec();
149
150
		Settings::set_is_syncing( true );
151
		$objects = $codec->encode( $sync_module->get_objects_by_id( $object_type, $object_ids ) );
152
		Settings::set_is_syncing( false );
153
154
		return array(
155
			'objects' => $objects,
156
			'codec' => $codec->name(),
157
		);
158
	}
159
}
160
161
class Jetpack_JSON_API_Sync_Now_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
162
	protected function result() {
163
		$args = $this->input();
164
		$queue_name = $this->validate_queue( $args['queue'] );
165
166
		if ( is_wp_error( $queue_name ) ){
167
			return $queue_name;
168
		}
169
170
		$sender = Sender::get_instance();
171
		$response = $sender->do_sync_for_queue( new Queue( $args['queue'] ) );
0 ignored issues
show
Documentation introduced by
new \Automattic\Jetpack\...c\Queue($args['queue']) is of type object<Automattic\Jetpack\Sync\Queue>, but the function expects a object<Automattic\Jetpac...tic\Jetpack\Sync\Queue>.

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...
172
173
		return array(
174
			'response' => $response
175
		);
176
	}
177
}
178
179
class Jetpack_JSON_API_Sync_Checkout_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
180
	protected function result() {
181
		$args       = $this->input();
182
		$queue_name = $this->validate_queue( $args['queue'] );
183
184
		if ( is_wp_error( $queue_name ) ) {
185
			return $queue_name;
186
		}
187
188
		if ( $args['number_of_items'] < 1 || $args['number_of_items'] > 100 ) {
189
			return new WP_Error( 'invalid_number_of_items', 'Number of items needs to be an integer that is larger than 0 and less then 100', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_number_of_items'.

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...
190
		}
191
192
		$number_of_items = absint( $args['number_of_items'] );
193
194
		if ( ! empty( $args['queue'] ) && 'immediate' === $args['queue'] ) {
195
			return $this->immediate_full_sync_pull( $number_of_items );
196
		}
197
198
		return $this->queue_pull( $queue_name, $number_of_items, $args );
199
	}
200
201
	function queue_pull( $queue_name, $number_of_items, $args ){
202
		$queue = new Queue( $queue_name );
203
204
		if ( 0 === $queue->size() ) {
205
			return new WP_Error( 'queue_size', 'The queue is empty and there is nothing to send', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'queue_size'.

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...
206
		}
207
208
		$sender = Sender::get_instance();
209
210
		// try to give ourselves as much time as possible.
211
		set_time_limit( 0 );
212
213
		if ( $args['pop'] ) {
214
			$buffer = new Queue_Buffer( 'pop', $queue->pop( $number_of_items ) );
0 ignored issues
show
Bug introduced by
It seems like $queue->pop($number_of_items) targeting Automattic\Jetpack\Sync\Queue::pop() can also be of type null or object; however, Automattic\Jetpack\Sync\...e_Buffer::__construct() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
215
		} else {
216
			// let's delete the checkin state.
217
			if ( $args['force'] ) {
218
				$queue->unlock();
219
			}
220
			$buffer = $this->get_buffer( $queue, $number_of_items );
221
		}
222
		// Check that the $buffer is not checkout out already.
223
		if ( is_wp_error( $buffer ) ) {
224
			return new WP_Error( 'buffer_open', "We couldn't get the buffer it is currently checked out", 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'buffer_open'.

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...
225
		}
226
227
		if ( ! is_object( $buffer ) ) {
228
			return new WP_Error( 'buffer_non-object', 'Buffer is not an object', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'buffer_non-object'.

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...
229
		}
230
231
		Settings::set_is_syncing( true );
232
		list( $items_to_send, $skipped_items_ids ) = $sender->get_items_to_send( $buffer, $args['encode'] );
233
		Settings::set_is_syncing( false );
234
235
		return array(
236
			'buffer_id'      => $buffer->id,
237
			'items'          => $items_to_send,
238
			'skipped_items'  => $skipped_items_ids,
239
			'codec'          => $args['encode'] ? $sender->get_codec()->name() : null,
240
			'sent_timestamp' => time(),
241
		);
242
	}
243
244
	public $items = [];
245
246
	public function jetpack_sync_send_data_listener() {
247
		foreach ( func_get_args()[0] as $key => $item ) {
248
			$items[ $key ] = $item;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$items was never initialized. Although not strictly required by PHP, it is generally a good practice to add $items = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
249
		}
250
	}
251
252
	public function immediate_full_sync_pull( $number_of_items ) {
253
		// try to give ourselves as much time as possible.
254
		set_time_limit( 0 );
255
256
		remove_filter( 'jetpack_sync_send_data', array( 'Automattic\Jetpack\Sync\Actions', 'send_data' ), 10, 6 );
257
		add_filter( 'jetpack_sync_send_data', array( $this, 'jetpack_sync_send_data_listener' ), 10, 6 );
258
		Sender::get_instance()->do_full_sync();
259
		remove_filter( 'jetpack_sync_send_data', array( $this, 'jetpack_sync_send_data_listener' ), 10, 6 );
260
		add_filter( 'jetpack_sync_send_data', array( 'Automattic\Jetpack\Sync\Actions', 'send_data' ), 10, 6 );
261
262
		return array(
263
			'items'          => $this->items,
264
			'codec'          => Sender::get_instance()->get_codec()->name(),
265
			'sent_timestamp' => time(),
266
			'status'         => Actions::get_sync_status(),
267
		);
268
	}
269
270
	protected function get_buffer( $queue, $number_of_items ) {
271
		$start = time();
272
		$max_duration = 5; // this will try to get the buffer
273
274
		$buffer = $queue->checkout( $number_of_items );
275
		$duration = time() - $start;
276
277
		while( is_wp_error( $buffer ) && $duration < $max_duration ) {
278
			sleep( 2 );
279
			$duration = time() - $start;
280
			$buffer = $queue->checkout( $number_of_items );
281
		}
282
283
		if ( $buffer === false ) {
284
			return new WP_Error( 'queue_size', 'The queue is empty and there is nothing to send', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'queue_size'.

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...
285
		}
286
287
		return $buffer;
288
	}
289
}
290
291
class Jetpack_JSON_API_Sync_Close_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
292
	protected function result() {
293
		$request_body = $this->input();
294
		$queue_name = $this->validate_queue( $request_body['queue'] );
295
296
		if ( is_wp_error( $queue_name ) ) {
297
			return $queue_name;
298
		}
299
300
		if ( ! isset( $request_body['buffer_id'] ) ) {
301
			return new WP_Error( 'missing_buffer_id', 'Please provide a buffer id', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'missing_buffer_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...
302
		}
303
304
		if ( ! isset( $request_body['item_ids'] ) || ! is_array( $request_body['item_ids'] ) ) {
305
			return new WP_Error( 'missing_item_ids', 'Please provide a list of item ids in the item_ids argument', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'missing_item_ids'.

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...
306
		}
307
308
		//Limit to A-Z,a-z,0-9,_,-
309
		$request_body ['buffer_id'] = preg_replace( '/[^A-Za-z0-9]/', '', $request_body['buffer_id'] );
310
		$request_body['item_ids'] = array_filter( array_map( array( 'Jetpack_JSON_API_Sync_Close_Endpoint', 'sanitize_item_ids' ), $request_body['item_ids'] ) );
311
312
		$queue = new Queue( $queue_name );
313
314
		$items = $queue->peek_by_id( $request_body['item_ids'] );
315
316
		/** This action is documented in packages/sync/src/modules/Full_Sync.php */
317
		$full_sync_module = Modules::get_module( 'full-sync' );
318
319
		$full_sync_module->update_sent_progress_action( $items );
320
321
		$buffer = new Queue_Buffer( $request_body['buffer_id'], $request_body['item_ids'] );
322
		$response = $queue->close( $buffer, $request_body['item_ids'] );
0 ignored issues
show
Documentation introduced by
$buffer is of type object<Automattic\Jetpack\Sync\Queue_Buffer>, but the function expects a object<Automattic\Jetpac...pack\Sync\Queue_Buffer>.

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...
323
324
		if ( is_wp_error( $response ) ) {
325
			return $response;
326
		}
327
328
		return array(
329
			'success' => $response,
330
			'status' => Actions::get_sync_status(),
331
		);
332
	}
333
334
	protected static function sanitize_item_ids( $item ) {
335
		// lets not delete any options that don't start with jpsq_sync-
336
		if ( substr( $item, 0, 5 ) !== 'jpsq_' ) {
337
			return null;
338
		}
339
		//Limit to A-Z,a-z,0-9,_,-,.
340
		return preg_replace( '/[^A-Za-z0-9-_.]/', '', $item );
341
	}
342
}
343
344
class Jetpack_JSON_API_Sync_Unlock_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
345
	protected function result() {
346
		$args = $this->input();
347
348
		if ( ! isset( $args['queue'] ) ) {
349
			return new WP_Error( 'invalid_queue', 'Queue name is required', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_queue'.

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...
350
		}
351
352 View Code Duplication
		if ( ! in_array( $args['queue'], array( 'sync', 'full_sync' ) ) ) {
353
			return new WP_Error( 'invalid_queue', 'Queue name should be sync or full_sync', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_queue'.

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...
354
		}
355
356
		$queue = new Queue( $args['queue'] );
357
358
		// False means that there was no lock to delete.
359
		$response = $queue->unlock();
360
		return array(
361
			'success' => $response
362
		);
363
	}
364
}
365
366
class Jetpack_JSON_API_Sync_Object_Id_Range extends Jetpack_JSON_API_Sync_Endpoint {
367
	protected function result() {
368
		$args = $this->query_args();
369
370
		$module_name = $args['sync_module'];
371
		$batch_size  = $args['batch_size'];
372
373
		if ( ! $this->is_valid_sync_module( $module_name ) ) {
374
			return new WP_Error( 'invalid_module', 'This sync module cannot be used to calculate a range.', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_module'.

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...
375
		}
376
377
		$module = Modules::get_module( $module_name );
378
379
		return array(
380
			'ranges' => $module->get_min_max_object_ids_for_batches( $batch_size ),
381
		);
382
	}
383
384
	protected function is_valid_sync_module( $module_name ) {
385
		return in_array(
386
			$module_name,
387
			array(
388
				'comments',
389
				'posts',
390
				'terms',
391
				'term_relationships',
392
				'users',
393
			),
394
			true
395
		);
396
	}
397
}
398