Completed
Pull Request — master (#1832)
by Devin
04:50
created

Give_Tools_Recount_All_Stats::get_data()   F

Complexity

Conditions 17
Paths 752

Size

Total Lines 105
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 55
nc 752
nop 0
dl 0
loc 105
rs 2.3769
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 23 and the first side effect is on line 15.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Recount all donation counts and income stats
4
 *
5
 * This class handles batch processing of recounting donations and income stat totals
6
 *
7
 * @subpackage Admin/Tools/Give_Tools_Recount_All_Stats
8
 * @copyright  Copyright (c) 2016, WordImpress
9
 * @license    https://opensource.org/licenses/gpl-license GNU Public License
10
 * @since      1.5
11
 */
12
13
// Exit if accessed directly.
14
if (! defined('ABSPATH') ) {
15
	exit;
16
}
17
18
/**
19
 * Give_Tools_Recount_All_Stats Class
20
 *
21
 * @since 1.5
22
 */
23
class Give_Tools_Recount_All_Stats extends Give_Batch_Export {
24
25
	/**
26
	 * Our export type. Used for export-type specific filters/actions
27
	 *
28
	 * @since 1.5
29
	 * @var   string
30
	 */
31
	public $export_type = '';
32
33
	/**
34
	 * Allows for a non-form batch processing to be run.
35
	 *
36
	 * @since 1.5
37
	 * @var   bool
38
	 */
39
	public $is_void = true;
40
41
	/**
42
	 * Sets the number of items to pull on each step
43
	 *
44
	 * @since 1.5
45
	 * @var   int
46
	 */
47
	public $per_step = 30;
48
49
	/**
50
	 * Display message on completing recount process
51
	 *
52
	 * @since 1.8.9
53
	 * @var   string
54
	 */
55
	public $message = '';
56
57
	/**
58
	 * Sets donation form id for recalculation
59
	 *
60
	 * @since 1.8.9
61
	 * @var   int
62
	 */
63
	protected $form_id = 0;
64
65
	/**
66
	 * Is Recount process completed
67
	 *
68
	 * @since 1.8.9
69
	 * @var   bool
70
	 */
71
	public $done = false;
72
73
	/**
74
	 * Get the recount all stats data
75
	 *
76
	 * @access public
77
	 * @since  1.5
78
	 *
79
	 * @return bool
80
	 */
81
	public function get_data() {
82
83
		$totals             = $this->get_stored_data( 'give_temp_recount_all_stats' );
84
		$payment_items      = $this->get_stored_data( 'give_temp_payment_items' );
85
		$processed_payments = $this->get_stored_data( 'give_temp_processed_payments' );
86
		$accepted_statuses  = apply_filters( 'give_recount_accepted_statuses', array( 'publish' ) );
87
88
		if ( false === $totals ) {
89
			$totals = array();
90
		}
91
92
		if ( false === $payment_items ) {
93
			$payment_items = array();
94
		}
95
96
		if ( false === $processed_payments ) {
97
			$processed_payments = array();
98
		}
99
100
		$all_forms = $this->get_stored_data( 'give_temp_form_ids' );
101
102
		$payments = $this->get_stored_data( 'give_temp_all_payments_data' );
103
104
		if( false === $payments ) {
105
			$args = apply_filters( 'give_recount_form_stats_args', array(
106
				'post_parent__in' => $all_forms,
107
				'number'          => $this->per_step,
108
				'status'          => 'publish',
109
				'paged'           => $this->step,
110
				'output'          => 'payments',
111
			) );
112
113
			$payments_query = new Give_Payments_Query( $args );
114
			$payments       = $payments_query->get_payments();
115
		}
116
117
		if ( $payments ) {
118
119
			//Loop through payments
120
			foreach ( $payments as $payment ) {
121
122
				// Prevent payments that have all ready been retrieved from a previous sales log from counting again.
123
				if ( in_array( $payment->ID, $processed_payments ) ) {
124
					continue;
125
				}
126
127
				// Verify accepted status
128
				if ( ! in_array( $payment->post_status, $accepted_statuses ) ) {
129
					$processed_payments[] = $payment->ID;
130
					continue;
131
				}
132
133
				$payment_item = $payment_items[ $payment->ID ];
134
135
				$form_id = isset( $payment_item['id'] ) ? $payment_item['id'] : '';
136
137
				//Must have a form ID
138
				if ( empty( $form_id ) ) {
139
					continue;
140
				}
141
142
				//Form ID must be within $all_forms array to be validated
143
				if ( ! in_array( $form_id, $all_forms ) ) {
144
					continue;
145
				}
146
147
				// Set Sales count
148
				$totals[ $form_id ]['sales'] = isset( $totals[ $form_id ]['sales'] ) ?
149
					++ $totals[ $form_id ]['sales'] :
150
					1;
151
152
				// Set Total Earnings
153
				$totals[ $form_id ]['earnings'] = isset( $totals[ $form_id ]['earnings'] ) ?
154
					( $totals[ $form_id ]['earnings'] + $payment_item['price'] ) :
155
					$payment_item['price'];
156
157
				$processed_payments[] = $payment->ID;
158
			}
159
160
			// Get the list of form ids which does not contain any payment record.
161
			$remaining_form_ids = array_diff( $all_forms, array_keys($totals) );
162
			foreach( $remaining_form_ids as $form_id) {
163
				//If array key doesn't exist, create it
164
				if ( ! array_key_exists( $form_id, $totals ) ) {
165
					$totals[ $form_id ] = array(
166
						'sales'    => (int) 0,
167
						'earnings' => (float) 0,
168
					);
169
				}
170
			}
171
172
			$this->store_data( 'give_temp_processed_payments', $processed_payments );
173
			$this->store_data( 'give_temp_recount_all_stats', $totals );
174
175
			return true;
176
		}
177
178
		foreach ( $totals as $key => $stats ) {
179
			give_update_meta( $key, '_give_form_sales', $stats['sales'] );
180
			give_update_meta( $key, '_give_form_earnings', $stats['earnings'] );
181
		}
182
183
		return false;
184
185
	}
186
187
	/**
188
	 * Return the calculated completion percentage
189
	 *
190
	 * @since 1.5
191
	 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
192
	 */
193
	public function get_percentage_complete() {
194
195
		$total = $this->get_stored_data( 'give_recount_all_total' );
196
197
		if ( false === $total ) {
198
			$this->pre_fetch();
199
			$total = $this->get_stored_data( 'give_recount_all_total' );
200
		}
201
202
		$percentage = 100;
203
204
		if ( $total > 0 ) {
205
			$percentage = ( ( $this->per_step * $this->step ) / $total ) * 100;
206
		}
207
208
		if ( $percentage > 100 ) {
209
			$percentage = 100;
210
		}
211
212
		return $percentage;
213
	}
214
215
	/**
216
	 * Set the properties specific to the payments export
217
	 *
218
	 * @since 1.5
219
	 *
220
	 * @param array $request The Form Data passed into the batch processing
221
	 */
222
	public function set_properties( $request ) {
223
		$this->form_id = isset( $request['form_id'] ) ? sanitize_text_field( $request['form_id'] ) : false;
224
	}
225
226
	/**
227
	 * Process a step
228
	 *
229
	 * @since 1.5
230
	 * @return bool
231
	 */
232
	public function process_step() {
233
234
		if ( ! $this->can_export() ) {
235
			wp_die( esc_html__( 'You do not have permission to recount stats.', 'give' ), esc_html__( 'Error', 'give' ), array( 'response' => 403 ) );
236
		}
237
238
		$had_data = $this->get_data();
239
240
		if ( $had_data ) {
241
			$this->done = false;
242
243
			return true;
244
		} else {
245
			$this->delete_data( 'give_recount_total_' . $this->form_id );
246
			$this->delete_data( 'give_recount_all_total' );
247
			$this->delete_data( 'give_temp_recount_all_stats' );
248
			$this->delete_data( 'give_temp_payment_items' );
249
			$this->delete_data( 'give_temp_form_ids' );
250
			$this->delete_data( 'give_temp_processed_payments' );
251
			$this->done    = true;
252
			$this->message = esc_html__( 'Donation form income amounts and donation counts stats successfully recounted.', 'give' );
253
254
			return false;
255
		}
256
	}
257
258
	public function headers() {
259
		ignore_user_abort( true );
260
261
		if ( ! give_is_func_disabled( 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) {
262
			set_time_limit( 0 );
263
		}
264
	}
265
266
	/**
267
	 * Perform the export
268
	 *
269
	 * @access public
270
	 * @since 1.5
271
	 * @return void
272
	 */
273
	public function export() {
274
275
		// Set headers
276
		$this->headers();
277
278
		give_die();
279
	}
280
281
	/**
282
	 * Pre Fetch Data
283
	 *
284
	 * @access public
285
	 * @since 1.5
286
	 */
287
	public function pre_fetch() {
288
289
		if ( $this->step == 1 ) {
290
			$this->delete_data( 'give_temp_recount_all_total' );
291
			$this->delete_data( 'give_temp_recount_all_stats' );
292
			$this->delete_data( 'give_temp_payment_items' );
293
			$this->delete_data( 'give_temp_processed_payments' );
294
			$this->delete_data( 'give_temp_all_payments_data' );
295
		}
296
297
		$accepted_statuses = apply_filters( 'give_recount_accepted_statuses', array( 'publish' ) );
298
		$total             = $this->get_stored_data( 'give_temp_recount_all_total' );
299
300
		if ( false === $total ) {
301
			$total         = 0;
302
			$payment_items = $this->get_stored_data( 'give_temp_payment_items' );
303
304
			if ( false === $payment_items ) {
305
				$payment_items = array();
306
				$this->store_data( 'give_temp_payment_items', $payment_items );
307
			}
308
309
			$args = array(
310
				'post_status'    => 'publish',
311
				'post_type'      => 'give_forms',
312
				'posts_per_page' => - 1,
313
				'fields'         => 'ids',
314
			);
315
316
			$all_forms = get_posts( $args );
317
			$this->store_data( 'give_temp_form_ids', $all_forms );
318
319
			$args = apply_filters( 'give_recount_form_stats_total_args', array(
320
				'post_parent__in'   => $all_forms,
321
				'number'            => $this->per_step,
322
				'status'            => 'publish',
323
				'page'              => $this->step,
324
				'output'            => 'payments',
325
			) );
326
327
			$payments_query = new Give_Payments_Query( $args );
328
			$payments       = $payments_query->get_payments();
329
			$total          = wp_count_posts( 'give_payment' )->publish;
330
331
			$this->store_data( 'give_temp_all_payments_data', $payments );
332
333
			if ( $payments ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $payments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
334
335
				foreach ( $payments as $payment ) {
336
337
					$payment = new Give_Payment( $payment->ID );
338
					$form_id = $payment->form_id;
339
340
					//If for some reason somehow the form_ID isn't set check payment meta
341
					if ( empty( $payment->form_id ) ) {
342
						$payment_meta = $payment->get_meta();
343
						$form_id = isset( $payment_meta['form_id'] ) ? $payment_meta['form_id'] : 0;
344
					}
345
346
					if ( ! in_array( $payment->post_status, $accepted_statuses ) ) {
347
						continue;
348
					}
349
350
					if ( ! array_key_exists( $payment->ID, $payment_items ) ) {
351
						$payment_items[ $payment->ID ] = array(
352
							'id'         => $form_id,
353
							'payment_id' => $payment->ID,
354
							'price'      => $payment->total
355
						);
356
					}
357
358
				}
359
360
			}
361
362
			$this->store_data( 'give_temp_payment_items', $payment_items );
363
			$this->store_data( 'give_recount_all_total', $total );
364
		}
365
366
	}
367
368
	/**
369
	 * Given a key, get the information from the Database Directly
370
	 *
371
	 * @since  1.5
372
	 *
373
	 * @param  string $key The option_name
374
	 *
375
	 * @return mixed       Returns the data from the database
376
	 */
377
	private function get_stored_data( $key ) {
378
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
379
		$value = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = '%s'", $key ) );
380
381
		return empty( $value ) ? false : maybe_unserialize( $value );
382
	}
383
384
	/**
385
	 * Give a key, store the value
386
	 *
387
	 * @since  1.5
388
	 *
389
	 * @param  string $key The option_name
390
	 * @param  mixed $value The value to store
391
	 *
392
	 * @return void
393
	 */
394
	private function store_data( $key, $value ) {
395
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
396
397
		$value = maybe_serialize( $value );
398
399
		$data = array(
400
			'option_name'  => $key,
401
			'option_value' => $value,
402
			'autoload'     => 'no',
403
		);
404
405
		$formats = array(
406
			'%s',
407
			'%s',
408
			'%s',
409
		);
410
411
		$wpdb->replace( $wpdb->options, $data, $formats );
412
	}
413
414
	/**
415
	 * Delete an option
416
	 *
417
	 * @since  1.5
418
	 *
419
	 * @param  string $key The option_name to delete
420
	 *
421
	 * @return void
422
	 */
423
	private function delete_data( $key ) {
424
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
425
		$wpdb->delete( $wpdb->options, array( 'option_name' => $key ) );
426
	}
427
428
}
429