Completed
Push — master ( fdf30b...a9ceed )
by
unknown
247:20 queued 235:26
created

Updates   F

Complexity

Total Complexity 76

Size/Duplication

Total Lines 564
Duplicated Lines 3.19 %

Coupling/Cohesion

Components 3
Dependencies 2

Importance

Changes 0
Metric Value
dl 18
loc 564
rs 2.32
c 0
b 0
f 0
wmc 76
lcom 3
cbo 2

23 Methods

Rating   Name   Duplication   Size   Complexity  
A set_defaults() 0 3 1
A name() 0 3 1
A init_listeners() 0 44 2
A init_full_sync_listeners() 0 3 1
A init_before_send() 0 4 1
A update_core_network_event() 0 14 1
B update_core() 0 42 5
D get_update_checksum() 0 62 29
B validate_update_change() 0 36 7
A sync_last_event() 0 18 2
A enqueue_full_sync_actions() 0 13 1
A send_full_sync_actions() 0 7 1
A estimate_full_sync_actions() 0 3 1
A get_full_sync_actions() 0 3 1
A get_all_updates() 0 7 1
A filter_update_keys() 0 9 2
A filter_upgrader_process_complete() 0 5 1
A expand_updates() 0 7 2
A expand_themes() 0 15 4
A reset_data() 0 3 1
A total() 0 3 1
A get_object_by_id() 0 13 4
A get_objects_by_id() 18 18 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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

1
<?php
2
/**
3
 * Updates sync module.
4
 *
5
 * @package automattic/jetpack-sync
6
 */
7
8
namespace Automattic\Jetpack\Sync\Modules;
9
10
use Automattic\Jetpack\Constants as Jetpack_Constants;
11
12
/**
13
 * Class to handle sync for updates.
14
 */
15
class Updates extends Module {
16
	/**
17
	 * Name of the updates checksum option.
18
	 *
19
	 * @var string
20
	 */
21
	const UPDATES_CHECKSUM_OPTION_NAME = 'jetpack_updates_sync_checksum';
22
23
	/**
24
	 * WordPress Version.
25
	 *
26
	 * @access private
27
	 *
28
	 * @var string
29
	 */
30
	private $old_wp_version = null;
31
32
	/**
33
	 * The current updates.
34
	 *
35
	 * @access private
36
	 *
37
	 * @var array
38
	 */
39
	private $updates = array();
40
41
	/**
42
	 * Set module defaults.
43
	 *
44
	 * @access public
45
	 */
46
	public function set_defaults() {
47
		$this->updates = array();
48
	}
49
50
	/**
51
	 * Sync module name.
52
	 *
53
	 * @access public
54
	 *
55
	 * @return string
56
	 */
57
	public function name() {
58
		return 'updates';
59
	}
60
61
	/**
62
	 * Initialize updates action listeners.
63
	 *
64
	 * @access public
65
	 *
66
	 * @param callable $callable Action handler callable.
67
	 */
68
	public function init_listeners( $callable ) {
69
		global $wp_version;
70
		$this->old_wp_version = $wp_version;
71
		add_action( 'set_site_transient_update_plugins', array( $this, 'validate_update_change' ), 10, 3 );
72
		add_action( 'set_site_transient_update_themes', array( $this, 'validate_update_change' ), 10, 3 );
73
		add_action( 'set_site_transient_update_core', array( $this, 'validate_update_change' ), 10, 3 );
74
75
		add_action( 'jetpack_update_plugins_change', $callable );
76
		add_action( 'jetpack_update_themes_change', $callable );
77
		add_action( 'jetpack_update_core_change', $callable );
78
79
		add_filter(
80
			'jetpack_sync_before_enqueue_jetpack_update_plugins_change',
81
			array(
82
				$this,
83
				'filter_update_keys',
84
			),
85
			10,
86
			2
87
		);
88
		add_filter(
89
			'jetpack_sync_before_enqueue_upgrader_process_complete',
90
			array(
91
				$this,
92
				'filter_upgrader_process_complete',
93
			),
94
			10,
95
			2
96
		);
97
98
		add_action( 'automatic_updates_complete', $callable );
99
100
		if ( is_multisite() ) {
101
			add_filter( 'pre_update_site_option_wpmu_upgrade_site', array( $this, 'update_core_network_event' ), 10, 2 );
102
			add_action( 'jetpack_sync_core_update_network', $callable, 10, 3 );
103
		}
104
105
		// Send data when update completes.
106
		add_action( '_core_updated_successfully', array( $this, 'update_core' ) );
107
		add_action( 'jetpack_sync_core_reinstalled_successfully', $callable );
108
		add_action( 'jetpack_sync_core_autoupdated_successfully', $callable, 10, 2 );
109
		add_action( 'jetpack_sync_core_updated_successfully', $callable, 10, 2 );
110
111
	}
112
113
	/**
114
	 * Initialize updates action listeners for full sync.
115
	 *
116
	 * @access public
117
	 *
118
	 * @param callable $callable Action handler callable.
119
	 */
120
	public function init_full_sync_listeners( $callable ) {
121
		add_action( 'jetpack_full_sync_updates', $callable );
122
	}
123
124
	/**
125
	 * Initialize the module in the sender.
126
	 *
127
	 * @access public
128
	 */
129
	public function init_before_send() {
130
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_updates', array( $this, 'expand_updates' ) );
131
		add_filter( 'jetpack_sync_before_send_jetpack_update_themes_change', array( $this, 'expand_themes' ) );
132
	}
133
134
	/**
135
	 * Handle a core network update.
136
	 *
137
	 * @access public
138
	 *
139
	 * @param int $wp_db_version     Current version of the WordPress database.
140
	 * @param int $old_wp_db_version Old version of the WordPress database.
141
	 * @return int Current version of the WordPress database.
142
	 */
143
	public function update_core_network_event( $wp_db_version, $old_wp_db_version ) {
144
		global $wp_version;
145
		/**
146
		 * Sync event for when core wp network updates to a new db version
147
		 *
148
		 * @since 5.0.0
149
		 *
150
		 * @param int $wp_db_version the latest wp_db_version
151
		 * @param int $old_wp_db_version previous wp_db_version
152
		 * @param string $wp_version the latest wp_version
153
		 */
154
		do_action( 'jetpack_sync_core_update_network', $wp_db_version, $old_wp_db_version, $wp_version );
155
		return $wp_db_version;
156
	}
157
158
	/**
159
	 * Handle a core update.
160
	 *
161
	 * @access public
162
	 *
163
	 * @todo Implement nonce or refactor to use `admin_post_{$action}` hooks instead.
164
	 *
165
	 * @param string $new_wp_version The new WP core version.
166
	 */
167
	public function update_core( $new_wp_version ) {
168
		global $pagenow;
169
170
		// // phpcs:ignore WordPress.Security.NonceVerification.Recommended
171
		if ( isset( $_GET['action'] ) && 'do-core-reinstall' === $_GET['action'] ) {
172
			/**
173
			 * Sync event that fires when core reinstall was successful
174
			 *
175
			 * @since 5.0.0
176
			 *
177
			 * @param string $new_wp_version the updated WordPress version
178
			 */
179
			do_action( 'jetpack_sync_core_reinstalled_successfully', $new_wp_version );
180
			return;
181
		}
182
183
		// Core was autoupdated.
184
		if (
185
			'update-core.php' !== $pagenow &&
186
			! Jetpack_Constants::is_true( 'REST_API_REQUEST' ) // WP.com rest api calls should never be marked as a core autoupdate.
187
		) {
188
			/**
189
			 * Sync event that fires when core autoupdate was successful
190
			 *
191
			 * @since 5.0.0
192
			 *
193
			 * @param string $new_wp_version the updated WordPress version
194
			 * @param string $old_wp_version the previous WordPress version
195
			 */
196
			do_action( 'jetpack_sync_core_autoupdated_successfully', $new_wp_version, $this->old_wp_version );
197
			return;
198
		}
199
		/**
200
		 * Sync event that fires when core update was successful
201
		 *
202
		 * @since 5.0.0
203
		 *
204
		 * @param string $new_wp_version the updated WordPress version
205
		 * @param string $old_wp_version the previous WordPress version
206
		 */
207
		do_action( 'jetpack_sync_core_updated_successfully', $new_wp_version, $this->old_wp_version );
208
	}
209
210
	/**
211
	 * Retrieve the checksum for an update.
212
	 *
213
	 * @access public
214
	 *
215
	 * @param object $update    The update object.
216
	 * @param string $transient The transient we're retrieving a checksum for.
217
	 * @return int The checksum.
218
	 */
219
	public function get_update_checksum( $update, $transient ) {
220
		$updates    = array();
221
		$no_updated = array();
222
		switch ( $transient ) {
223
			case 'update_plugins':
224
				if ( ! empty( $update->response ) && is_array( $update->response ) ) {
225
					foreach ( $update->response as $plugin_slug => $response ) {
226
						if ( ! empty( $plugin_slug ) && isset( $response->new_version ) ) {
227
							$updates[] = array( $plugin_slug => $response->new_version );
228
						}
229
					}
230
				}
231
				if ( ! empty( $update->no_update ) ) {
232
					$no_updated = array_keys( $update->no_update );
233
				}
234
235
				if ( ! isset( $no_updated['jetpack/jetpack.php'] ) && isset( $updates['jetpack/jetpack.php'] ) ) {
236
					return false;
237
				}
238
239
				break;
240
			case 'update_themes':
241
				if ( ! empty( $update->response ) && is_array( $update->response ) ) {
242
					foreach ( $update->response as $theme_slug => $response ) {
243
						if ( ! empty( $theme_slug ) && isset( $response['new_version'] ) ) {
244
							$updates[] = array( $theme_slug => $response['new_version'] );
245
						}
246
					}
247
				}
248
249
				if ( ! empty( $update->checked ) ) {
250
					$no_updated = $update->checked;
251
				}
252
253
				break;
254
			case 'update_core':
255
				if ( ! empty( $update->updates ) && is_array( $update->updates ) ) {
256
					foreach ( $update->updates as $response ) {
257
						if ( ! empty( $response->response ) && 'latest' === $response->response ) {
258
							continue;
259
						}
260
						if ( ! empty( $response->response ) && isset( $response->packages->full ) ) {
261
							$updates[] = array( $response->response => $response->packages->full );
262
						}
263
					}
264
				}
265
266
				if ( ! empty( $update->version_checked ) ) {
267
					$no_updated = $update->version_checked;
268
				}
269
270
				if ( empty( $updates ) ) {
271
					return false;
272
				}
273
				break;
274
275
		}
276
		if ( empty( $updates ) && empty( $no_updated ) ) {
277
			return false;
278
		}
279
		return $this->get_check_sum( array( $no_updated, $updates ) );
280
	}
281
282
	/**
283
	 * Validate a change coming from an update before sending for sync.
284
	 *
285
	 * @access public
286
	 *
287
	 * @param mixed  $value      Site transient value.
288
	 * @param int    $expiration Time until transient expiration in seconds.
289
	 * @param string $transient  Transient name.
290
	 */
291
	public function validate_update_change( $value, $expiration, $transient ) {
292
		$new_checksum = $this->get_update_checksum( $value, $transient );
293
294
		if ( false === $new_checksum ) {
295
			return;
296
		}
297
298
		$checksums = get_option( self::UPDATES_CHECKSUM_OPTION_NAME, array() );
299
300
		if ( isset( $checksums[ $transient ] ) && $checksums[ $transient ] === $new_checksum ) {
301
			return;
302
		}
303
304
		$checksums[ $transient ] = $new_checksum;
305
306
		update_option( self::UPDATES_CHECKSUM_OPTION_NAME, $checksums );
307
		if ( 'update_core' === $transient ) {
308
			/**
309
			 * Trigger a change to core update that we want to sync.
310
			 *
311
			 * @since 5.1.0
312
			 *
313
			 * @param array $value Contains info that tells us what needs updating.
314
			 */
315
			do_action( 'jetpack_update_core_change', $value );
316
			return;
317
		}
318
		if ( empty( $this->updates ) ) {
319
			// Lets add the shutdown method once and only when the updates move from empty to filled with something.
320
			add_action( 'shutdown', array( $this, 'sync_last_event' ), 9 );
321
		}
322
		if ( ! isset( $this->updates[ $transient ] ) ) {
323
			$this->updates[ $transient ] = array();
324
		}
325
		$this->updates[ $transient ][] = $value;
326
	}
327
328
	/**
329
	 * Sync the last update only.
330
	 *
331
	 * @access public
332
	 */
333
	public function sync_last_event() {
334
		foreach ( $this->updates as $transient => $values ) {
335
			$value = end( $values ); // Only send over the last value.
336
			/**
337
			 * Trigger a change to a specific update that we want to sync.
338
			 * Triggers one of the following actions:
339
			 * - jetpack_{$transient}_change
340
			 * - jetpack_update_plugins_change
341
			 * - jetpack_update_themes_change
342
			 *
343
			 * @since 5.1.0
344
			 *
345
			 * @param array $value Contains info that tells us what needs updating.
346
			 */
347
			do_action( "jetpack_{$transient}_change", $value );
348
		}
349
350
	}
351
352
	/**
353
	 * Enqueue the updates actions for full sync.
354
	 *
355
	 * @access public
356
	 *
357
	 * @param array   $config               Full sync configuration for this sync module.
358
	 * @param int     $max_items_to_enqueue Maximum number of items to enqueue.
359
	 * @param boolean $state                True if full sync has finished enqueueing this module, false otherwise.
360
	 * @return array Number of actions enqueued, and next module state.
361
	 */
362
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
363
		/**
364
		 * Tells the client to sync all updates to the server
365
		 *
366
		 * @since 4.2.0
367
		 *
368
		 * @param boolean Whether to expand updates (should always be true)
369
		 */
370
		do_action( 'jetpack_full_sync_updates', true );
371
372
		// The number of actions enqueued, and next module state (true == done).
373
		return array( 1, true );
374
	}
375
376
	/**
377
	 * Send the updates actions for full sync.
378
	 *
379
	 * @access public
380
	 *
381
	 * @param array $config Full sync configuration for this sync module.
382
	 * @param int   $send_until The timestamp until the current request can send.
383
	 * @param array $state This module Full Sync status.
384
	 *
385
	 * @return array This module Full Sync status.
386
	 */
387
	public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
388
		// we call this instead of do_action when sending immediately.
389
		$this->send_action( 'jetpack_full_sync_updates', array( true ) );
390
391
		// The number of actions enqueued, and next module state (true == done).
392
		return array( 'finished' => true );
393
	}
394
395
	/**
396
	 * Retrieve an estimated number of actions that will be enqueued.
397
	 *
398
	 * @access public
399
	 *
400
	 * @param array $config Full sync configuration for this sync module.
401
	 * @return array Number of items yet to be enqueued.
402
	 */
403
	public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
404
		return 1;
405
	}
406
407
	/**
408
	 * Retrieve the actions that will be sent for this module during a full sync.
409
	 *
410
	 * @access public
411
	 *
412
	 * @return array Full sync actions of this module.
413
	 */
414
	public function get_full_sync_actions() {
415
		return array( 'jetpack_full_sync_updates' );
416
	}
417
418
	/**
419
	 * Retrieve all updates that we're interested in.
420
	 *
421
	 * @access public
422
	 *
423
	 * @return array All updates.
424
	 */
425
	public function get_all_updates() {
426
		return array(
427
			'core'    => get_site_transient( 'update_core' ),
428
			'plugins' => get_site_transient( 'update_plugins' ),
429
			'themes'  => get_site_transient( 'update_themes' ),
430
		);
431
	}
432
433
	/**
434
	 * Remove unnecessary keys from synced updates data.
435
	 *
436
	 * @access public
437
	 *
438
	 * @param array $args Hook arguments.
439
	 * @return array $args Hook arguments.
440
	 */
441
	public function filter_update_keys( $args ) {
442
		$updates = $args[0];
443
444
		if ( isset( $updates->no_update ) ) {
445
			unset( $updates->no_update );
446
		}
447
448
		return $args;
449
	}
450
451
	/**
452
	 * Filter out upgrader object from the completed upgrader action args.
453
	 *
454
	 * @access public
455
	 *
456
	 * @param array $args Hook arguments.
457
	 * @return array $args Filtered hook arguments.
458
	 */
459
	public function filter_upgrader_process_complete( $args ) {
460
		array_shift( $args );
461
462
		return $args;
463
	}
464
465
	/**
466
	 * Expand the updates within a hook before they are serialized and sent to the server.
467
	 *
468
	 * @access public
469
	 *
470
	 * @param array $args The hook parameters.
471
	 * @return array $args The hook parameters.
472
	 */
473
	public function expand_updates( $args ) {
474
		if ( $args[0] ) {
475
			return $this->get_all_updates();
476
		}
477
478
		return $args;
479
	}
480
481
	/**
482
	 * Expand the themes within a hook before they are serialized and sent to the server.
483
	 *
484
	 * @access public
485
	 *
486
	 * @param array $args The hook parameters.
487
	 * @return array $args The hook parameters.
488
	 */
489
	public function expand_themes( $args ) {
490
		if ( ! isset( $args[0], $args[0]->response ) ) {
491
			return $args;
492
		}
493
		if ( ! is_array( $args[0]->response ) ) {
494
			// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
495
			trigger_error( 'Warning: Not an Array as expected but -> ' . wp_json_encode( $args[0]->response ) . ' instead', E_USER_WARNING );
496
			return $args;
497
		}
498
		foreach ( $args[0]->response as $stylesheet => &$theme_data ) {
499
			$theme              = wp_get_theme( $stylesheet );
500
			$theme_data['name'] = $theme->name;
501
		}
502
		return $args;
503
	}
504
505
	/**
506
	 * Perform module cleanup.
507
	 * Deletes any transients and options that this module uses.
508
	 * Usually triggered when uninstalling the plugin.
509
	 *
510
	 * @access public
511
	 */
512
	public function reset_data() {
513
		delete_option( self::UPDATES_CHECKSUM_OPTION_NAME );
514
	}
515
516
	/**
517
	 * Return Total number of objects.
518
	 *
519
	 * @param array $config Full Sync config.
520
	 *
521
	 * @return int total
522
	 */
523
	public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
524
		return 3;
525
	}
526
527
	/**
528
	 * Retrieve a set of updates by their IDs.
529
	 *
530
	 * @access public
531
	 *
532
	 * @param string $object_type Object type.
533
	 * @param array  $ids         Object IDs.
534
	 * @return array Array of objects.
535
	 */
536 View Code Duplication
	public function get_objects_by_id( $object_type, $ids ) {
537
		if ( empty( $ids ) || empty( $object_type ) || 'update' !== $object_type ) {
538
			return array();
539
		}
540
541
		$objects = array();
542
		foreach ( (array) $ids as $id ) {
543
			$object = $this->get_object_by_id( $object_type, $id );
544
545
			if ( 'all' === $id ) {
546
				// If all was requested it contains all updates and can simply be returned.
547
				return $object;
548
			}
549
			$objects[ $id ] = $object;
550
		}
551
552
		return $objects;
553
	}
554
555
	/**
556
	 * Retrieve a update by its id.
557
	 *
558
	 * @access public
559
	 *
560
	 * @param string $object_type Type of the sync object.
561
	 * @param string $id          ID of the sync object.
562
	 * @return mixed              Value of Update.
563
	 */
564
	public function get_object_by_id( $object_type, $id ) {
565
		if ( 'update' === $object_type ) {
566
567
			// Only whitelisted constants can be returned.
568
			if ( in_array( $id, array( 'core', 'plugins', 'themes' ), true ) ) {
569
				return get_site_transient( 'update_' . $id );
570
			} elseif ( 'all' === $id ) {
571
				return $this->get_all_updates();
572
			}
573
		}
574
575
		return false;
576
	}
577
578
}
579