Completed
Push — master ( b4488c...e1d700 )
by Devin
55:57 queued 36:00
created

Give_Customer::remove_payment()   B

Complexity

Conditions 9
Paths 19

Size

Total Lines 56
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 11.107

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 26
nc 19
nop 2
dl 0
loc 56
ccs 19
cts 27
cp 0.7037
crap 11.107
rs 7.1584
c 1
b 0
f 0

How to fix   Long Method   

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 22 and the first side effect is on line 14.

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
 * Customer (Donor) Object
4
 *
5
 * @package     Give
6
 * @subpackage  Classes/Customer
7
 * @copyright   Copyright (c) 2016, WordImpress
8
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
 * @since       1.0
10
 */
11
12
// Exit if accessed directly
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * Give_Customer Class
19
 *
20
 * @since 1.0
21
 */
22
class Give_Customer {
23
24
	/**
25
	 * The customer ID
26
	 *
27
	 * @since 1.0
28
	 */
29
	public $id = 0;
30
31
	/**
32
	 * The customer's purchase count
33
	 *
34
	 * @since 1.0
35
	 */
36
	public $purchase_count = 0;
37
38
	/**
39
	 * The customer's lifetime value
40
	 *
41
	 * @since 1.0
42
	 */
43
	public $purchase_value = 0;
44
45
	/**
46
	 * The customer's email
47
	 *
48
	 * @since 1.0
49
	 */
50
	public $email;
51
52
	/**
53
	 * The customer's name
54
	 *
55
	 * @since 1.0
56
	 */
57
	public $name;
58
59
	/**
60
	 * The customer's creation date
61
	 *
62
	 * @since 1.0
63
	 */
64
	public $date_created;
65
66
	/**
67
	 * The payment IDs associated with the customer
68
	 *
69
	 * @since  1.0
70
	 */
71
	public $payment_ids;
72
73
	/**
74
	 * The user ID associated with the customer
75
	 *
76
	 * @since  1.0
77
	 */
78
	public $user_id;
79
80
	/**
81
	 * Customer Notes
82
	 *
83
	 * @since  1.0
84
	 */
85
	public $notes;
86
87
	/**
88
	 * The Database Abstraction
89
	 *
90
	 * @since  1.0
91
	 */
92
	protected $db;
93
94
	/**
95
	 * Give_Customer constructor.
96
	 *
97
	 * @param bool $_id_or_email
98
	 * @param bool $by_user_id
99
	 */
100 34
	public function __construct( $_id_or_email = false, $by_user_id = false ) {
101
102 34
		$this->db = new Give_DB_Customers;
103
104 34
		if ( false === $_id_or_email || ( is_numeric( $_id_or_email ) && (int) $_id_or_email !== absint( $_id_or_email ) ) ) {
105
			return false;
106
		}
107
108 34
		$by_user_id = is_bool( $by_user_id ) ? $by_user_id : false;
109
110 34
		if ( is_numeric( $_id_or_email ) ) {
111 34
			$field = $by_user_id ? 'user_id' : 'id';
112 34
		} else {
113 34
			$field = 'email';
114
		}
115
116 34
		$customer = $this->db->get_customer_by( $field, $_id_or_email );
117
118 34
		if ( empty( $customer ) || ! is_object( $customer ) ) {
119 34
			return false;
120
		}
121
122 34
		$this->setup_customer( $customer );
123
124 34
	}
125
126
	/**
127
	 * Given the customer data, let's set the variables
128
	 *
129
	 * @since  1.0
130
	 *
131
	 * @param  object $customer The Customer Object
132
	 *
133
	 * @return bool             If the setup was successful or not
134
	 */
135 34
	private function setup_customer( $customer ) {
136
137 34
		if ( ! is_object( $customer ) ) {
138
			return false;
139
		}
140
141 34
		foreach ( $customer as $key => $value ) {
142
143
			switch ( $key ) {
144
145 34
				case 'notes':
146 34
					$this->$key = $this->get_notes();
147 34
					break;
148
149 34
				default:
150 34
					$this->$key = $value;
151 34
					break;
152
153 34
			}
154
155 34
		}
156
157
		// Customer ID and email are the only things that are necessary, make sure they exist
158 34
		if ( ! empty( $this->id ) && ! empty( $this->email ) ) {
159 34
			return true;
160
		}
161
162
		return false;
163
164
	}
165
166
	/**
167
	 * Magic __get function to dispatch a call to retrieve a private property
168
	 *
169
	 * @since 1.0
170
	 */
171 1
	public function __get( $key ) {
172
173 1
		if ( method_exists( $this, 'get_' . $key ) ) {
174
175
			return call_user_func( array( $this, 'get_' . $key ) );
176
177
		} else {
178
179 1
			return new WP_Error( 'give-customer-invalid-property', sprintf( __( 'Can\'t get property %s', 'give' ), $key ) );
180
181
		}
182
183
	}
184
185
	/**
186
	 * Creates a customer
187
	 *
188
	 * @since  1.0
189
	 *
190
	 * @param  array $data Array of attributes for a customer
191
	 *
192
	 * @return mixed        False if not a valid creation, Customer ID if user is found or valid creation
193
	 */
194 34
	public function create( $data = array() ) {
195
196 34
		if ( $this->id != 0 || empty( $data ) ) {
197
			return false;
198
		}
199
200
		$defaults = array(
201
			'payment_ids' => ''
202 34
		);
203
204 34
		$args = wp_parse_args( $data, $defaults );
205 34
		$args = $this->sanitize_columns( $args );
206
207 34
		if ( empty( $args['email'] ) || ! is_email( $args['email'] ) ) {
208
			return false;
209
		}
210
211 34
		if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
212
			$args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
213
		}
214
215 34
		do_action( 'give_customer_pre_create', $args );
216
217 34
		$created = false;
218
219
		// The DB class 'add' implies an update if the customer being asked to be created already exists
220 34
		if ( $this->db->add( $data ) ) {
221
222
			// We've successfully added/updated the customer, reset the class vars with the new data
223 34
			$customer = $this->db->get_customer_by( 'email', $args['email'] );
224
225
			// Setup the customer data with the values from DB
226 34
			$this->setup_customer( $customer );
227
228 34
			$created = $this->id;
229 34
		}
230
231 34
		do_action( 'give_customer_post_create', $created, $args );
232
233 34
		return $created;
234
235
	}
236
237
	/**
238
	 * Update a customer record
239
	 *
240
	 * @since  1.0
241
	 *
242
	 * @param  array $data Array of data attributes for a customer (checked via whitelist)
243
	 *
244
	 * @return bool         If the update was successful or not
245
	 */
246 34
	public function update( $data = array() ) {
247
248 34
		if ( empty( $data ) ) {
249 1
			return false;
250
		}
251
252 34
		$data = $this->sanitize_columns( $data );
253
254 34
		do_action( 'give_customer_pre_update', $this->id, $data );
255
256 34
		$updated = false;
257
258 34
		if ( $this->db->update( $this->id, $data ) ) {
259
260 34
			$customer = $this->db->get_customer_by( 'id', $this->id );
261 34
			$this->setup_customer( $customer );
262
263 34
			$updated = true;
264 34
		}
265
266 34
		do_action( 'give_customer_post_update', $updated, $this->id, $data );
267
268 34
		return $updated;
269
	}
270
271
272
	/**
273
	 * Attach payment to the customer then triggers increasing stats
274
	 *
275
	 * @since  1.0
276
	 *
277
	 * @param  int  $payment_id   The payment ID to attach to the customer
278
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not
279
	 *
280
	 * @return bool            If the attachment was successfuly
281
	 */
282 34
	public function attach_payment( $payment_id = 0, $update_stats = true ) {
283
284 34
		if ( empty( $payment_id ) ) {
285 1
			return false;
286
		}
287
288 34
		if ( empty( $this->payment_ids ) ) {
289
290 34
			$new_payment_ids = $payment_id;
291
292 34
		} else {
293
294 5
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
295
296 5
			if ( in_array( $payment_id, $payment_ids ) ) {
297 1
				$update_stats = false;
298 1
			}
299
300 5
			$payment_ids[] = $payment_id;
301
302 5
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
303
304
		}
305
306 34
		do_action( 'give_customer_pre_attach_payment', $payment_id, $this->id );
307
308 34
		$payment_added = $this->update( array( 'payment_ids' => $new_payment_ids ) );
309
310 34
		if ( $payment_added ) {
311
312 34
			$this->payment_ids = $new_payment_ids;
313
314
			// We added this payment successfully, increment the stats
315 34
			if ( $update_stats ) {
316 1
				$payment_amount = give_get_payment_amount( $payment_id );
317
318 1
				if ( ! empty( $payment_amount ) ) {
319
					$this->increase_value( $payment_amount );
320
				}
321
322 1
				$this->increase_purchase_count();
323 1
			}
324
325 34
		}
326
327 34
		do_action( 'give_customer_post_attach_payment', $payment_added, $payment_id, $this->id );
328
329 34
		return $payment_added;
330
	}
331
332
333
	/**
334
	 * Remove a payment from this customer, then triggers reducing stats
335
	 *
336
	 * @since  1.0
337
	 *
338
	 * @param  integer $payment_id   The Payment ID to remove
339
	 * @param  bool    $update_stats For backwards compatibility, if we should increase the stats or not
340
	 *
341
	 * @return boolean             If the removal was successful
342
	 */
343 2
	public function remove_payment( $payment_id = 0, $update_stats = true ) {
344
345 2
		if ( empty( $payment_id ) ) {
346
			return false;
347
		}
348
349 2
		$payment = new Give_Payment( $payment_id );
350
351 2
		if ( 'publish' !== $payment->status && 'revoked' !== $payment->status ) {
0 ignored issues
show
Documentation introduced by
The property $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...
352
			$update_stats = false;
353 2
		}
354
355 2
		$new_payment_ids = '';
356 2
357
		if ( ! empty( $this->payment_ids ) ) {
358
359
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
360 2
361 2
			$pos = array_search( $payment_id, $payment_ids );
362
			if ( false === $pos ) {
363 2
				return false;
364
			}
365 2
366
			unset( $payment_ids[ $pos ] );
367 2
			$payment_ids = array_filter( $payment_ids );
368
369 2
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
370
371 2
		}
372
373 2
		do_action( 'give_customer_pre_remove_payment', $payment_id, $this->id );
374
375 2
		$payment_removed = $this->update( array( 'payment_ids' => $new_payment_ids ) );
376
377
		if ( $payment_removed ) {
378
379
			$this->payment_ids = $new_payment_ids;
380
381
			if ( $update_stats ) {
382
				// We removed this payment successfully, decrement the stats
383
				$payment_amount = give_get_payment_amount( $payment_id );
384
385
				if ( ! empty( $payment_amount ) ) {
386 2
					$this->decrease_value( $payment_amount );
387
				}
388 2
389
				$this->decrease_purchase_count();
390 2
			}
391
392
		}
393
394
		do_action( 'give_customer_post_remove_payment', $payment_removed, $payment_id, $this->id );
395
396
		return $payment_removed;
397
398
	}
399
400
	/**
401
	 * Increase the purchase count of a customer
402
	 *
403 34
	 * @since  1.0
404
	 *
405
	 * @param  integer $count The number to increment by
406 34
	 *
407 1
	 * @return int            The purchase count
408
	 */
409
	public function increase_purchase_count( $count = 1 ) {
410 34
411
		// Make sure it's numeric and not negative
412 34
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
413
			return false;
414 34
		}
415 34
416 34
		$new_total = (int) $this->purchase_count + (int) $count;
417
418 34
		do_action( 'give_customer_pre_increase_purchase_count', $count, $this->id );
419
420 34
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
421
			$this->purchase_count = $new_total;
422
		}
423
424
		do_action( 'give_customer_post_increase_purchase_count', $this->purchase_count, $count, $this->id );
425
426
		return $this->purchase_count;
427
	}
428
429
	/**
430
	 * Decrease the customer purchase count
431
	 *
432 2
	 * @since  1.0
433
	 *
434
	 * @param  integer $count The amount to decrease by
435 2
	 *
436 1
	 * @return mixed          If successful, the new count, otherwise false
437
	 */
438
	public function decrease_purchase_count( $count = 1 ) {
439 2
440
		// Make sure it's numeric and not negative
441 2
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
442 1
			return false;
443 1
		}
444
445 2
		$new_total = (int) $this->purchase_count - (int) $count;
446
447 2
		if ( $new_total < 0 ) {
448 2
			$new_total = 0;
449 2
		}
450
451 2
		do_action( 'give_customer_pre_decrease_purchase_count', $count, $this->id );
452
453 2
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
454
			$this->purchase_count = $new_total;
455
		}
456
457
		do_action( 'give_customer_post_decrease_purchase_count', $this->purchase_count, $count, $this->id );
458
459
		return $this->purchase_count;
460
	}
461
462
	/**
463
	 * Increase the customer's lifetime value
464
	 *
465 34
	 * @since  1.0
466
	 *
467 34
	 * @param  float $value The value to increase by
468
	 *
469 34
	 * @return mixed         If successful, the new value, otherwise false
470
	 */
471 34
	public function increase_value( $value = 0.00 ) {
472 34
473 34
		$new_value = floatval( $this->purchase_value ) + $value;
474
475 34
		do_action( 'give_customer_pre_increase_value', $value, $this->id );
476
477 34
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
478
			$this->purchase_value = $new_value;
479
		}
480
481
		do_action( 'give_customer_post_increase_value', $this->purchase_value, $value, $this->id );
482
483
		return $this->purchase_value;
484
	}
485
486
	/**
487
	 * Decrease a customer's lifetime value
488
	 *
489 2
	 * @since  1.0
490
	 *
491 2
	 * @param  float $value The value to decrease by
492
	 *
493 2
	 * @return mixed         If successful, the new value, otherwise false
494 1
	 */
495 1
	public function decrease_value( $value = 0.00 ) {
496
497 2
		$new_value = floatval( $this->purchase_value ) - $value;
498
499 2
		if ( $new_value < 0 ) {
500 2
			$new_value = 0.00;
501 2
		}
502
503 2
		do_action( 'give_customer_pre_decrease_value', $value, $this->id );
504
505 2
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
506
			$this->purchase_value = $new_value;
507
		}
508
509
		do_action( 'give_customer_post_decrease_value', $this->purchase_value, $value, $this->id );
510
511
		return $this->purchase_value;
512
	}
513
514
	/**
515
	 * Get the parsed notes for a customer as an array
516
	 *
517
	 * @since  1.0
518 34
	 *
519
	 * @param  integer $length The number of notes to get
520 34
	 * @param  integer $paged  What note to start at
521 34
	 *
522
	 * @return array           The notes requsted
523 34
	 */
524 34
	public function get_notes( $length = 20, $paged = 1 ) {
525
526 34
		$length = is_numeric( $length ) ? $length : 20;
527
		$offset = is_numeric( $paged ) && $paged != 1 ? ( ( absint( $paged ) - 1 ) * $length ) : 0;
528 34
529
		$all_notes   = $this->get_raw_notes();
530
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
531
532
		$desired_notes = array_slice( $notes_array, $offset, $length );
533
534
		return $desired_notes;
535
536
	}
537
538 1
	/**
539
	 * Get the total number of notes we have after parsing
540 1
	 *
541 1
	 * @since  1.0
542
	 * @return int The number of notes for the customer
543 1
	 */
544
	public function get_notes_count() {
545
546
		$all_notes   = $this->get_raw_notes();
547
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
548
549
		return count( $notes_array );
550
551
	}
552
553
	/**
554
	 * Add a note for the customer
555
	 *
556 1
	 * @since  1.0
557
	 *
558 1
	 * @param string $note The note to add
559 1
	 *
560
	 * @return string|boolean The new note if added succesfully, false otherwise
561
	 */
562
	public function add_note( $note = '' ) {
563 1
564
		$note = trim( $note );
565 1
		if ( empty( $note ) ) {
566 1
			return false;
567 1
		}
568
569 1
		$notes = $this->get_raw_notes();
570 1
571 1
		if ( empty( $notes ) ) {
572
			$notes = '';
573 1
		}
574
575 1
		$note_string = date_i18n( 'F j, Y H:i:s', current_time( 'timestamp' ) ) . ' - ' . $note;
576
		$new_note    = apply_filters( 'give_customer_add_note_string', $note_string );
577 1
		$notes .= "\n\n" . $new_note;
578 1
579 1
		do_action( 'give_customer_pre_add_note', $new_note, $this->id );
580
581 1
		$updated = $this->update( array( 'notes' => $notes ) );
582
583
		if ( $updated ) {
584 1
			$this->notes = $this->get_notes();
585
		}
586
587
		do_action( 'give_customer_post_add_note', $this->notes, $new_note, $this->id );
588
589
		// Return the formatted note, so we can test, as well as update any displays
590
		return $new_note;
591
592
	}
593
594 34
	/**
595
	 * Get the notes column for the customer
596 34
	 *
597
	 * @since  1.0
598 34
	 * @return string The Notes for the customer, non-parsed
599
	 */
600
	private function get_raw_notes() {
601
602
		$all_notes = $this->db->get_column( 'notes', $this->id );
603
604
		return $all_notes;
605
606
	}
607
608
	/**
609
	 * Sanitize the data for update/create
610
	 *
611 34
	 * @since  1.0
612
	 *
613 34
	 * @param  array $data The data to sanitize
614 34
	 *
615
	 * @return array       The sanitized data, based off column defaults
616 34
	 */
617
	private function sanitize_columns( $data ) {
618
619 34
		$columns        = $this->db->get_columns();
620 34
		$default_values = $this->db->get_column_defaults();
621
622
		foreach ( $columns as $key => $type ) {
623
624
			// Only sanitize data that we were provided
625 34
			if ( ! array_key_exists( $key, $data ) ) {
626 34
				continue;
627 34
			}
628 34
629 1
			switch ( $type ) {
630 1
631 34
				case '%s':
632
					if ( 'email' == $key ) {
633 34
						$data[ $key ] = sanitize_email( $data[ $key ] );
634
					} elseif ( 'notes' == $key ) {
635 34
						$data[ $key ] = strip_tags( $data[ $key ] );
636 34
					} else {
637
						$data[ $key ] = sanitize_text_field( $data[ $key ] );
638
					}
639 34
					break;
640
641 34
				case '%d':
642
					if ( ! is_numeric( $data[ $key ] ) || (int) $data[ $key ] !== absint( $data[ $key ] ) ) {
643 34
						$data[ $key ] = $default_values[ $key ];
644
					} else {
645 34
						$data[ $key ] = absint( $data[ $key ] );
646
					}
647 34
					break;
648
649
				case '%f':
650 34
					// Convert what was given to a float
651
					$value = floatval( $data[ $key ] );
652 34
653
					if ( ! is_float( $value ) ) {
654
						$data[ $key ] = $default_values[ $key ];
655
					} else {
656
						$data[ $key ] = $value;
657
					}
658
					break;
659
660 34
				default:
661
					$data[ $key ] = sanitize_text_field( $data[ $key ] );
662 34
					break;
663
664
			}
665
666
		}
667
668
		return $data;
669
	}
670
671
}
672