Give_Background_Updater   C
last analyzed

Complexity

Total Complexity 57

Size/Duplication

Total Lines 450
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 450
rs 5.04
c 0
b 0
f 0
wmc 57
lcom 1
cbo 4

15 Methods

Rating   Name   Duplication   Size   Complexity  
A dispatch() 0 7 3
A get_all_batch() 0 3 1
A has_queue() 0 3 1
A lock_process() 0 29 3
A handle_cron_healthcheck() 0 15 4
A schedule_event() 0 5 3
D task() 0 132 15
A complete() 0 20 3
A get_memory_limit() 0 15 4
A maybe_handle() 0 20 4
B handle() 0 44 11
A is_paused_process() 0 8 1
A get_identifier() 0 3 1
A get_cron_identifier() 0 3 1
A flush_cache() 0 17 2

How to fix   Complexity   

Complex Class

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

1
<?php
2
/**
3
 * Background Updater
4
 *
5
 * Uses https://github.com/A5hleyRich/wp-background-processing to handle DB
6
 * updates in the background.
7
 *
8
 * @class    Give_Background_Updater
9
 * @version  2.0.0
10
 * @package  Give/Classes
11
 * @category Class
12
 * @author   GiveWP
13
 */
14
if ( ! defined( 'ABSPATH' ) ) {
15
	exit;
16
}
17
18
/**
19
 * Give_Background_Updater Class.
20
 */
21
class Give_Background_Updater extends WP_Background_Process {
22
23
	/**
24
	 * @var string
25
	 */
26
	protected $action = 'give_db_updater';
27
28
	/**
29
	 * Dispatch updater.
30
	 *
31
	 * Updater will still run via cron job if this fails for any reason.
32
	 */
33
	public function dispatch() {
34
		if ( give_test_ajax_works() ) {
35
			parent::dispatch();
36
		} elseif ( wp_doing_ajax() ) {
37
			$this->maybe_handle();
38
		}
39
	}
40
41
42
	/**
43
	 * Get all batches.
44
	 *
45
	 * @since  2.0
46
	 * @access public
47
	 * @return stdClass
48
	 */
49
	public function get_all_batch() {
50
		return parent::get_batch();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (get_batch() instead of get_all_batch()). Are you sure this is correct? If so, you might want to change this to $this->get_batch().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
51
	}
52
53
	/**
54
	 * Is queue empty
55
	 *
56
	 * @since 2.0.3
57
	 *
58
	 * @return bool
59
	 */
60
	public function has_queue() {
61
		return ( ! parent::is_queue_empty() );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (is_queue_empty() instead of has_queue()). Are you sure this is correct? If so, you might want to change this to $this->is_queue_empty().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
62
	}
63
64
65
	/**
66
	 * Lock process
67
	 *
68
	 * Lock the process so that multiple instances can't run simultaneously.
69
	 * Override if applicable, but the duration should be greater than that
70
	 * defined in the time_exceeded() method.
71
	 *
72
	 *
73
	 * @since 2.0.3
74
	 */
75
	protected function lock_process() {
76
		// Check if admin want to pause upgrade.
77
		if( get_option('give_pause_upgrade') ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
78
			self::flush_cache();
79
80
			delete_option( 'give_paused_batches' );
81
82
			Give_Updates::get_instance()->__pause_db_update( true );
83
84
			delete_option('give_pause_upgrade');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
85
86
			/**
87
			 * Fire action when pause db updates
88
			 *
89
			 * @since 2.0.1
90
			 */
91
			do_action( 'give_pause_db_upgrade', Give_Updates::get_instance() );
92
93
			wp_die();
94
		}
95
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
96
97
		$this->start_time = time(); // Set start time of current process.
98
99
		$lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
100
		$lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
101
102
		set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
103
	}
104
105
	/**
106
	 * Handle cron healthcheck
107
	 *
108
	 * Restart the background process if not already running
109
	 * and data exists in the queue.
110
	 */
111
	public function handle_cron_healthcheck() {
112
		if ( $this->is_process_running() || $this->is_paused_process()  ) {
113
			// Background process already running.
114
			return;
115
		}
116
117
		if ( $this->is_queue_empty() ) {
118
			// No data to process.
119
			$this->clear_scheduled_event();
120
121
			return;
122
		}
123
124
		$this->handle();
125
	}
126
127
	/**
128
	 * Schedule fallback event.
129
	 */
130
	protected function schedule_event() {
131
		if ( ! wp_next_scheduled( $this->cron_hook_identifier ) && ! $this->is_paused_process() ) {
132
			wp_schedule_event( time() + 10, $this->cron_interval_identifier, $this->cron_hook_identifier );
133
		}
134
	}
135
136
	/**
137
	 * Task
138
	 *
139
	 * Override this method to perform any actions required on each
140
	 * queue item. Return the modified item for further processing
141
	 * in the next pass through. Or, return false to remove the
142
	 * item from the queue.
143
	 *
144
	 * @param array $update Update info
145
	 *
146
	 * @return mixed
147
	 */
148
	protected function task( $update ) {
149
		// Pause upgrade immediately if admin pausing upgrades.
150
		if( $this->is_paused_process() ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
151
			wp_die();
152
		}
153
154
		if ( empty( $update ) ) {
155
			return false;
156
		}
157
158
		// Delete cache.
159
		self::flush_cache();
160
161
		/* @var  Give_Updates $give_updates */
162
		$give_updates  = Give_Updates::get_instance();
163
		$resume_update = get_option(
164
			'give_doing_upgrade',
165
0 ignored issues
show
Coding Style introduced by
There should be no empty lines in a multi-line function call.
Loading history...
166
			// Default update.
167
			array(
168
				'update_info'      => $update,
169
				'step'             => 1,
170
				'update'           => 1,
171
				'heading'          => sprintf( 'Update %s of {update_count}', 1 ),
172
				'percentage'       => $give_updates->percentage,
173
				'total_percentage' => 0,
174
			)
175
		);
176
177
		// Continuously skip update if previous update does not complete yet.
178
		if (
179
			$resume_update['update_info']['id'] !== $update['id'] &&
180
			! give_has_upgrade_completed( $resume_update['update_info']['id'] )
181
		) {
182
			return $update;
183
		}
184
185
		// Set params.
186
		$resume_update['update_info'] = $update;
187
		$give_updates->step           = absint( $resume_update['step'] );
188
		$give_updates->update         = absint( $resume_update['update'] );
189
		$is_parent_update_completed   = $give_updates->is_parent_updates_completed( $update );
190
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
191
192
		// Skip update if dependency update does not complete yet.
193
		if ( empty( $is_parent_update_completed ) ) {
194
			// @todo: set error when you have only one update with invalid dependency
195
			if ( ! is_null( $is_parent_update_completed ) ) {
196
				return $update;
197
			}
198
199
			return false;
200
		}
201
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
202
203
		// Pause upgrade immediately if found following:
204
		// 1. Running update number greater then total update count
205
		// 2. Processing percentage greater then 100%
206
		if( (
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
207
			101 < $resume_update['total_percentage'] ) ||
208
		    ( $give_updates->get_total_db_update_count() < $resume_update['update'] ) ||
209
		    ! in_array( $resume_update['update_info']['id'], $give_updates->get_update_ids() )
210
		) {
211
			if( ! $this->is_paused_process() ){
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
212
				$give_updates->__pause_db_update(true);
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
213
			}
214
215
			update_option( 'give_upgrade_error', 1, false );
216
217
			$log_data = 'Update Task' . "\n";
218
			$log_data .= "Total update count: {$give_updates->get_total_db_update_count()}\n";
219
			$log_data .= 'Update IDs: ' . print_r( $give_updates->get_update_ids() , true );
0 ignored issues
show
introduced by
The use of function print_r() is discouraged
Loading history...
220
			$log_data .= 'Update: ' . print_r( $resume_update , true );
0 ignored issues
show
introduced by
The use of function print_r() is discouraged
Loading history...
221
222
			Give()->logs->add( 'Update Error', $log_data, 0, 'update' );
223
224
			wp_die();
225
		}
226
227
		// Disable cache.
228
		Give_Cache::disable();
229
230
		try{
231
			// Run update.
232
			if ( is_array( $update['callback'] ) ) {
233
				$object      = $update['callback'][0];
234
				$method_name = $update['callback'][1];
235
236
				$object->$method_name();
237
238
			} else {
239
				$update['callback']();
240
			}
241
		} catch ( Exception $e ){
242
243
			if( ! $this->is_paused_process() ){
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
244
				$give_updates->__pause_db_update(true);
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
245
			}
246
247
			$log_data = 'Update Task' . "\n";
248
			$log_data .= print_r( $resume_update, true ) . "\n\n";
0 ignored issues
show
introduced by
The use of function print_r() is discouraged
Loading history...
249
			$log_data .= "Error\n {$e->getMessage()}";
250
251
			Give()->logs->add( 'Update Error', $log_data, 0, 'update' );
252
			update_option( 'give_upgrade_error', 1, false );
253
254
			wp_die();
255
		}
256
257
		// Set update info.
258
		$doing_upgrade_args = array(
259
			'update_info'      => $update,
260
			'step'             => ++ $give_updates->step,
261
			'update'           => $give_updates->update,
262
			'heading'          => sprintf( 'Update %s of %s', $give_updates->update, get_option( 'give_db_update_count' ) ),
263
			'percentage'       => $give_updates->percentage,
264
			'total_percentage' => $give_updates->get_db_update_processing_percentage(),
265
		);
266
267
		// Cache upgrade.
268
		update_option( 'give_doing_upgrade', $doing_upgrade_args, false );
269
270
		// Enable cache.
271
		Give_Cache::enable();
272
273
		// Check if current update completed or not.
274
		if ( give_has_upgrade_completed( $update['id'] ) ) {
275
			return false;
276
		}
277
278
		return $update;
279
	}
280
281
	/**
282
	 * Complete
283
	 *
284
	 * Override if applicable, but ensure that the below actions are
285
	 * performed, or, call parent::complete().
286
	 */
287
	public function complete() {
288
		if ( $this->is_paused_process() ) {
289
			return false;
290
		}
291
292
		parent::complete();
293
294
		delete_option( 'give_pause_upgrade' );
295
		delete_option( 'give_upgrade_error' );
296
		delete_option( 'give_db_update_count' );
297
		delete_option( 'give_doing_upgrade' );
298
		add_option( 'give_show_db_upgrade_complete_notice', 1, '', false );
299
300
		// Flush cache.
301
		Give_Cache::flush_cache( true );
302
303
		if ( $cache_keys = Give_Cache::get_options_like( '' ) ) {
304
			Give_Cache::delete( $cache_keys );
305
		}
306
	}
307
308
	/**
309
	 * Get memory limit
310
	 *
311
	 * @return int
312
	 */
313
	protected function get_memory_limit() {
314
		if ( function_exists( 'ini_get' ) ) {
315
			$memory_limit = ini_get( 'memory_limit' );
316
		} else {
317
			// Sensible default.
318
			$memory_limit = '128M';
319
		}
320
321
		if ( ! $memory_limit || '-1' === $memory_limit ) {
322
			// Unlimited, set to 32GB.
323
			$memory_limit = '32000M';
324
		}
325
326
		return intval( $memory_limit ) * 1024 * 1024;
327
	}
328
329
	/**
330
	 * Maybe process queue
331
	 *
332
	 * Checks whether data exists within the queue and that
333
	 * the process is not already running.
334
	 */
335
	public function maybe_handle() {
336
		// Don't lock up other requests while processing
337
		session_write_close();
0 ignored issues
show
introduced by
The use of PHP session function session_write_close() is prohibited.
Loading history...
338
339
		if ( $this->is_process_running() || $this->is_paused_process() ) {
340
			// Background process already running.
341
			wp_die();
342
		}
343
344
		if ( $this->is_queue_empty() ) {
345
			// No data to process.
346
			wp_die();
347
		}
348
349
		check_ajax_referer( $this->identifier, 'nonce' );
350
351
		$this->handle();
352
353
		wp_die();
354
	}
355
356
	/**
357
	 * Handle
358
	 *
359
	 * Pass each queue item to the task handler, while remaining
360
	 * within server memory and time limit constraints.
361
	 */
362
	protected function handle() {
363
		$this->lock_process();
364
365
		do {
366
			$batch = $this->get_batch();
367
368
			foreach ( $batch->data as $key => $value ) {
369
				$task = $this->task( $value );
370
371
				if ( false !== $task ) {
372
					$batch->data[ $key ] = $task;
373
				} else {
374
					unset( $batch->data[ $key ] );
375
				}
376
377
				if ( $this->time_exceeded() || $this->memory_exceeded() ) {
378
					// Batch limits reached.
379
					break;
380
				}
381
			}
382
383
			// Update or delete current batch.
384
			if ( ! empty( $batch->data ) ) {
385
				$this->update( $batch->key, $batch->data );
386
			} else {
387
				$this->delete( $batch->key );
388
			}
389
		} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
390
391
		$this->unlock_process();
392
393
		// Start next batch or complete process.
394
		if ( ! $this->is_queue_empty() ) {
395
396
			// Dispatch only if ajax works.
397
			if( give_test_ajax_works() ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
398
				$this->dispatch();
399
			}
400
		} else {
401
			$this->complete();
402
		}
403
404
		wp_die();
405
	}
406
407
408
	/**
409
	 * Check if backgound upgrade paused or not.
410
	 *
411
	 * @since 2.0
412
	 * @access public
413
	 * @return bool
414
	 */
415
	public function is_paused_process(){
416
		// Delete cache.
417
		wp_cache_delete( 'give_paused_batches', 'options' );
418
419
		$paused_batches = get_option('give_paused_batches');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
420
421
		return ! empty( $paused_batches );
422
	}
423
424
425
	/**
426
	 * Get identifier
427
	 *
428
	 * @since  2.0
429
	 * @access public
430
	 * @return mixed|string
431
	 */
432
	public function get_identifier() {
433
		return $this->identifier;
434
	}
435
436
	/**
437
	 * Get cron identifier
438
	 *
439
	 * @since  2.0
440
	 * @access public
441
	 * @return mixed|string
442
	 */
443
	public function get_cron_identifier() {
444
		return $this->cron_hook_identifier;
445
	}
446
447
448
	/**
449
	 * Flush background update related cache to prevent task to go to stalled state.
450
	 *
451
	 * @since 2.0.3
452
	 */
453
	public static function flush_cache() {
454
455
		$options = array(
456
			'give_completed_upgrades',
457
			'give_doing_upgrade',
458
			'give_paused_batches',
459
			'give_upgrade_error',
460
			'give_db_update_count',
461
			'give_pause_upgrade',
462
			'give_show_db_upgrade_complete_notice',
463
		);
464
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
465
466
		foreach ( $options as $option ) {
467
			wp_cache_delete( $option, 'options' );
468
		}
469
	}
470
}
471