Completed
Push — issue/2504 ( a7082f )
by Ravinder
944:19 queued 938:31
created

Give_Updates   F

Complexity

Total Complexity 66

Size/Duplication

Total Lines 594
Duplicated Lines 5.72 %

Coupling/Cohesion

Components 3
Dependencies 1

Importance

Changes 0
Metric Value
dl 34
loc 594
rs 3.12
c 0
b 0
f 0
wmc 66
lcom 3
cbo 1

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A register() 0 19 5
B get_updates() 0 33 8
A get_instance() 0 7 2
A setup() 0 15 1
A __register_plugin_addon_updates() 0 12 5
A __register_upgrade() 0 12 2
A __change_donations_label() 0 25 5
A __register_menu() 0 37 4
A get_db_update_count() 0 3 1
A get_total_db_update_count() 0 3 1
A render_complete_page() 0 3 1
A render_page() 0 3 1
A get_plugin_update_count() 0 3 1
A get_update_count() 0 6 1
A __flush_resume_updates() 0 8 1
C __give_ajax_updates() 34 105 12
A send_ajax_response() 0 31 3
A resume_updates() 0 9 3
A set_percentage() 0 7 3
A is_parent_updates_completed() 0 22 5

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 Give_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 Give_Updates, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Class Give_Updates
5
 *
6
 * @since 1.8.12
7
 */
8
class Give_Updates {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
9
10
	/**
11
	 * Instance.
12
	 *
13
	 * @since
14
	 * @access static
15
	 * @var
16
	 */
17
	static private $instance;
18
19
	/**
20
	 * Updates
21
	 *
22
	 * @since  1.8.12
23
	 * @access private
24
	 * @var array
25
	 */
26
	private $updates = array();
27
28
	/**
29
	 * Current update percentage number
30
	 *
31
	 * @since  1.8.12
32
	 * @access private
33
	 * @var array
34
	 */
35
	public $percentage = 0;
36
37
	/**
38
	 * Current update step number
39
	 *
40
	 * @since  1.8.12
41
	 * @access private
42
	 * @var array
43
	 */
44
	public $step = 1;
45
46
	/**
47
	 * Current update number
48
	 *
49
	 * @since  1.8.12
50
	 * @access private
51
	 * @var array
52
	 */
53
	public $update = 1;
54
55
	/**
56
	 * Singleton pattern.
57
	 *
58
	 * @since  1.8.12
59
	 * @access private
60
	 *
61
	 * @param Give_Updates .
62
	 */
63
	private function __construct() {
64
	}
65
66
	/**
67
	 * Register updates
68
	 *
69
	 * @since  1.8.12
70
	 * @access public
71
	 *
72
	 * @param array $args
73
	 */
74
	public function register( $args ) {
75
		$args_default = array(
76
			'id'       => '',
77
			'version'  => '',
78
			'callback' => '',
79
		);
80
81
		$args = wp_parse_args( $args, $args_default );
82
83
		// You can only register database upgrade.
84
		$args['type'] = 'database';
85
86
		// Bailout.
87
		if ( empty( $args['id'] ) || empty( $args['version'] ) || empty( $args['callback'] ) || ! is_callable( $args['callback'] ) ) {
88
			return;
89
		}
90
91
		$this->updates[ $args['type'] ][] = $args;
92
	}
93
94
95
	/**
96
	 * Get updates.
97
	 *
98
	 * @since  1.8.12
99
	 * @access public
100
	 *
101
	 * @param string $update_type Tye of update.
102
	 * @param string $status      Tye of update.
103
	 *
104
	 * @return array
105
	 */
106
	public function get_updates( $update_type = '', $status = 'all' ) {
107
		// return all updates.
108
		if ( empty( $update_type ) ) {
109
			return $this->updates;
110
		}
111
112
		// Get specific update.
113
		$updates = ! empty( $this->updates[ $update_type ] ) ? $this->updates[ $update_type ] : array();
114
115
		// Bailout.
116
		if ( empty( $updates ) ) {
117
			return $updates;
118
		}
119
120
		switch ( $status ) {
121
			case 'new':
122
				// Remove already completed updates.
123
				$completed_updates = give_get_completed_upgrades();
124
125
				if ( ! empty( $completed_updates ) ) {
126
					foreach ( $updates as $index => $update ) {
127
						if ( in_array( $update['id'], $completed_updates ) ) {
128
							unset( $updates[ $index ] );
129
						}
130
					}
131
					$updates = array_values( $updates );
132
				}
133
134
				break;
135
		}
136
137
		return $updates;
138
	}
139
140
	/**
141
	 * Get instance.
142
	 *
143
	 * @since
144
	 * @access static
145
	 * @return static
146
	 */
147
	static function get_instance() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
148
		if ( is_null( self::$instance ) ) {
149
			self::$instance = new self();
150
		}
151
152
		return self::$instance;
153
	}
154
155
	/**
156
	 *
157
	 * Setup hook
158
	 *
159
	 * @since  1.8.12
160
	 * @access public
161
	 */
162
	public function setup() {
163
		/**
164
		 * Setup hooks.
165
		 */
166
		add_action( 'init', array( $this, '__register_upgrade' ), 9999 );
167
		add_action( 'admin_init', array( $this, '__change_donations_label' ), 9999 );
168
		add_action( 'admin_menu', array( $this, '__register_menu' ), 9999 );
169
		add_action( 'give_set_upgrade_completed', array( $this, '__flush_resume_updates' ), 9999 );
170
		add_action( 'wp_ajax_give_do_ajax_updates', array( $this, '__give_ajax_updates' ) );
171
172
		/**
173
		 * Load file
174
		 */
175
		require_once GIVE_PLUGIN_DIR . 'includes/admin/upgrades/upgrade-functions.php';
176
	}
177
178
	/**
179
	 * Register plugin add-on updates.
180
	 *
181
	 * @since  1.8.12
182
	 * @access public
183
	 */
184
	public function __register_plugin_addon_updates() {
0 ignored issues
show
Coding Style introduced by
Method name "Give_Updates::__register_plugin_addon_updates" is invalid; only PHP magic methods should be prefixed with a double underscore
Loading history...
185
		$addons         = give_get_plugins();
186
		$plugin_updates = get_plugin_updates();
187
188
		foreach ( $addons as $key => $info ) {
189
			if ( 'active' != $info['Status'] || 'add-on' != $info['Type'] || empty( $plugin_updates[ $key ] ) ) {
190
				continue;
191
			}
192
193
			$this->updates['plugin'][] = array_merge( $info, (array) $plugin_updates[ $key ] );
194
		}
195
	}
196
197
198
	/**
199
	 * Fire custom action hook to register updates
200
	 *
201
	 * @since  1.8.12
202
	 * @access public
203
	 */
204
	public function __register_upgrade() {
0 ignored issues
show
Coding Style introduced by
Method name "Give_Updates::__register_upgrade" is invalid; only PHP magic methods should be prefixed with a double underscore
Loading history...
205
		if ( ! is_admin() ) {
206
			return;
207
		}
208
209
		/**
210
		 * Fire the hook
211
		 *
212
		 * @since 1.8.12
213
		 */
214
		do_action( 'give_register_updates', $this );
215
	}
216
217
	/**
218
	 * Rename `Donations` menu title if updates exists
219
	 *
220
	 * @since  1.8.12
221
	 * @access public
222
	 */
223
	function __change_donations_label() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Coding Style introduced by
Method name "Give_Updates::__change_donations_label" is invalid; only PHP magic methods should be prefixed with a double underscore
Loading history...
224
		global $menu;
225
		global $submenu;
226
227
		// Bailout.
228
		if ( empty( $menu ) || ! $this->get_update_count() ) {
229
			return;
230
		}
231
232
		foreach ( $menu as $index => $menu_item ) {
233
			if ( 'edit.php?post_type=give_forms' !== $menu_item[2] ) {
234
				continue;
235
			}
236
237
			$menu[ $index ][0] = sprintf(
238
				__( 'Donations %s', 'give' ),
239
				sprintf(
240
					'<span class="update-plugins count-%1$d"><span class="plugin-count">%1$d</span></span>',
241
					$this->get_update_count()
242
				)
243
			);
244
245
			break;
246
		}
247
	}
248
249
	/**
250
	 * Register updates menu
251
	 *
252
	 * @since  1.8.12
253
	 * @access public
254
	 */
255
	public function __register_menu() {
0 ignored issues
show
Coding Style introduced by
Method name "Give_Updates::__register_menu" is invalid; only PHP magic methods should be prefixed with a double underscore
Loading history...
256
257
		// Load plugin updates.
258
		$this->__register_plugin_addon_updates();
259
260
		// Bailout.
261
		if ( ! $this->get_update_count() ) {
262
			// Show complete update message if still on update setting page.
263
			if ( isset( $_GET['page'] ) && 'give-updates' === $_GET['page'] ) {
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
264
				// Upgrades
265
				add_submenu_page(
266
					'edit.php?post_type=give_forms',
267
					esc_html__( 'Give Updates Complete', 'give' ),
268
					__( 'Updates', 'give' ),
269
					'manage_give_settings',
270
					'give-updates',
271
					array( $this, 'render_complete_page' )
272
				);
273
			}
274
275
			return;
276
		}
277
278
		// Upgrades
279
		add_submenu_page(
280
			'edit.php?post_type=give_forms',
281
			esc_html__( 'Give Updates', 'give' ),
282
			sprintf(
283
				'%1$s <span class="update-plugins count-%2$d"><span class="plugin-count">%2$d</span></span>',
284
				__( 'Updates', 'give' ),
285
				$this->get_update_count()
286
			),
287
			'manage_give_settings',
288
			'give-updates',
289
			array( $this, 'render_page' )
290
		);
291
	}
292
293
	/**
294
	 * Get total pending updates count
295
	 *
296
	 * @since  1.8.12
297
	 * @access public
298
	 *
299
	 * @return int
300
	 */
301
	public function get_db_update_count() {
302
		return count( $this->get_updates( 'database', 'new' ) );
303
	}
304
305
	/**
306
	 * Get total updates count
307
	 *
308
	 * @since  1.8.18
309
	 * @access public
310
	 *
311
	 * @return int
312
	 */
313
	public function get_total_db_update_count() {
314
		return count( $this->get_updates( 'database', 'all' ) );
315
	}
316
317
	/**
318
	 * Render Give Updates Completed page
319
	 *
320
	 * @since  1.8.12
321
	 * @access public
322
	 */
323
	public function render_complete_page() {
324
		include_once GIVE_PLUGIN_DIR . 'includes/admin/upgrades/views/upgrades-complete.php';
325
	}
326
327
	/**
328
	 * Render Give Updates page
329
	 *
330
	 * @since  1.8.12
331
	 * @access public
332
	 */
333
	public function render_page() {
334
		include_once GIVE_PLUGIN_DIR . 'includes/admin/upgrades/views/upgrades.php';
335
	}
336
337
	/**
338
	 * Get addon update count.
339
	 *
340
	 * @since  1.8.12
341
	 * @access public
342
	 * @return int
343
	 */
344
	public function get_plugin_update_count() {
345
		return count( $this->get_updates( 'plugin' ) );
346
	}
347
348
	/**
349
	 * Get total update count
350
	 *
351
	 * @since  1.8.12
352
	 * @access public
353
	 *
354
	 * @return int
355
	 */
356
	public function get_update_count() {
357
		$db_update_count     = $this->get_db_update_count();
358
		$plugin_update_count = $this->get_plugin_update_count();
359
360
		return ( $db_update_count + $plugin_update_count );
361
	}
362
363
364
	/**
365
	 * Delete resume updates
366
	 *
367
	 * @since  1.8.12
368
	 * @access public
369
	 */
370
	public function __flush_resume_updates() {
0 ignored issues
show
Coding Style introduced by
Method name "Give_Updates::__flush_resume_updates" is invalid; only PHP magic methods should be prefixed with a double underscore
Loading history...
371
		delete_option( 'give_doing_upgrade' );
372
		update_option( 'give_version', preg_replace( '/[^0-9.].*/', '', GIVE_VERSION ) );
373
374
		// Reset counter.
375
		$this->step = $this->percentage = 0;
0 ignored issues
show
Documentation Bug introduced by
It seems like 0 of type integer is incompatible with the declared type array of property $percentage.

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...
Documentation Bug introduced by
It seems like $this->percentage = 0 of type integer is incompatible with the declared type array of property $step.

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...
376
		++ $this->update;
377
	}
378
379
	/**
380
	 *  Process give updates.
381
	 *
382
	 * @since  1.8.12
383
	 * @access public
384
	 */
385
	public function __give_ajax_updates() {
0 ignored issues
show
Coding Style introduced by
Method name "Give_Updates::__give_ajax_updates" is invalid; only PHP magic methods should be prefixed with a double underscore
Loading history...
386
		// Disable cache.
387
		Give_Cache::disable();
388
389
		// Check permission.
390
		if ( ! current_user_can( 'manage_give_settings' ) ) {
391
			$this->send_ajax_response(
392
				array(
393
					'message' => esc_html__( 'You do not have permission to do Give upgrades.', 'give' ),
394
				),
395
				'error'
396
			);
397
		}
398
399
		ignore_user_abort( true );
400
401
		if ( ! give_is_func_disabled( 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) {
402
			set_time_limit( 0 );
403
		}
404
		
405
		// Set params.
406
		$this->step   = absint( $_POST['step'] );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-validated input variable: $_POST
Loading history...
407
		$this->update = absint( $_POST['update'] );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-validated input variable: $_POST
Loading history...
408
409
		// Bailout: step and update must be positive and greater then zero.
410
		if ( ! $this->step ) {
411
			$this->send_ajax_response(
412
				array(
413
					'message'    => __( 'Please reload this page and try again', 'give' ),
414
					'heading'    => '',
415
					'percentage' => 0,
416
				),
417
				'error'
418
			);
419
		}
420
421
		// Get updates.
422
		$updates = $this->get_updates( 'database', 'new' );
423
424
		// Bailout if we do not have nay updates.
425 View Code Duplication
		if ( empty( $updates ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
426
			$this->send_ajax_response(
427
				array(
428
					'message'    => __( 'The database is already up to date.', 'give' ),
429
					'heading'    => __( 'Updates Completed.', 'give' ),
430
					'percentage' => 0,
431
				),
432
				'success'
433
			);
434
		}
435
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
436
437
		// Process update.
438
		foreach ( $updates as $index => $update ) {
439
			// Check if update depend upon any other update.
440 View Code Duplication
			if ( ! $this->is_parent_updates_completed( $update ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
441
				if ( 1 === count( $updates ) ) {
442
					$this->send_ajax_response(
443
						array(
444
							'message'    => __( 'Error occurred while running current update because it contains invalid update dependencies', 'give' ),
445
							'heading'    => '',
446
							'percentage' => 0,
447
						),
448
						'error'
449
					);
450
				}
451
452
				continue;
453
			}
454
455
			// Run update.
456
			if ( is_array( $update['callback'] ) ) {
457
				$update['callback'][0]->$update['callback'][1]();
458
			} else {
459
				$update['callback']();
460
			}
461
462
			// Check if current update completed or not.
463
			if ( give_has_upgrade_completed( $update['id'] ) ) {
464 View Code Duplication
				if ( 1 === count( $updates ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
465
					$this->send_ajax_response(
466
						array(
467
							'message'    => __( 'Database updated successfully.', 'give' ),
468
							'heading'    => __( 'Updates Completed.', 'give' ),
469
							'percentage' => 0,
470
						),
471
						'success'
472
					);
473
				}
474
			}
475
476
			$doing_upgrade_args = array(
477
				'update_info' => $update,
478
				'step'        => ++ $this->step,
479
				'update'      => $this->update,
480
				'heading'     => sprintf( 'Update %s of {update_count}', $this->update ),
481
				'percentage'  => $this->percentage,
482
			);
483
484
			// Cache upgrade.
485
			update_option( 'give_doing_upgrade', $doing_upgrade_args );
486
487
			$this->send_ajax_response( $doing_upgrade_args );
488
		}// End foreach().
489
	}
490
491
	/**
492
	 * Send ajax response
493
	 *
494
	 * @since  1.8.12
495
	 * @access public
496
	 *
497
	 * @param        $data
498
	 * @param string $type
499
	 */
500
	public function send_ajax_response( $data, $type = '' ) {
501
		$default = array(
502
			'message'    => '',
503
			'heading'    => '',
504
			'percentage' => 0,
505
			'step'       => 0,
506
			'update'     => 0,
507
		);
508
509
		// Set data.
510
		$data = wp_parse_args( $data, $default );
511
512
		// Enable cache.
513
		Give_Cache::enable();
514
515
		switch ( $type ) {
516
			case 'success':
517
				wp_send_json_success( $data );
518
				break;
519
520
			case 'error':
521
				wp_send_json_error( $data );
522
				break;
523
524
			default:
525
				wp_send_json( array(
526
					'data' => $data,
527
				) );
528
				break;
529
		}
530
	}
531
532
533
	/**
534
	 * Resume updates
535
	 *
536
	 * @since  1.8.12
537
	 * @access public
538
	 *
539
	 * @return bool|int
540
	 */
541
	public function resume_updates() {
542
		$status = false;
543
544
		if ( $update = get_option( 'give_doing_upgrade' ) ) {
545
			$status = ! empty( $update['step'] ) ? $update['step'] : $status;
546
		}
547
548
		return $status;
549
	}
550
551
552
	/**
553
	 * Set current update percentage.
554
	 *
555
	 * @since  1.8.12
556
	 * @access public
557
	 *
558
	 * @param $total
559
	 * @param $current_total
560
	 */
561
	public function set_percentage( $total, $current_total ) {
562
		// Set percentage.
563
		$this->percentage = $total ? ( ( $current_total ) / $total ) * 100 : 0;
0 ignored issues
show
Documentation Bug introduced by
It seems like $total ? $current_total / $total * 100 : 0 of type integer or double is incompatible with the declared type array of property $percentage.

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...
564
565
		// Verify percentage.
566
		$this->percentage = ( 100 < $this->percentage ) ? 100 : $this->percentage;
0 ignored issues
show
Documentation Bug introduced by
It seems like 100 < $this->percentage ? 100 : $this->percentage of type integer or double is incompatible with the declared type array of property $percentage.

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...
567
	}
568
569
	/**
570
	 * Check if parent update completed or not.
571
	 *
572
	 * @since  2.0
573
	 * @access private
574
	 *
575
	 * @param array $update
576
	 *
577
	 * @return bool
578
	 */
579
	private function is_parent_updates_completed( $update ) {
580
		// Bailout.
581
		if ( empty( $update['depend'] ) ) {
582
			return true;
583
		}
584
585
		$is_dependency_completed = true;
586
587
		// Change param to array.
588
		if ( is_string( $update['depend'] ) ) {
589
			$update['depend'] = array( $update['depend'] );
590
		}
591
592
		foreach ( $update['depend'] as $depend ) {
593
			if ( ! give_has_upgrade_completed( $depend ) ) {
594
				$is_dependency_completed = false;
595
				break;
596
			}
597
		}
598
599
		return $is_dependency_completed;
600
	}
601
}
602
603
Give_Updates::get_instance()->setup();
604