Completed
Pull Request — master (#664)
by Devin
19:01
created

Give_Tools_Recount_All_Stats::get_data()   D

Complexity

Conditions 13
Paths 104

Size

Total Lines 102
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 55
c 1
b 0
f 0
nc 104
nop 0
dl 0
loc 102
rs 4.9194

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     http://opensource.org/licenses/gpl-2.0.php 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
	 * @var string
28
	 * @since 1.5
29
	 */
30
	public $export_type = '';
31
32
	/**
33
	 * Allows for a non-form batch processing to be run.
34
	 * @since  1.5
35
	 * @var boolean
36
	 */
37
	public $is_void = true;
38
39
	/**
40
	 * Sets the number of items to pull on each step
41
	 * @since  1.5
42
	 * @var integer
43
	 */
44
	public $per_step = 30;
45
46
	/**
47
	 * Get the Export Data
48
	 *
49
	 * @access public
50
	 * @since 1.5
51
	 * @global object $wpdb Used to query the database using the WordPress
52
	 *   Database API
53
	 * @return array $data The data for the CSV file
54
	 */
55
	public function get_data() {
56
		global $give_logs, $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...
57
58
		$totals             = $this->get_stored_data( 'give_temp_recount_all_stats' );
59
		$payment_items      = $this->get_stored_data( 'give_temp_payment_items' );
60
		$processed_payments = $this->get_stored_data( 'give_temp_processed_payments' );
61
		$accepted_statuses  = apply_filters( 'give_recount_accepted_statuses', array( 'publish', 'revoked' ) );
62
63
		if ( false === $totals ) {
64
			$totals = array();
65
		}
66
67
		if ( false === $payment_items ) {
68
			$payment_items = array();
69
		}
70
71
		if ( false === $processed_payments ) {
72
			$processed_payments = array();
73
		}
74
75
		$all_forms = $this->get_stored_data( 'give_temp_form_ids' );
76
77
		$args = apply_filters( 'give_recount_form_stats_args', array(
78
			'post_parent__in' => $all_forms,
79
			'post_type'       => 'give_log',
80
			'posts_per_page'  => $this->per_step,
81
			'post_status'     => 'publish',
82
			'paged'           => $this->step,
83
			'log_type'        => 'sale',
84
			'fields'          => 'ids',
85
		) );
86
87
		$log_ids = $give_logs->get_connected_logs( $args, 'sale' );
88
89
		if ( $log_ids ) {
90
			$log_ids = implode( ',', $log_ids );
91
92
			$payment_ids = $wpdb->get_col( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key='_give_log_payment_id' AND post_id IN ($log_ids)" );
93
			unset( $log_ids );
94
95
			$payment_ids = implode( ',', $payment_ids );
96
			$payments    = $wpdb->get_results( "SELECT ID, post_status FROM $wpdb->posts WHERE ID IN (" . $payment_ids . ")" );
97
			unset( $payment_ids );
98
99
			//Loop through payments
100
			foreach ( $payments as $payment ) {
101
102
				// Prevent payments that have all ready been retrieved from a previous sales log from counting again.
103
				if ( in_array( $payment->ID, $processed_payments ) ) {
104
					continue;
105
				}
106
107
				//Verify accepted status'
108
				if ( ! in_array( $payment->post_status, $accepted_statuses ) ) {
109
					$processed_payments[] = $payment->ID;
110
					continue;
111
				}
112
113
				$payment_item = $payment_items[ $payment->ID ];
114
115
116
				$form_id = isset( $payment_item['id'] ) ? $payment_item['id'] : '';
117
118
				//Must have a form ID
119
				if ( empty( $form_id ) ) {
120
					continue;
121
				}
122
123
				//Form ID must be within $all_forms array to be validated
124
				if ( ! in_array( $form_id, $all_forms ) ) {
125
					continue;
126
				}
127
128
				//If array key doesn't exist, create it
129
				if ( ! array_key_exists( $form_id, $totals ) ) {
130
					$totals[ $form_id ] = array(
131
						'sales'    => (int) 0,
132
						'earnings' => (float) 0,
133
					);
134
				}
135
136
				$totals[ $form_id ]['sales'] ++;
137
				$totals[ $form_id ]['earnings'] += $payment_item['price'];
138
139
				$processed_payments[] = $payment->ID;
140
141
			}
142
143
			$this->store_data( 'give_temp_processed_payments', $processed_payments );
144
			$this->store_data( 'give_temp_recount_all_stats', $totals );
145
146
			return true;
147
		}
148
149
		foreach ( $totals as $key => $stats ) {
150
			update_post_meta( $key, '_give_form_sales', $stats['sales'] );
151
			update_post_meta( $key, '_give_form_earnings', $stats['earnings'] );
152
		}
153
154
		return false;
155
156
	}
157
158
	/**
159
	 * Return the calculated completion percentage
160
	 *
161
	 * @since 1.5
162
	 * @return int
163
	 */
164
	public function get_percentage_complete() {
165
166
		$total = $this->get_stored_data( 'give_recount_all_total', false );
0 ignored issues
show
Unused Code introduced by
The call to Give_Tools_Recount_All_Stats::get_stored_data() has too many arguments starting with false.

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...
167
168
		if ( false === $total ) {
169
			$this->pre_fetch();
170
			$total = $this->get_stored_data( 'give_recount_all_total', 0 );
0 ignored issues
show
Unused Code introduced by
The call to Give_Tools_Recount_All_Stats::get_stored_data() has too many arguments starting with 0.

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...
171
		}
172
173
		$percentage = 100;
174
175
		if ( $total > 0 ) {
176
			$percentage = ( ( $this->per_step * $this->step ) / $total ) * 100;
177
		}
178
179
		if ( $percentage > 100 ) {
180
			$percentage = 100;
181
		}
182
183
		return $percentage;
184
	}
185
186
	/**
187
	 * Set the properties specific to the payments export
188
	 *
189
	 * @since 1.5
190
	 *
191
	 * @param array $request The Form Data passed into the batch processing
192
	 */
193
	public function set_properties( $request ) {
194
		$this->form_id = isset( $request['form_id'] ) ? sanitize_text_field( $request['form_id'] ) : false;
0 ignored issues
show
Bug introduced by
The property form_id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
195
	}
196
197
	/**
198
	 * Process a step
199
	 *
200
	 * @since 1.5
201
	 * @return bool
202
	 */
203
	public function process_step() {
204
205
		if ( ! $this->can_export() ) {
206
			wp_die( __( 'You do not have permission to recount stats.', 'give' ), __( 'Error', 'give' ), array( 'response' => 403 ) );
207
		}
208
209
		$had_data = $this->get_data();
210
211
		if ( $had_data ) {
212
			$this->done = false;
0 ignored issues
show
Bug introduced by
The property done does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
213
214
			return true;
215
		} else {
216
			$this->delete_data( 'give_recount_all_total' );
217
			$this->delete_data( 'give_temp_recount_all_stats' );
218
			$this->delete_data( 'give_temp_payment_items' );
219
			$this->delete_data( 'give_temp_form_ids' );
220
			$this->delete_data( 'give_temp_processed_payments' );
221
			$this->done    = true;
222
			$this->message = __( 'Donation form income amounts and donation counts stats successfully recounted.', 'give' );
0 ignored issues
show
Bug introduced by
The property message does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
223
224
			return false;
225
		}
226
	}
227
228
	public function headers() {
229
		ignore_user_abort( true );
230
231
		if ( ! give_is_func_disabled( 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) {
232
			set_time_limit( 0 );
233
		}
234
	}
235
236
	/**
237
	 * Perform the export
238
	 *
239
	 * @access public
240
	 * @since 1.5
241
	 * @return void
242
	 */
243
	public function export() {
244
245
		// Set headers
246
		$this->headers();
247
248
		give_die();
249
	}
250
251
	/**
252
	 * Pre Fetch Data
253
	 *
254
	 * @access public
255
	 * @since 1.5
256
	 */
257
	public function pre_fetch() {
258
259
		global $give_logs, $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...
260
261
		if ( $this->step == 1 ) {
262
			$this->delete_data( 'give_temp_recount_all_total' );
263
			$this->delete_data( 'give_temp_recount_all_stats' );
264
			$this->delete_data( 'give_temp_payment_items' );
265
			$this->delete_data( 'give_temp_processed_payments' );
266
		}
267
268
		$accepted_statuses = apply_filters( 'give_recount_accepted_statuses', array( 'publish', 'revoked' ) );
269
		$total             = $this->get_stored_data( 'give_temp_recount_all_total' );
270
271
		if ( false === $total ) {
272
			$total         = 0;
273
			$payment_items = $this->get_stored_data( 'give_temp_payment_items' );
274
275
			if ( false === $payment_items ) {
276
				$payment_items = array();
277
				$this->store_data( 'give_temp_payment_items', $payment_items );
278
			}
279
280
			$all_forms = $this->get_stored_data( 'give_temp_form_ids' );
281
282
			if ( false === $all_forms ) {
283
				$args = array(
284
					'post_status'    => 'any',
285
					'post_type'      => 'give_forms',
286
					'posts_per_page' => - 1,
287
					'fields'         => 'ids',
288
				);
289
290
				$all_forms = get_posts( $args );
291
				$this->store_data( 'give_temp_form_ids', $all_forms );
292
			}
293
294
			$args = apply_filters( 'give_recount_form_stats_total_args', array(
295
				'post_parent__in' => $all_forms,
296
				'post_type'       => 'give_log',
297
				'post_status'     => 'publish',
298
				'log_type'        => 'sale',
299
				'fields'          => 'ids',
300
				'nopaging'        => true,
301
			) );
302
303
			$all_logs = $give_logs->get_connected_logs( $args, 'sale' );
304
305
			if ( $all_logs ) {
306
				$log_ids     = implode( ',', $all_logs );
307
				$payment_ids = $wpdb->get_col( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key='_give_log_payment_id' AND post_id IN ($log_ids)" );
308
				unset( $log_ids );
309
310
				$payment_ids = implode( ',', $payment_ids );
311
				$payments    = $wpdb->get_results( "SELECT ID, post_status FROM $wpdb->posts WHERE ID IN (" . $payment_ids . ")" );
312
				unset( $payment_ids );
313
314
				foreach ( $payments as $payment ) {
315
316
					$payment = new Give_Payment( $payment->ID );
317
					$form_id = $payment->form_id;
0 ignored issues
show
Documentation introduced by
The property $form_id is declared protected in Give_Payment. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
318
319
					//If for some reason somehow the form_ID isn't set check payment meta
320
					if ( empty( $payment->form_id ) ) {
0 ignored issues
show
Documentation introduced by
The property $form_id is declared protected in Give_Payment. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
321
						$payment_meta = $payment->get_meta();
322
						$form_id = isset( $payment_meta['form_id'] ) ? $payment_meta['form_id'] : 0;
323
					}
324
325
					if ( ! in_array( $payment->post_status, $accepted_statuses ) ) {
0 ignored issues
show
Documentation introduced by
The property $post_status is declared protected in Give_Payment. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
326
						continue;
327
					}
328
329
					if ( ! array_key_exists( $payment->ID, $payment_items ) ) {
330
						$payment_items[ $payment->ID ] = array(
331
							'id'         => $form_id,
332
							'payment_id' => $payment->ID,
333
							'price'      => $payment->total
0 ignored issues
show
Documentation introduced by
The property $total is declared protected in Give_Payment. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
334
						);
335
					}
336
337
				}
338
339
				$total = count( $all_logs );
340
			}
341
342
			$this->store_data( 'give_temp_payment_items', $payment_items );
343
			$this->store_data( 'give_recount_all_total', $total );
344
		}
345
346
	}
347
348
	/**
349
	 * Given a key, get the information from the Database Directly
350
	 *
351
	 * @since  1.5
352
	 *
353
	 * @param  string $key The option_name
354
	 *
355
	 * @return mixed       Returns the data from the database
356
	 */
357
	private function get_stored_data( $key ) {
358
		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...
359
		$value = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = '%s'", $key ) );
360
361
		return empty( $value ) ? false : maybe_unserialize( $value );
362
	}
363
364
	/**
365
	 * Give a key, store the value
366
	 *
367
	 * @since  1.5
368
	 *
369
	 * @param  string $key The option_name
370
	 * @param  mixed $value The value to store
371
	 *
372
	 * @return void
373
	 */
374
	private function store_data( $key, $value ) {
375
		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...
376
377
		$value = maybe_serialize( $value );
378
379
		$data = array(
380
			'option_name'  => $key,
381
			'option_value' => $value,
382
			'autoload'     => 'no',
383
		);
384
385
		$formats = array(
386
			'%s',
387
			'%s',
388
			'%s',
389
		);
390
391
		$wpdb->replace( $wpdb->options, $data, $formats );
392
	}
393
394
	/**
395
	 * Delete an option
396
	 *
397
	 * @since  1.5
398
	 *
399
	 * @param  string $key The option_name to delete
400
	 *
401
	 * @return void
402
	 */
403
	private function delete_data( $key ) {
404
		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...
405
		$wpdb->delete( $wpdb->options, array( 'option_name' => $key ) );
406
	}
407
408
}
409