Test Failed
Pull Request — master (#2054)
by Devin
05:04
created

Give_Tools_Reset_Stats   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 342
Duplicated Lines 16.96 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 58
loc 342
rs 8.6
c 0
b 0
f 0
wmc 37
lcom 1
cbo 4

10 Methods

Rating   Name   Duplication   Size   Complexity  
D get_data() 0 85 14
A get_percentage_complete() 17 17 3
A set_properties() 0 2 1
B process_step() 0 31 4
A headers() 7 7 3
A export() 0 7 1
B pre_fetch() 0 47 5
A get_stored_data() 15 15 3
A store_data() 19 19 2
A delete_data() 0 6 1

How to fix   Duplicated Code   

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:

1
<?php
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     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_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
	 *
28
	 * @var string
29
	 * @since 1.5
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 boolean
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 integer
46
	 */
47
	public $per_step = 30;
48
49
	/**
50
	 * Get the Export Data
51
	 *
52
	 * @access public
53
	 * @since  1.5
54
	 * @global object $wpdb Used to query the database using the WordPress
55
	 *                      Database API
56
	 * @return bool   $data The data for the CSV file
57
	 */
58
	public function get_data() {
59
		global $wpdb;
60
61
		$items = $this->get_stored_data( 'give_temp_reset_ids' );
62
63
		if ( ! is_array( $items ) ) {
64
			return false;
65
		}
66
67
		$offset     = ( $this->step - 1 ) * $this->per_step;
68
		$step_items = array_slice( $items, $offset, $this->per_step );
69
70
		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...
71
72
			$step_ids = array(
73
				'customers' => array(),
74
				'forms'     => array(),
75
				'other'     => array(),
76
			);
77
78
			foreach ( $step_items as $item ) {
79
80
				switch ( $item['type'] ) {
81
					case 'customer':
82
						$step_ids['customers'][] = $item['id'];
83
						break;
84
					case 'forms':
85
						$step_ids['give_forms'][] = $item['id'];
86
						break;
87
					default:
88
						$item_type                = apply_filters( 'give_reset_item_type', 'other', $item );
89
						$step_ids[ $item_type ][] = $item['id'];
90
						break;
91
				}
92
			}
93
94
			$sql = array();
95
96
			foreach ( $step_ids as $type => $ids ) {
97
98
				if ( empty( $ids ) ) {
99
					continue;
100
				}
101
102
				$ids = implode( ',', $ids );
103
104
				switch ( $type ) {
105
					case 'customers':
106
						$table_name = $wpdb->prefix . 'give_customers';
107
						$meta_table_name = $wpdb->prefix . 'give_customermeta';
108
						$sql[]      = "DELETE FROM $table_name WHERE id IN ($ids)";
109
						$sql[]      = "DELETE FROM $meta_table_name WHERE customer_id IN ($ids)";
110
						break;
111
					case 'forms':
112
						$sql[] = "UPDATE $wpdb->postmeta SET meta_value = 0 WHERE meta_key = '_give_form_sales' AND post_id IN ($ids)";
113
						$sql[] = "UPDATE $wpdb->postmeta SET meta_value = 0.00 WHERE meta_key = '_give_form_earnings' AND post_id IN ($ids)";
114
						break;
115
					case 'other':
116
						$sql[] = "DELETE FROM $wpdb->posts WHERE id IN ($ids)";
117
						$sql[] = "DELETE FROM $wpdb->postmeta WHERE post_id IN ($ids)";
118
						$sql[] = "DELETE FROM $wpdb->comments WHERE comment_post_ID IN ($ids)";
119
						$sql[] = "DELETE FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments)";
120
						break;
121
				}
122
123
				if ( ! in_array( $type, array( 'customers', 'forms', 'other' ) ) ) {
124
					// Allows other types of custom post types to filter on their own post_type
125
					// and add items to the query list, for the IDs found in their post type.
126
					$sql = apply_filters( "give_reset_add_queries_{$type}", $sql, $ids );
127
				}
128
			}
129
130
			if ( ! empty( $sql ) ) {
131
				foreach ( $sql as $query ) {
132
					$wpdb->query( $query );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
133
				}
134
			}
135
136
			return true;
137
138
		}// End if().
139
140
		return false;
141
142
	}
143
144
	/**
145
	 * Return the calculated completion percentage.
146
	 *
147
	 * @since 1.5
148
	 * @return int
149
	 */
150 View Code Duplication
	public function get_percentage_complete() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
151
152
		$items = $this->get_stored_data( 'give_temp_reset_ids' );
153
		$total = count( $items );
154
155
		$percentage = 100;
156
157
		if ( $total > 0 ) {
158
			$percentage = ( ( $this->per_step * $this->step ) / $total ) * 100;
159
		}
160
161
		if ( $percentage > 100 ) {
162
			$percentage = 100;
163
		}
164
165
		return $percentage;
166
	}
167
168
	/**
169
	 * Set the properties specific to the payments export.
170
	 *
171
	 * @since 1.5
172
	 *
173
	 * @param array $request The Form Data passed into the batch processing.
174
	 */
175
	public function set_properties( $request ) {
176
	}
177
178
	/**
179
	 * Process a step
180
	 *
181
	 * @since 1.5
182
	 * @return bool
183
	 */
184
	public function process_step() {
185
186
		if ( ! $this->can_export() ) {
187
			wp_die( esc_html__( 'You do not have permission to reset data.', 'give' ), esc_html__( 'Error', 'give' ), array(
188
				'response' => 403,
189
			) );
190
		}
191
192
		$had_data = $this->get_data();
193
194
		if ( $had_data ) {
195
			$this->done = false;
196
197
			return true;
198
		} else {
199
			update_option( 'give_earnings_total', 0 );
200
			Give_Cache::delete( Give_Cache::get_key( 'give_estimated_monthly_stats' ) );
201
202
			$this->delete_data( 'give_temp_reset_ids' );
203
204
			// Reset the sequential order numbers
205
			if ( give_get_option( 'enable_sequential' ) ) {
206
				delete_option( 'give_last_payment_number' );
207
			}
208
209
			$this->done    = true;
210
			$this->message = esc_html__( 'Donation forms, income, donations counts, and logs successfully reset.', 'give' );
211
212
			return false;
213
		}
214
	}
215
216
	/**
217
	 * Headers
218
	 */
219 View Code Duplication
	public function headers() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
220
		ignore_user_abort( true );
221
222
		if ( ! give_is_func_disabled( 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) {
223
			set_time_limit( 0 );
224
		}
225
	}
226
227
	/**
228
	 * Perform the export
229
	 *
230
	 * @access public
231
	 * @since  1.5
232
	 * @return void
233
	 */
234
	public function export() {
235
236
		// Set headers
237
		$this->headers();
238
239
		give_die();
240
	}
241
242
	/**
243
	 * Pre Fetch
244
	 */
245
	public function pre_fetch() {
246
247
		if ( $this->step == 1 ) {
0 ignored issues
show
introduced by
Found "== 1". Use Yoda Condition checks, you must
Loading history...
248
			$this->delete_data( 'give_temp_reset_ids' );
249
		}
250
251
		$items = get_option( 'give_temp_reset_ids', false );
252
253
		if ( false === $items ) {
254
			$items = array();
255
256
			$give_types_for_reset = array( 'give_forms', 'give_payment' );
257
			$give_types_for_reset = apply_filters( 'give_reset_store_post_types', $give_types_for_reset );
258
259
			$args = apply_filters( 'give_tools_reset_stats_total_args', array(
260
				'post_type'      => $give_types_for_reset,
261
				'post_status'    => 'any',
262
				'posts_per_page' => - 1,
263
			) );
264
265
			$posts = get_posts( $args );
266
			foreach ( $posts as $post ) {
267
				$items[] = array(
268
					'id'   => (int) $post->ID,
269
					'type' => $post->post_type,
270
				);
271
			}
272
273
			$donor_args = array(
274
				'number' => - 1,
275
			);
276
			$donors     = Give()->donors->get_donors( $donor_args );
277
			foreach ( $donors as $donor ) {
0 ignored issues
show
Bug introduced by
The expression $donors of type array|object|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
278
				$items[] = array(
279
					'id'   => (int) $donor->id,
280
					'type' => 'customer',
281
				);
282
			}
283
284
			// Allow filtering of items to remove with an unassociative array for each item
285
			// The array contains the unique ID of the item, and a 'type' for you to use in the execution of the get_data method
286
			$items = apply_filters( 'give_reset_items', $items );
287
288
			$this->store_data( 'give_temp_reset_ids', $items );
289
		}// End if().
290
291
	}
292
293
	/**
294
	 * Given a key, get the information from the Database Directly.
295
	 *
296
	 * @since  1.5
297
	 *
298
	 * @param  string $key The option_name
299
	 *
300
	 * @return mixed       Returns the data from the database.
301
	 */
302 View Code Duplication
	private function get_stored_data( $key ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
303
		global $wpdb;
304
		$value = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = '%s'", $key ) );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
305
306
		if ( empty( $value ) ) {
307
			return false;
308
		}
309
310
		$maybe_json = json_decode( $value );
311
		if ( ! is_null( $maybe_json ) ) {
312
			$value = json_decode( $value, true );
313
		}
314
315
		return $value;
316
	}
317
318
	/**
319
	 * Give a key, store the value.
320
	 *
321
	 * @since  1.5
322
	 *
323
	 * @param  string $key   The option_name.
324
	 * @param  mixed  $value The value to store.
325
	 *
326
	 * @return void
327
	 */
328 View Code Duplication
	private function store_data( $key, $value ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
329
		global $wpdb;
330
331
		$value = is_array( $value ) ? wp_json_encode( $value ) : esc_attr( $value );
332
333
		$data = array(
334
			'option_name'  => $key,
335
			'option_value' => $value,
336
			'autoload'     => 'no',
337
		);
338
339
		$formats = array(
340
			'%s',
341
			'%s',
342
			'%s',
343
		);
344
345
		$wpdb->replace( $wpdb->options, $data, $formats );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
346
	}
347
348
	/**
349
	 * Delete an option
350
	 *
351
	 * @since  1.5
352
	 *
353
	 * @param  string $key The option_name to delete
354
	 *
355
	 * @return void
356
	 */
357
	private function delete_data( $key ) {
358
		global $wpdb;
359
		$wpdb->delete( $wpdb->options, array(
360
			'option_name' => $key,
361
		) );
362
	}
363
364
}