Completed
Pull Request — master (#639)
by
unknown
19:14
created

Give_Tools_Reset_Stats   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 325
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 325
rs 9
wmc 35
lcom 1
cbo 2

10 Methods

Rating   Name   Duplication   Size   Complexity  
D get_data() 0 85 14
A get_percentage_complete() 0 17 3
A set_properties() 0 2 1
B process_step() 0 30 4
A headers() 0 7 3
A export() 0 7 1
B pre_fetch() 0 45 5
A get_stored_data() 0 6 2
A store_data() 0 19 1
A delete_data() 0 4 1
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 income and stats
4
 *
5
 * This class handles batch processing of resetting donations and income stats
6
 *
7
 * @subpackage  Admin/Tools/Give_Tools_Reset_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_Reset_Stats Class
20
 *
21
 * @since 1.5
22
 */
23
class Give_Tools_Reset_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 $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
		$items = $this->get_stored_data( 'give_temp_reset_ids' );
59
60
		if ( ! is_array( $items ) ) {
61
			return false;
62
		}
63
64
		$offset     = ( $this->step - 1 ) * $this->per_step;
65
		$step_items = array_slice( $items, $offset, $this->per_step );
66
67
		if ( $step_items ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $step_items 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...
68
69
			$step_ids = array(
70
				'customers'  => array(),
71
				'forms' => array(),
72
				'other'      => array(),
73
			);
74
75
			foreach ( $step_items as $item ) {
76
77
				switch ( $item['type'] ) {
78
					case 'customer':
79
						$step_ids['customers'][] = $item['id'];
80
						break;
81
					case 'forms':
82
						$step_ids['give_forms'][] = $item['id'];
83
						break;
84
					default:
85
						$item_type                = apply_filters( 'give_reset_item_type', 'other', $item );
86
						$step_ids[ $item_type ][] = $item['id'];
87
						break;
88
				}
89
90
			}
91
92
			$sql = array();
93
94
			foreach ( $step_ids as $type => $ids ) {
95
96
				if ( empty( $ids ) ) {
97
					continue;
98
				}
99
100
				$ids = implode( ',', $ids );
101
102
				switch ( $type ) {
103
					case 'customers':
104
						$table_name = $wpdb->prefix . 'give_customers';
105
						$sql[]      = "DELETE FROM $table_name WHERE id IN ($ids)";
106
						break;
107
					case 'forms':
108
						$sql[] = "UPDATE $wpdb->postmeta SET meta_value = 0 WHERE meta_key = '_give_form_sales' AND post_id IN ($ids)";
109
						$sql[] = "UPDATE $wpdb->postmeta SET meta_value = 0.00 WHERE meta_key = '_give_form_earnings' AND post_id IN ($ids)";
110
						break;
111
					case 'other':
112
						$sql[] = "DELETE FROM $wpdb->posts WHERE id IN ($ids)";
113
						$sql[] = "DELETE FROM $wpdb->postmeta WHERE post_id IN ($ids)";
114
						$sql[] = "DELETE FROM $wpdb->comments WHERE comment_post_ID IN ($ids)";
115
						$sql[] = "DELETE FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments)";
116
						break;
117
				}
118
119
				if ( ! in_array( $type, array( 'customers', 'forms', 'other' ) ) ) {
120
					// Allows other types of custom post types to filter on their own post_type
121
					// and add items to the query list, for the IDs found in their post type.
122
					$sql = apply_filters( 'give_reset_add_queries_' . $type, $sql, $ids );
123
				}
124
125
			}
126
127
			if ( ! empty( $sql ) ) {
128
				foreach ( $sql as $query ) {
129
					$wpdb->query( $query );
130
				}
131
			}
132
133
			return true;
134
135
		}
136
137
		return false;
138
139
	}
140
141
	/**
142
	 * Return the calculated completion percentage
143
	 *
144
	 * @since 1.5
145
	 * @return int
146
	 */
147
	public function get_percentage_complete() {
148
149
		$items = $this->get_stored_data( 'give_temp_reset_ids', false );
0 ignored issues
show
Unused Code introduced by
The call to Give_Tools_Reset_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...
150
		$total = count( $items );
151
152
		$percentage = 100;
153
154
		if ( $total > 0 ) {
155
			$percentage = ( ( $this->per_step * $this->step ) / $total ) * 100;
156
		}
157
158
		if ( $percentage > 100 ) {
159
			$percentage = 100;
160
		}
161
162
		return $percentage;
163
	}
164
165
	/**
166
	 * Set the properties specific to the payments export
167
	 *
168
	 * @since 1.5
169
	 *
170
	 * @param array $request The Form Data passed into the batch processing
171
	 */
172
	public function set_properties( $request ) {
173
	}
174
175
	/**
176
	 * Process a step
177
	 *
178
	 * @since 1.5
179
	 * @return bool
180
	 */
181
	public function process_step() {
182
183
		if ( ! $this->can_export() ) {
184
			wp_die( __( 'You do not have permission to export data.', 'give' ), __( 'Error', 'give' ), array( 'response' => 403 ) );
185
		}
186
187
		$had_data = $this->get_data();
188
189
		if ( $had_data ) {
190
			$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...
191
192
			return true;
193
		} else {
194
			update_option( 'give_earnings_total', 0 );
195
			delete_transient( 'give_earnings_total' );
196
			delete_transient( 'give_estimated_monthly_stats' . true );
197
			delete_transient( 'give_estimated_monthly_stats' . false );
198
			$this->delete_data( 'give_temp_reset_ids' );
199
200
			// Reset the sequential order numbers
201
			if ( give_get_option( 'enable_sequential' ) ) {
202
				delete_option( 'give_last_payment_number' );
203
			}
204
205
			$this->done    = true;
206
			$this->message = __( 'Donation forms, income, donations counts, and logs successfully reset.', '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...
207
208
			return false;
209
		}
210
	}
211
212
	/**
213
	 * Headers
214
	 */
215
	public function headers() {
216
		ignore_user_abort( true );
217
218
		if ( ! give_is_func_disabled( 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) {
219
			set_time_limit( 0 );
220
		}
221
	}
222
223
	/**
224
	 * Perform the export
225
	 *
226
	 * @access public
227
	 * @since 1.5
228
	 * @return void
229
	 */
230
	public function export() {
231
232
		// Set headers
233
		$this->headers();
234
235
		give_die();
236
	}
237
238
	/**
239
	 * Pre Fetch
240
	 */
241
	public function pre_fetch() {
242
243
		if ( $this->step == 1 ) {
244
			$this->delete_data( 'give_temp_reset_ids' );
245
		}
246
247
		$items = get_option( 'give_temp_reset_ids', false );
248
249
		if ( false === $items ) {
250
			$items = array();
251
252
			$give_types_for_reset = array( 'give_forms', 'give_log', 'give_payment' );
253
			$give_types_for_reset = apply_filters( 'give_reset_store_post_types', $give_types_for_reset );
254
255
			$args = apply_filters( 'give_tools_reset_stats_total_args', array(
256
				'post_type'      => $give_types_for_reset,
257
				'post_status'    => 'any',
258
				'posts_per_page' => - 1,
259
			) );
260
261
			$posts = get_posts( $args );
262
			foreach ( $posts as $post ) {
263
				$items[] = array(
264
					'id'   => (int) $post->ID,
265
					'type' => $post->post_type,
266
				);
267
			}
268
269
			$customer_args = array( 'number' => - 1 );
270
			$customers     = Give()->customers->get_customers( $customer_args );
271
			foreach ( $customers as $customer ) {
272
				$items[] = array(
273
					'id'   => (int) $customer->id,
274
					'type' => 'customer',
275
				);
276
			}
277
278
			// Allow filtering of items to remove with an unassociative array for each item
279
			// The array contains the unique ID of the item, and a 'type' for you to use in the execution of the get_data method
280
			$items = apply_filters( 'give_reset_store_items', $items );
281
282
			$this->store_data( 'give_temp_reset_ids', $items );
283
		}
284
285
	}
286
287
	/**
288
	 * Given a key, get the information from the Database Directly
289
	 *
290
	 * @since  1.5
291
	 *
292
	 * @param  string $key The option_name
293
	 *
294
	 * @return mixed       Returns the data from the database
295
	 */
296
	private function get_stored_data( $key ) {
297
		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...
298
		$value = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = '%s'", $key ) );
299
300
		return empty( $value ) ? false : maybe_unserialize( $value );
301
	}
302
303
	/**
304
	 * Give a key, store the value
305
	 *
306
	 * @since  1.5
307
	 *
308
	 * @param  string $key The option_name
309
	 * @param  mixed $value The value to store
310
	 *
311
	 * @return void
312
	 */
313
	private function store_data( $key, $value ) {
314
		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...
315
316
		$value = maybe_serialize( $value );
317
318
		$data = array(
319
			'option_name'  => $key,
320
			'option_value' => $value,
321
			'autoload'     => 'no',
322
		);
323
324
		$formats = array(
325
			'%s',
326
			'%s',
327
			'%s',
328
		);
329
330
		$wpdb->replace( $wpdb->options, $data, $formats );
331
	}
332
333
	/**
334
	 * Delete an option
335
	 *
336
	 * @since  1.5
337
	 *
338
	 * @param  string $key The option_name to delete
339
	 *
340
	 * @return void
341
	 */
342
	private function delete_data( $key ) {
343
		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...
344
		$wpdb->delete( $wpdb->options, array( 'option_name' => $key ) );
345
	}
346
347
}
348