Give_Export_Donations_CSV   F
last analyzed

Complexity

Total Complexity 110

Size/Duplication

Total Lines 575
Duplicated Lines 10.26 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 59
loc 575
rs 2
c 0
b 0
f 0
wmc 110
lcom 1
cbo 4

8 Methods

Rating   Name   Duplication   Size   Complexity  
D set_properties() 0 23 10
B get_form_ids() 0 11 8
A csv_cols() 0 13 3
D get_cols() 8 99 26
F get_data() 14 217 46
A get_percentage_complete() 0 17 4
B print_csv_rows() 29 29 6
B get_donation_argument() 8 30 7

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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

1
<?php
2
/**
3
 * Payments Export Class.
4
 *
5
 * This class handles payment export in batches.
6
 *
7
 * @package     Give
8
 * @subpackage  Admin/Reports
9
 * @copyright   Copyright (c) 2016, GiveWP
10
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
11
 * @since       2.1
12
 */
13
14
// Exit if accessed directly.
15
if ( ! defined( 'ABSPATH' ) ) {
16
	exit;
17
}
18
19
/**
20
 * Give_Export_Donations_CSV Class
21
 *
22
 * @since 2.1
23
 */
24
class Give_Export_Donations_CSV extends Give_Batch_Export {
25
26
	/**
27
	 * Our export type. Used for export-type specific filters/actions.
28
	 *
29
	 * @since 2.1
30
	 *
31
	 * @var string
32
	 */
33
	public $export_type = 'payments';
34
35
	/**
36
	 * Form submission data.
37
	 *
38
	 * @since 2.1
39
	 *
40
	 * @var array
41
	 */
42
	public $data = array();
43
44
	/**
45
	 * Form submission data.
46
	 *
47
	 * @since 2.1
48
	 *
49
	 * @var array
50
	 */
51
	public $cols = array();
52
53
	/**
54
	 * Form ID.
55
	 *
56
	 * @since 2.1
57
	 *
58
	 * @var string
59
	 */
60
	public $form_id = '';
61
62
	/**
63
	 * Form tags ids.
64
	 *
65
	 * @since 2.1
66
	 *
67
	 * @var array
68
	 */
69
	public $tags = '';
70
71
72
	/**
73
	 * Form categories ids.
74
	 *
75
	 * @since 2.1
76
	 *
77
	 * @var array
78
	 */
79
	public $categories = '';
80
81
	/**
82
	 * Set the properties specific to the export.
83
	 *
84
	 * @since 2.1
85
	 *
86
	 * @param array $request The Form Data passed into the batch processing.
87
	 */
88
	public function set_properties( $request ) {
89
90
		// Set data from form submission
91
		if ( isset( $_POST['form'] ) ) {
92
			$this->data = give_clean( wp_parse_args( $_POST['form'] ) );
0 ignored issues
show
Documentation Bug introduced by
It seems like give_clean(wp_parse_args($_POST['form'])) can also be of type string. However, the property $data is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
93
		}
94
95
		$this->form       = $this->data['forms'];
96
		$this->categories = ! empty( $request['give_forms_categories'] ) ? (array) $request['give_forms_categories'] : array();
97
		$this->tags       = ! empty( $request['give_forms_tags'] ) ? (array) $request['give_forms_tags'] : array();
98
		$this->form_id    = $this->get_form_ids( $request );
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_form_ids($request) can also be of type array or boolean. However, the property $form_id is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
99
		$this->price_id   = isset( $request['give_price_option'] ) && ( 'all' !== $request['give_price_option'] && '' !== $request['give_price_option'] ) ? absint( $request['give_price_option'] ) : null;
100
		$this->start      = isset( $request['start'] ) ? sanitize_text_field( $request['start'] ) : '';
101
		$this->end        = isset( $request['end'] ) ? sanitize_text_field( $request['end'] ) : '';
102
		$this->status     = isset( $request['status'] ) ? sanitize_text_field( $request['status'] ) : 'complete';
103
104
		/**
105
		 * Hook to use after setting properties.
106
		 *
107
		 * @since 2.1.3
108
		 */
109
		do_action( 'give_export_donations_form_data', $this->data );
110
	}
111
112
	/**
113
	 * Get donation form id list
114
	 *
115
	 * @since 2.1
116
	 *
117
	 * @param array $request form data that need to be exported
118
	 *
119
	 * @return array|boolean|null $form get all the donation id that need to be exported
120
	 */
121
	public function get_form_ids( $request = array() ) {
122
		$form = ! empty( $request['forms'] ) && 0 !== $request['forms'] ? absint( $request['forms'] ) : null;
123
124
		$form_ids = ! empty( $request['form_ids'] ) ? sanitize_text_field( $request['form_ids'] ) : null;
125
126
		if ( empty( $form ) && ! empty( $form_ids ) && ( ! empty( $this->categories ) || ! empty( $this->tags ) ) ) {
127
			$form = explode( ',', $form_ids );
128
		}
129
130
		return $form;
131
	}
132
133
	/**
134
	 * Set the CSV columns.
135
	 *
136
	 * @access public
137
	 *
138
	 * @since  2.1
139
	 *
140
	 * @return array|bool $cols All the columns.
141
	 */
142
	public function csv_cols() {
143
144
		$columns = isset( $this->data['give_give_donations_export_option'] ) ? $this->data['give_give_donations_export_option'] : array();
145
146
		// We need columns.
147
		if ( empty( $columns ) ) {
148
			return false;
149
		}
150
151
		$this->cols = $this->get_cols( $columns );
152
153
		return $this->cols;
154
	}
155
156
157
	/**
158
	 * CSV file columns.
159
	 *
160
	 * @since  2.1
161
	 *
162
	 * @param array $columns
163
	 *
164
	 * @return array
165
	 */
166
	private function get_cols( $columns ) {
167
168
		$cols = array();
169
170
		foreach ( $columns as $key => $value ) {
171
172
			switch ( $key ) {
173
				case 'donation_id':
174
					$cols['donation_id'] = __( 'Donation ID', 'give' );
175
					break;
176
				case 'seq_id':
177
					$cols['seq_id'] = __( 'Donation Number', 'give' );
178
					break;
179
				case 'title_prefix':
180
					$cols['title_prefix'] = __( 'Title Prefix', 'give' );
181
					break;
182
				case 'first_name':
183
					$cols['first_name'] = __( 'First Name', 'give' );
184
					break;
185
				case 'last_name':
186
					$cols['last_name'] = __( 'Last Name', 'give' );
187
					break;
188
				case 'email':
189
					$cols['email'] = __( 'Email Address', 'give' );
190
					break;
191
				case 'company':
192
					$cols['company'] = __( 'Company Name', 'give' );
193
					break;
194 View Code Duplication
				case 'address':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
195
					$cols['address_line1']   = __( 'Address 1', 'give' );
196
					$cols['address_line2']   = __( 'Address 2', 'give' );
197
					$cols['address_city']    = __( 'City', 'give' );
198
					$cols['address_state']   = __( 'State', 'give' );
199
					$cols['address_zip']     = __( 'Zip', 'give' );
200
					$cols['address_country'] = __( 'Country', 'give' );
201
					break;
202
				case 'comment':
203
					$cols['comment'] = __( 'Donor Comment', 'give' );
204
					break;
205
				case 'donation_total':
206
					$cols['donation_total'] = __( 'Donation Total', 'give' );
207
					break;
208
				case 'currency_code':
209
					$cols['currency_code'] = __( 'Currency Code', 'give' );
210
					break;
211
				case 'currency_symbol':
212
					$cols['currency_symbol'] = __( 'Currency Symbol', 'give' );
213
					break;
214
				case 'donation_status':
215
					$cols['donation_status'] = __( 'Donation Status', 'give' );
216
					break;
217
				case 'payment_gateway':
218
					$cols['payment_gateway'] = __( 'Payment Gateway', 'give' );
219
				case 'payment_mode':
220
					$cols['payment_mode'] = __( 'Payment Mode', 'give' );
221
					break;
222
				case 'form_id':
223
					$cols['form_id'] = __( 'Form ID', 'give' );
224
					break;
225
				case 'form_title':
226
					$cols['form_title'] = __( 'Form Title', 'give' );
227
					break;
228
				case 'form_level_id':
229
					$cols['form_level_id'] = __( 'Level ID', 'give' );
230
					break;
231
				case 'form_level_title':
232
					$cols['form_level_title'] = __( 'Level Title', 'give' );
233
					break;
234
				case 'donation_date':
235
					$cols['donation_date'] = __( 'Donation Date', 'give' );
236
					break;
237
				case 'donation_time':
238
					$cols['donation_time'] = __( 'Donation Time', 'give' );
239
					break;
240
				case 'userid':
241
					$cols['userid'] = __( 'User ID', 'give' );
242
					break;
243
				case 'donorid':
244
					$cols['donorid'] = __( 'Donor ID', 'give' );
245
					break;
246
				case 'donor_ip':
247
					$cols['donor_ip'] = __( 'Donor IP Address', 'give' );
248
					break;
249
				default:
250
					$cols[ $key ] = $key;
251
252
			}
253
		}
254
255
		/**
256
		 * Filter to get columns name when exporting donation
257
		 *
258
		 * @since 2.1
259
		 *
260
		 * @param array $cols    columns name for CSV
261
		 * @param array $columns columns select by admin to export
262
		 */
263
		return (array) apply_filters( 'give_export_donation_get_columns_name', $cols, $columns );
264
	}
265
266
	/**
267
	 * Get the donation argument
268
	 *
269
	 * @since 2.1
270
	 *
271
	 * @param array $args donation argument
272
	 *
273
	 * @return array $args donation argument
274
	 */
275
	public function get_donation_argument( $args = array() ) {
276
		$defaults = array(
277
			'number' => 30,
278
			'page'   => $this->step,
279
			'status' => $this->status,
280
		);
281
		// Date query.
282
		if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
283 View Code Duplication
			if ( ! empty( $this->start ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
284
				$start_date                         = give_get_formatted_date( $this->start );
285
				$defaults['date_query'][0]['after'] = "{$start_date} 00:00:00";
286
			}
287 View Code Duplication
			if ( ! empty( $this->end ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
288
				$end_date                            = give_get_formatted_date( $this->end );
289
				$defaults['date_query'][0]['before'] = "{$end_date} 23:59:59";
290
			}
291
		}
292
293
		if ( ! empty( $this->form_id ) ) {
294
			$defaults['give_forms'] = is_array( $this->form_id ) ? $this->form_id : array( $this->form_id );
295
		}
296
297
		/**
298
		 * Filter to modify Payment Query arguments for exporting
299
		 * donations.
300
		 *
301
		 * @since 2.1.3
302
		 */
303
		return apply_filters( 'give_export_donations_donation_query_args', wp_parse_args( $args, $defaults ) );
304
	}
305
306
	/**
307
	 * Get the Export Data.
308
	 *
309
	 * @access public
310
	 *
311
	 * @since  2.1
312
	 *
313
	 * @global object $wpdb Used to query the database using the WordPress database API.
314
	 *
315
	 * @return array $data The data for the CSV file.
316
	 */
317
	public function get_data() {
318
319
		$data = array();
320
		$i    = 0;
321
		// Payment query.
322
		$payments = give_get_payments( $this->get_donation_argument() );
323
324
		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...
325
326
			foreach ( $payments as $payment ) {
327
328
				$columns      = $this->csv_cols();
329
				$payment      = new Give_Payment( $payment->ID );
330
				$payment_meta = $payment->payment_meta;
331
				$address      = $payment->address;
332
333
				// Set columns.
334
				if ( ! empty( $columns['donation_id'] ) ) {
335
					$data[ $i ]['donation_id'] = $payment->ID;
336
				}
337
338
				if ( ! empty( $columns['seq_id'] ) ) {
339
					$data[ $i ]['seq_id'] = Give()->seq_donation_number->get_serial_code( $payment->ID );
340
				}
341
342
				if ( ! empty( $columns['title_prefix'] ) ) {
343
					$data[ $i ]['title_prefix'] = ! empty( $payment->title_prefix ) ? $payment->title_prefix : '';
344
				}
345
346 View Code Duplication
				if ( ! empty( $columns['first_name'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
347
					$data[ $i ]['first_name'] = isset( $payment->first_name ) ? $payment->first_name : '';
348
				}
349
350 View Code Duplication
				if ( ! empty( $columns['last_name'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
351
					$data[ $i ]['last_name'] = isset( $payment->last_name ) ? $payment->last_name : '';
352
				}
353
354
				if ( ! empty( $columns['email'] ) ) {
355
					$data[ $i ]['email'] = $payment->email;
356
				}
357
358
				if ( ! empty( $columns['company'] ) ) {
359
					$data[ $i ]['company'] = empty( $payment_meta['_give_donation_company'] ) ? '' : str_replace( "\'", "'", $payment_meta['_give_donation_company'] );
360
				}
361
362 View Code Duplication
				if ( ! empty( $columns['address_line1'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
363
					$data[ $i ]['address_line1']   = isset( $address['line1'] ) ? $address['line1'] : '';
364
					$data[ $i ]['address_line2']   = isset( $address['line2'] ) ? $address['line2'] : '';
365
					$data[ $i ]['address_city']    = isset( $address['city'] ) ? $address['city'] : '';
366
					$data[ $i ]['address_state']   = isset( $address['state'] ) ? $address['state'] : '';
367
					$data[ $i ]['address_zip']     = isset( $address['zip'] ) ? $address['zip'] : '';
368
					$data[ $i ]['address_country'] = isset( $address['country'] ) ? $address['country'] : '';
369
				}
370
371
				if ( ! empty( $columns['comment'] ) ) {
372
					$comment               = give_get_donor_donation_comment( $payment->ID, $payment->donor_id );
373
					$data[ $i ]['comment'] = ! empty( $comment ) ? $comment->comment_content : '';
374
				}
375
376
				if ( ! empty( $columns['donation_total'] ) ) {
377
					$data[ $i ]['donation_total'] = give_format_amount( give_donation_amount( $payment->ID ) );
378
				}
379
380
				if ( ! empty( $columns['currency_code'] ) ) {
381
					$data[ $i ]['currency_code'] = empty( $payment_meta['_give_payment_currency'] ) ? give_get_currency() : $payment_meta['_give_payment_currency'];
382
				}
383
384
				if ( ! empty( $columns['currency_symbol'] ) ) {
385
					$currency_code                 = $data[ $i ]['currency_code'];
386
					$data[ $i ]['currency_symbol'] = give_currency_symbol( $currency_code, true );
387
				}
388
389
				if ( ! empty( $columns['donation_status'] ) ) {
390
					$data[ $i ]['donation_status'] = give_get_payment_status( $payment, true );
391
				}
392
393
				if ( ! empty( $columns['payment_gateway'] ) ) {
394
					$data[ $i ]['payment_gateway'] = $payment->gateway;
395
				}
396
397
				if ( ! empty( $columns['payment_mode'] ) ) {
398
					$data[ $i ]['payment_mode'] = $payment->mode;
399
				}
400
401
				if ( ! empty( $columns['form_id'] ) ) {
402
					$data[ $i ]['form_id'] = $payment->form_id;
403
				}
404
405
				if ( ! empty( $columns['form_title'] ) ) {
406
					$data[ $i ]['form_title'] = get_the_title( $payment->form_id );
407
				}
408
409
				if ( ! empty( $columns['form_level_id'] ) ) {
410
					$data[ $i ]['form_level_id'] = $payment->price_id;
411
				}
412
413
				if ( ! empty( $columns['form_level_title'] ) ) {
414
					$var_prices = give_has_variable_prices( $payment->form_id );
415
					if ( empty( $var_prices ) ) {
416
						$data[ $i ]['form_level_title'] = '';
417
					} else {
418
						if ( 'custom' === $payment->price_id ) {
419
							$custom_amount_text = give_get_meta( $payment->form_id, '_give_custom_amount_text', true );
420
421
							if ( empty( $custom_amount_text ) ) {
422
								$custom_amount_text = esc_html__( 'Custom', 'give' );
423
							}
424
							$data[ $i ]['form_level_title'] = $custom_amount_text;
425
						} else {
426
							$data[ $i ]['form_level_title'] = give_get_price_option_name( $payment->form_id, $payment->price_id );
427
						}
428
					}
429
				}
430
431
				if ( ! empty( $columns['donation_date'] ) ) {
432
					$payment_date                = strtotime( $payment->date );
433
					$data[ $i ]['donation_date'] = date( give_date_format(), $payment_date );
434
				}
435
436
				if ( ! empty( $columns['donation_time'] ) ) {
437
					$payment_date                = strtotime( $payment->date );
438
					$data[ $i ]['donation_time'] = date_i18n( 'H', $payment_date ) . ':' . date( 'i', $payment_date );
439
				}
440
441
				if ( ! empty( $columns['userid'] ) ) {
442
					$data[ $i ]['userid'] = $payment->user_id;
443
				}
444
445
				if ( ! empty( $columns['donorid'] ) ) {
446
					$data[ $i ]['donorid'] = $payment->customer_id;
447
				}
448
449
				if ( ! empty( $columns['donor_ip'] ) ) {
450
					$data[ $i ]['donor_ip'] = give_get_payment_user_ip( $payment->ID );
451
				}
452
453
				// Add custom field data.
454
				// First we remove the standard included keys from above.
455
				$remove_keys = array(
456
					'donation_id',
457
					'seq_id',
458
					'first_name',
459
					'last_name',
460
					'email',
461
					'address_line1',
462
					'address_line2',
463
					'address_city',
464
					'address_state',
465
					'address_zip',
466
					'address_country',
467
					'donation_total',
468
					'payment_gateway',
469
					'payment_mode',
470
					'form_id',
471
					'form_title',
472
					'form_level_id',
473
					'form_level_title',
474
					'donation_date',
475
					'donation_time',
476
					'userid',
477
					'donorid',
478
					'donor_ip',
479
				);
480
481
				// Removing above keys...
482
				foreach ( $remove_keys as $key ) {
483
					unset( $columns[ $key ] );
484
				}
485
486
				// Now loop through remaining meta fields.
487
				foreach ( $columns as $col ) {
0 ignored issues
show
Bug introduced by
The expression $columns of type false|array 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...
488
					$field_data         = get_post_meta( $payment->ID, $col, true );
489
					$data[ $i ][ $col ] = $field_data;
490
					unset( $columns[ $col ] );
491
				}
492
493
				/**
494
				 * Filter to modify Donation CSV data when exporting donation
495
				 *
496
				 * @since 2.1
497
				 *
498
				 * @param array Donation data
499
				 * @param Give_Payment              $payment Instance of Give_Payment
500
				 * @param array                     $columns Donation data $columns that are not being merge
501
				 * @param Give_Export_Donations_CSV $this    Instance of Give_Export_Donations_CSV
502
				 *
503
				 * @return array Donation data
504
				 */
505
				$data[ $i ] = apply_filters( 'give_export_donation_data', $data[ $i ], $payment, $columns, $this );
506
507
				$new_data = array();
508
				$old_data = $data[ $i ];
509
510
				// sorting the columns bas on row
511
				foreach ( $this->csv_cols() as $key => $value ) {
0 ignored issues
show
Bug introduced by
The expression $this->csv_cols() of type false|array 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...
512
					if ( array_key_exists( $key, $old_data ) ) {
513
						$new_data[ $key ] = $old_data[ $key ];
514
					}
515
				}
516
517
				$data[ $i ] = $new_data;
518
519
				// Increment iterator.
520
				$i ++;
521
522
			}
523
524
			$data = apply_filters( 'give_export_get_data', $data );
525
			$data = apply_filters( "give_export_get_data_{$this->export_type}", $data );
526
527
			return $data;
528
529
		}
530
531
		return array();
532
533
	}
534
535
	/**
536
	 * Return the calculated completion percentage.
537
	 *
538
	 * @since 2.1
539
	 *
540
	 * @return int
541
	 */
542
	public function get_percentage_complete() {
543
		$args = $this->get_donation_argument( array( 'number' => - 1 ) );
544
		if ( isset( $args['page'] ) ) {
545
			unset( $args['page'] );
546
		}
547
		$query      = give_get_payments( $args );
548
		$total      = count( $query );
549
		$percentage = 100;
550
		if ( $total > 0 ) {
551
			$percentage = ( ( 30 * $this->step ) / $total ) * 100;
552
		}
553
		if ( $percentage > 100 ) {
554
			$percentage = 100;
555
		}
556
557
		return $percentage;
558
	}
559
560
	/**
561
	 * Print the CSV rows for the current step.
562
	 *
563
	 * @access public
564
	 *
565
	 * @since  2.1
566
	 *
567
	 * @return string|false
568
	 */
569 View Code Duplication
	public function print_csv_rows() {
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...
570
571
		$row_data = '';
572
		$data     = $this->get_data();
573
		$cols     = $this->get_csv_cols();
574
575
		if ( $data ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data 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...
576
577
			// Output each row
578
			foreach ( $data as $row ) {
579
				$i = 1;
580
				foreach ( $row as $col_id => $column ) {
581
					// Make sure the column is valid
582
					if ( array_key_exists( $col_id, $cols ) ) {
583
						$row_data .= '"' . preg_replace( '/"/', "'", $column ) . '"';
584
						$row_data .= $i == count( $cols ) ? '' : ',';
585
						$i ++;
586
					}
587
				}
588
				$row_data .= "\r\n";
589
			}
590
591
			$this->stash_step_data( $row_data );
592
593
			return $row_data;
594
		}
595
596
		return false;
597
	}
598
}
599