Test Failed
Push — issues/1944 ( ef3669...b2a977 )
by Ravinder
05:02
created

Give_Donor::get_total_donation_amount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Donor
4
 *
5
 * @package     Give
6
 * @subpackage  Classes/Give_Donor
7
 * @copyright   Copyright (c) 2016, WordImpress
8
 * @license     https://opensource.org/licenses/gpl-license 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_Donor Class
19
 *
20
 * This class handles customers.
21
 *
22
 * @since 1.0
23
 */
24
class Give_Donor {
25
26
	/**
27
	 * The donor ID
28
	 *
29
	 * @since  1.0
30
	 * @access public
31
	 *
32
	 * @var    int
33
	 */
34
	public $id = 0;
35
36
	/**
37
	 * The donor's donation count.
38
	 *
39
	 * @since  1.0
40
	 * @access public
41
	 *
42
	 * @var    int
43
	 */
44
	public $purchase_count = 0;
45
46
	/**
47
	 * The donor's lifetime value.
48
	 *
49
	 * @since  1.0
50
	 * @access public
51
	 *
52
	 * @var    int
53
	 */
54
	public $purchase_value = 0;
55
56
	/**
57
	 * The donor's email.
58
	 *
59
	 * @since  1.0
60
	 * @access public
61
	 *
62
	 * @var    string
63
	 */
64
	public $email;
65
66
	/**
67
	 * The donor's emails.
68
	 *
69
	 * @since  1.7
70
	 * @access public
71
	 *
72
	 * @var    array
73
	 */
74
	public $emails;
75
76
	/**
77
	 * The donor's name.
78
	 *
79
	 * @since  1.0
80
	 * @access public
81
	 *
82
	 * @var    string
83
	 */
84
	public $name;
85
86
	/**
87
	 * The donor creation date.
88
	 *
89
	 * @since  1.0
90
	 * @access public
91
	 *
92
	 * @var    string
93
	 */
94
	public $date_created;
95
96
	/**
97
	 * The payment IDs associated with the donor.
98
	 *
99
	 * @since  1.0
100
	 * @access public
101
	 *
102
	 * @var    string
103
	 */
104
	public $payment_ids;
105
106
	/**
107
	 * The user ID associated with the donor.
108
	 *
109
	 * @since  1.0
110
	 * @access public
111
	 *
112
	 * @var    int
113
	 */
114
	public $user_id;
115
116
	/**
117
	 * Donor notes saved by admins.
118
	 *
119
	 * @since  1.0
120
	 * @access public
121
	 *
122
	 * @var    string
123
	 */
124
	public $notes;
125
126
	/**
127
	 * Donor address.
128
	 *
129
	 * @since  1.0
130
	 * @access public
131
	 *
132
	 * @var    array
133
	 */
134
	public $address;
135
136
	/**
137
	 * The Database Abstraction
138
	 *
139
	 * @since  1.0
140
	 * @access protected
141
	 *
142
	 * @var    Give_DB_Donors
143
	 */
144
	protected $db;
145
146
	/**
147
	 * Give_Donor constructor.
148
	 *
149
	 * @param bool $_id_or_email
150
	 * @param bool $by_user_id
151
	 */
152
	public function __construct( $_id_or_email = false, $by_user_id = false ) {
153
154
		$this->db = Give()->donors;
155
156
		if (
157
			false === $_id_or_email
158
			|| ( is_numeric( $_id_or_email ) && (int) $_id_or_email !== absint( $_id_or_email ) )
159
		) {
160
			return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
161
		}
162
163
		$by_user_id = is_bool( $by_user_id ) ? $by_user_id : false;
164
165
		if ( is_numeric( $_id_or_email ) ) {
166
			$field = $by_user_id ? 'user_id' : 'id';
167
		} else {
168
			$field = 'email';
169
		}
170
171
		$donor = $this->db->get_donor_by( $field, $_id_or_email );
172
173
		if ( empty( $donor ) || ! is_object( $donor ) ) {
174
			return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
175
		}
176
177
		$this->setup_donor( $donor );
178
179
	}
180
181
	/**
182
	 * Setup Donor
183
	 *
184
	 * Set donor variables.
185
	 *
186
	 * @since  1.0
187
	 * @access private
188
	 *
189
	 * @param  object $donor The Donor Object.
190
	 *
191
	 * @return bool             If the setup was successful or not.
192
	 */
193
	private function setup_donor( $donor ) {
194
195
		if ( ! is_object( $donor ) ) {
196
			return false;
197
		}
198
199
		if( ! ( $donor_vars = Give_Cache::get_group( $donor->id, 'give-donors' )) ){
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
200
			foreach ( $donor as $key => $value ) {
201
202
				switch ( $key ) {
203
204
					case 'notes':
205
						$this->$key = $this->get_notes();
206
						break;
207
208
					default:
209
						$this->$key = $value;
210
						break;
211
212
				}
213
			}
214
215
			// Get donor's all email including primary email.
216
			$this->emails = (array) $this->get_meta( 'additional_email', false );
217
			$this->emails = array( 'primary' => $this->email ) + $this->emails;
218
219
			$this->setup_address();
220
221
			wp_cache_set( $donor->id, get_object_vars( $this ), 'give-donors' );
222
		} else{
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
223
			foreach ( $donor_vars as $donor_var => $value ) {
224
				$this->$donor_var = $value;
225
			}
226
		}
227
228
		// Donor ID and email are the only things that are necessary, make sure they exist.
229
		if ( ! empty( $this->id ) && ! empty( $this->email ) ) {
230
			return true;
231
		}
232
233
		return false;
234
235
	}
236
237
238
	/**
239
	 * Setup donor address.
240
	 *
241
	 * @since 2.0
242
	 * @access public
243
	 */
244
	public function setup_address() {
245
		global $wpdb;
246
		$meta_type = Give()->donor_meta->meta_type;
247
248
		$addresses = $wpdb->get_results(
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...
249
			$wpdb->prepare(
250
				"
251
				SELECT meta_key, meta_value FROM {$wpdb->donormeta}
252
				WHERE meta_key
253
				LIKE '%%%s%%'
254
				AND {$meta_type}_id=%d
255
				",
256
				'give_donor_address',
257
				$this->id
258
			),
259
			ARRAY_N
260
		);
261
262
		if ( empty( $addresses ) ) {
263
			return array();
264
		}
265
266
		foreach ( $addresses as $address ) {
267
			$address[0] = str_replace( '_give_donor_address_', '', $address[0] );
268
			$address[0] = explode( '_', $address[0] );
269
270
			if ( 3 === count( $address[0] ) ) {
271
				$this->address[ $address[0][0] ][ $address[0][2] ][ $address[0][1] ] = $address[1];
272
			} else {
273
				$this->address[ $address[0][0] ][ $address[0][1] ] = $address[1];
274
			}
275
		}
276
	}
277
278
	/**
279
	 * Magic __get function to dispatch a call to retrieve a private property.
280
	 *
281
	 * @since  1.0
282
	 * @access public
283
	 * @param $key
284
	 *
285
	 * @return mixed|\WP_Error
286
	 */
287 View Code Duplication
	public function __get( $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...
288
289
		if ( method_exists( $this, 'get_' . $key ) ) {
290
291
			return call_user_func( array( $this, 'get_' . $key ) );
292
293
		} else {
294
295
			/* translators: %s: property key */
296
			return new WP_Error( 'give-donor-invalid-property', sprintf( esc_html__( 'Can\'t get property %s.', 'give' ), $key ) );
297
298
		}
299
300
	}
301
302
	/**
303
	 * Creates a donor.
304
	 *
305
	 * @since  1.0
306
	 * @access public
307
	 *
308
	 * @param  array $data Array of attributes for a donor.
309
	 *
310
	 * @return bool|int    False if not a valid creation, donor ID if user is found or valid creation.
311
	 */
312
	public function create( $data = array() ) {
313
314
		if ( $this->id != 0 || empty( $data ) ) {
0 ignored issues
show
introduced by
Found "!= 0". Use Yoda Condition checks, you must
Loading history...
315
			return false;
316
		}
317
318
		$defaults = array(
319
			'payment_ids' => '',
320
		);
321
322
		$args = wp_parse_args( $data, $defaults );
323
		$args = $this->sanitize_columns( $args );
324
325
		if ( empty( $args['email'] ) || ! is_email( $args['email'] ) ) {
326
			return false;
327
		}
328
329 View Code Duplication
		if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
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...
330
			$args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
331
		}
332
333
		/**
334
		 * Fires before creating donors.
335
		 *
336
		 * @since 1.0
337
		 *
338
		 * @param array $args Donor attributes.
339
		 */
340
		do_action( 'give_donor_pre_create', $args );
341
342
		$created = false;
343
344
		// The DB class 'add' implies an update if the donor being asked to be created already exists
345 View Code Duplication
		if ( $this->db->add( $data ) ) {
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...
346
347
			// We've successfully added/updated the donor, reset the class vars with the new data
348
			$donor = $this->db->get_donor_by( 'email', $args['email'] );
349
350
			// Setup the donor data with the values from DB
351
			$this->setup_donor( $donor );
352
353
			$created = $this->id;
354
		}
355
356
		/**
357
		 * Fires after creating donors.
358
		 *
359
		 * @since 1.0
360
		 *
361
		 * @param bool|int $created False if not a valid creation, donor ID if user is found or valid creation.
362
		 * @param array $args Customer attributes.
363
		 */
364
		do_action( 'give_donor_post_create', $created, $args );
365
366
		return $created;
367
368
	}
369
370
	/**
371
	 * Updates a donor record.
372
	 *
373
	 * @since  1.0
374
	 * @access public
375
	 *
376
	 * @param  array $data Array of data attributes for a donor (checked via whitelist).
377
	 *
378
	 * @return bool        If the update was successful or not.
379
	 */
380
	public function update( $data = array() ) {
381
382
		if ( empty( $data ) ) {
383
			return false;
384
		}
385
386
		$data = $this->sanitize_columns( $data );
387
388
		/**
389
		 * Fires before updating donors.
390
		 *
391
		 * @since 1.0
392
		 *
393
		 * @param int $donor_id Donor id.
394
		 * @param array $data Donor attributes.
395
		 */
396
		do_action( 'give_donor_pre_update', $this->id, $data );
397
398
		$updated = false;
399
400 View Code Duplication
		if ( $this->db->update( $this->id, $data ) ) {
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...
401
402
			$donor = $this->db->get_donor_by( 'id', $this->id );
403
			$this->setup_donor( $donor );
404
405
			$updated = true;
406
		}
407
408
		/**
409
		 * Fires after updating donors.
410
		 *
411
		 * @since 1.0
412
		 *
413
		 * @param bool $updated If the update was successful or not.
414
		 * @param int $donor_id Donor id.
415
		 * @param array $data Donor attributes.
416
		 */
417
		do_action( 'give_donor_post_update', $updated, $this->id, $data );
418
419
		return $updated;
420
	}
421
422
	/**
423
	 * Attach Payment
424
	 *
425
	 * Attach payment to the donor then triggers increasing stats.
426
	 *
427
	 * @since  1.0
428
	 * @access public
429
	 *
430
	 * @param  int $payment_id The payment ID to attach to the donor.
431
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
432
	 *
433
	 * @return bool            If the attachment was successfully.
434
	 */
435
	public function attach_payment( $payment_id = 0, $update_stats = true ) {
436
437
		if ( empty( $payment_id ) ) {
438
			return false;
439
		}
440
441
		if ( empty( $this->payment_ids ) ) {
442
443
			$new_payment_ids = $payment_id;
444
445
		} else {
446
447
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
448
449
			if ( in_array( $payment_id, $payment_ids ) ) {
450
				$update_stats = false;
451
			}
452
453
			$payment_ids[] = $payment_id;
454
455
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
456
457
		}
458
459
		/**
460
		 * Fires before attaching payments to customers.
461
		 *
462
		 * @since 1.0
463
		 *
464
		 * @param int $payment_id Payment id.
465
		 * @param int $donor_id Customer id.
466
		 */
467
		do_action( 'give_donor_pre_attach_payment', $payment_id, $this->id );
468
469
		$payment_added = $this->update( array( 'payment_ids' => $new_payment_ids ) );
470
471 View Code Duplication
		if ( $payment_added ) {
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...
472
473
			$this->payment_ids = $new_payment_ids;
0 ignored issues
show
Documentation Bug introduced by
It seems like $new_payment_ids can also be of type integer. However, the property $payment_ids 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...
474
475
			// We added this payment successfully, increment the stats
476
			if ( $update_stats ) {
477
				$payment_amount = give_get_payment_amount( $payment_id );
478
479
				if ( ! empty( $payment_amount ) ) {
480
					$this->increase_value( $payment_amount );
481
				}
482
483
				$this->increase_purchase_count();
484
			}
485
		}
486
487
		/**
488
		 * Fires after attaching payments to the donor.
489
		 *
490
		 * @since 1.0
491
		 *
492
		 * @param bool $payment_added If the attachment was successfully.
493
		 * @param int $payment_id Payment id.
494
		 * @param int $donor_id Donor id.
495
		 */
496
		do_action( 'give_donor_post_attach_payment', $payment_added, $payment_id, $this->id );
497
498
		return $payment_added;
499
	}
500
501
	/**
502
	 * Remove Payment
503
	 *
504
	 * Remove a payment from this donor, then triggers reducing stats.
505
	 *
506
	 * @since  1.0
507
	 * @access public
508
	 *
509
	 * @param  int $payment_id The Payment ID to remove.
510
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
511
	 *
512
	 * @return boolean               If the removal was successful.
513
	 */
514
	public function remove_payment( $payment_id = 0, $update_stats = true ) {
515
516
		if ( empty( $payment_id ) ) {
517
			return false;
518
		}
519
520
		$payment = new Give_Payment( $payment_id );
521
522
		if ( 'publish' !== $payment->status && 'revoked' !== $payment->status ) {
523
			$update_stats = false;
524
		}
525
526
		$new_payment_ids = '';
527
528
		if ( ! empty( $this->payment_ids ) ) {
529
530
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
531
532
			$pos = array_search( $payment_id, $payment_ids );
533
			if ( false === $pos ) {
534
				return false;
535
			}
536
537
			unset( $payment_ids[ $pos ] );
538
			$payment_ids = array_filter( $payment_ids );
539
540
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
541
542
		}
543
544
		/**
545
		 * Fires before removing payments from customers.
546
		 *
547
		 * @since 1.0
548
		 *
549
		 * @param int $payment_id Payment id.
550
		 * @param int $donor_id Customer id.
551
		 */
552
		do_action( 'give_donor_pre_remove_payment', $payment_id, $this->id );
553
554
		$payment_removed = $this->update( array( 'payment_ids' => $new_payment_ids ) );
555
556 View Code Duplication
		if ( $payment_removed ) {
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...
557
558
			$this->payment_ids = $new_payment_ids;
559
560
			if ( $update_stats ) {
561
				// We removed this payment successfully, decrement the stats
562
				$payment_amount = give_get_payment_amount( $payment_id );
563
564
				if ( ! empty( $payment_amount ) ) {
565
					$this->decrease_value( $payment_amount );
566
				}
567
568
				$this->decrease_donation_count();
569
			}
570
		}
571
572
		/**
573
		 * Fires after removing payments from donors.
574
		 *
575
		 * @since 1.0
576
		 *
577
		 * @param bool $payment_removed If the removal was successfully.
578
		 * @param int $payment_id Payment id.
579
		 * @param int $donor_id Donor id.
580
		 */
581
		do_action( 'give_donor_post_remove_payment', $payment_removed, $payment_id, $this->id );
582
583
		return $payment_removed;
584
585
	}
586
587
	/**
588
	 * Increase the donation count of a donor.
589
	 *
590
	 * @since  1.0
591
	 * @access public
592
	 *
593
	 * @param  int $count The number to increase by.
594
	 *
595
	 * @return int        The donation count.
596
	 */
597
	public function increase_purchase_count( $count = 1 ) {
598
599
		// Make sure it's numeric and not negative.
600
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
601
			return false;
602
		}
603
604
		$new_total = (int) $this->purchase_count + (int) $count;
605
606
		/**
607
		 * Fires before increasing the donor's donation count.
608
		 *
609
		 * @since 1.0
610
		 *
611
		 * @param int $count The number to increase by.
612
		 * @param int $donor_id Donor id.
613
		 */
614
		do_action( 'give_donor_pre_increase_donation_count', $count, $this->id );
615
616
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
617
			$this->purchase_count = $new_total;
618
		}
619
620
		/**
621
		 * Fires after increasing the donor's donation count.
622
		 *
623
		 * @since 1.0
624
		 *
625
		 * @param int $purchase_count Donor donation count.
626
		 * @param int $count The number increased by.
627
		 * @param int $donor_id Donor id.
628
		 */
629
		do_action( 'give_donor_post_increase_donation_count', $this->purchase_count, $count, $this->id );
630
631
		return $this->purchase_count;
632
	}
633
634
	/**
635
	 * Decrease the donor donation count.
636
	 *
637
	 * @since  1.0
638
	 * @access public
639
	 *
640
	 * @param  int $count The amount to decrease by.
641
	 *
642
	 * @return mixed      If successful, the new count, otherwise false.
643
	 */
644
	public function decrease_donation_count( $count = 1 ) {
645
646
		// Make sure it's numeric and not negative
647
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
648
			return false;
649
		}
650
651
		$new_total = (int) $this->purchase_count - (int) $count;
652
653
		if ( $new_total < 0 ) {
654
			$new_total = 0;
655
		}
656
657
		/**
658
		 * Fires before decreasing the donor's donation count.
659
		 *
660
		 * @since 1.0
661
		 *
662
		 * @param int $count The number to decrease by.
663
		 * @param int $donor_id Customer id.
664
		 */
665
		do_action( 'give_donor_pre_decrease_donation_count', $count, $this->id );
666
667
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
668
			$this->purchase_count = $new_total;
669
		}
670
671
		/**
672
		 * Fires after decreasing the donor's donation count.
673
		 *
674
		 * @since 1.0
675
		 *
676
		 * @param int $purchase_count Donor's donation count.
677
		 * @param int $count The number decreased by.
678
		 * @param int $donor_id Donor id.
679
		 */
680
		do_action( 'give_donor_post_decrease_donation_count', $this->purchase_count, $count, $this->id );
681
682
		return $this->purchase_count;
683
	}
684
685
	/**
686
	 * Increase the donor's lifetime value.
687
	 *
688
	 * @since  1.0
689
	 * @access public
690
	 *
691
	 * @param  float $value The value to increase by.
692
	 *
693
	 * @return mixed        If successful, the new value, otherwise false.
694
	 */
695
	public function increase_value( $value = 0.00 ) {
696
697
		$new_value = floatval( $this->purchase_value ) + $value;
698
699
		/**
700
		 * Fires before increasing donor lifetime value.
701
		 *
702
		 * @since 1.0
703
		 *
704
		 * @param float $value The value to increase by.
705
		 * @param int $donor_id Customer id.
706
		 */
707
		do_action( 'give_donor_pre_increase_value', $value, $this->id );
708
709
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
710
			$this->purchase_value = $new_value;
0 ignored issues
show
Documentation Bug introduced by
The property $purchase_value was declared of type integer, but $new_value is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
711
		}
712
713
		/**
714
		 * Fires after increasing donor lifetime value.
715
		 *
716
		 * @since 1.0
717
		 *
718
		 * @param float $purchase_value Donor's lifetime value.
719
		 * @param float $value The value increased by.
720
		 * @param int $donor_id Donor id.
721
		 */
722
		do_action( 'give_donor_post_increase_value', $this->purchase_value, $value, $this->id );
723
724
		return $this->purchase_value;
725
	}
726
727
	/**
728
	 * Decrease a donor's lifetime value.
729
	 *
730
	 * @since  1.0
731
	 * @access public
732
	 *
733
	 * @param  float $value The value to decrease by.
734
	 *
735
	 * @return mixed        If successful, the new value, otherwise false.
736
	 */
737
	public function decrease_value( $value = 0.00 ) {
738
739
		$new_value = floatval( $this->purchase_value ) - $value;
740
741
		if ( $new_value < 0 ) {
742
			$new_value = 0.00;
743
		}
744
745
		/**
746
		 * Fires before decreasing donor lifetime value.
747
		 *
748
		 * @since 1.0
749
		 *
750
		 * @param float $value The value to decrease by.
751
		 * @param int $donor_id Donor id.
752
		 */
753
		do_action( 'give_donor_pre_decrease_value', $value, $this->id );
754
755
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
756
			$this->purchase_value = $new_value;
0 ignored issues
show
Documentation Bug introduced by
The property $purchase_value was declared of type integer, but $new_value is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
757
		}
758
759
		/**
760
		 * Fires after decreasing donor lifetime value.
761
		 *
762
		 * @since 1.0
763
		 *
764
		 * @param float $purchase_value Donor lifetime value.
765
		 * @param float $value The value decreased by.
766
		 * @param int $donor_id Donor id.
767
		 */
768
		do_action( 'give_donor_post_decrease_value', $this->purchase_value, $value, $this->id );
769
770
		return $this->purchase_value;
771
	}
772
773
	/**
774
	 * Decrease/Increase a donor's lifetime value.
775
	 *
776
	 * This function will update donation stat on basis of current amount and new amount donation difference.
777
	 * Difference value can positive or negative. Negative value will decrease user donation stat while positive value increase donation stat.
778
	 *
779
	 * @since  1.0
780
	 * @access public
781
	 *
782
	 * @param  float $curr_amount Current Donation amount.
783
	 * @param  float $new_amount New (changed) Donation amount.
784
	 *
785
	 * @return mixed              If successful, the new donation stat value, otherwise false.
786
	 */
787
	public function update_donation_value( $curr_amount, $new_amount ) {
788
		/**
789
		 * Payment total difference value can be:
790
		 *  zero   (in case amount not change)
791
		 *  or -ve (in case amount decrease)
792
		 *  or +ve (in case amount increase)
793
		 */
794
		$payment_total_diff = $new_amount - $curr_amount;
795
796
		// We do not need to update donation stat if donation did not change.
797
		if ( ! $payment_total_diff ) {
798
			return false;
799
		}
800
801
		if ( $payment_total_diff > 0 ) {
802
			$this->increase_value( $payment_total_diff );
803
		} else {
804
			// Pass payment total difference as +ve value to decrease amount from user lifetime stat.
805
			$this->decrease_value( - $payment_total_diff );
806
		}
807
808
		return $this->purchase_value;
809
	}
810
811
	/**
812
	 * Get the parsed notes for a donor as an array.
813
	 *
814
	 * @since  1.0
815
	 * @access public
816
	 *
817
	 * @param  int $length The number of notes to get.
818
	 * @param  int $paged What note to start at.
819
	 *
820
	 * @return array       The notes requested.
821
	 */
822
	public function get_notes( $length = 20, $paged = 1 ) {
823
824
		$length = is_numeric( $length ) ? $length : 20;
825
		$offset = is_numeric( $paged ) && $paged != 1 ? ( ( absint( $paged ) - 1 ) * $length ) : 0;
826
827
		$all_notes   = $this->get_raw_notes();
828
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
829
830
		$desired_notes = array_slice( $notes_array, $offset, $length );
831
832
		return $desired_notes;
833
834
	}
835
836
	/**
837
	 * Get the total number of notes we have after parsing.
838
	 *
839
	 * @since  1.0
840
	 * @access public
841
	 *
842
	 * @return int The number of notes for the donor.
843
	 */
844
	public function get_notes_count() {
845
846
		$all_notes   = $this->get_raw_notes();
847
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
848
849
		return count( $notes_array );
850
851
	}
852
853
	/**
854
	 * Get the total donation amount.
855
	 *
856
	 * @since 1.8.17
857
	 *
858
	 * @param array $args Pass any additional data.
859
	 *
860
	 * @return string|float
861
	 */
862
	public function get_total_donation_amount( $args = array() ) {
863
864
		/**
865
		 * Filter total donation amount.
866
		 *
867
		 * @since 1.8.17
868
		 *
869
		 * @param string|float $purchase_value Donor Purchase value.
870
		 * @param integer      $donor_id       Donor ID.
871
		 * @param array        $args           Pass additional data.
872
		 */
873
		return apply_filters( 'give_get_total_donation_amount', $this->purchase_value, $this->id, $args );
874
	}
875
876
	/**
877
	 * Add a note for the donor.
878
	 *
879
	 * @since  1.0
880
	 * @access public
881
	 *
882
	 * @param  string $note The note to add. Default is empty.
883
	 *
884
	 * @return string|boolean The new note if added successfully, false otherwise.
885
	 */
886
	public function add_note( $note = '' ) {
887
888
		$note = trim( $note );
889
		if ( empty( $note ) ) {
890
			return false;
891
		}
892
893
		$notes = $this->get_raw_notes();
894
895
		if ( empty( $notes ) ) {
896
			$notes = '';
897
		}
898
899
		$note_string = date_i18n( 'F j, Y H:i:s', current_time( 'timestamp' ) ) . ' - ' . $note;
900
		$new_note    = apply_filters( 'give_customer_add_note_string', $note_string );
901
		$notes       .= "\n\n" . $new_note;
902
903
		/**
904
		 * Fires before donor note is added.
905
		 *
906
		 * @since 1.0
907
		 *
908
		 * @param string $new_note New note to add.
909
		 * @param int $donor_id Donor id.
910
		 */
911
		do_action( 'give_donor_pre_add_note', $new_note, $this->id );
912
913
		$updated = $this->update( array( 'notes' => $notes ) );
914
915
		if ( $updated ) {
916
			$this->notes = $this->get_notes();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_notes() of type array is incompatible with the declared type string of property $notes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
917
		}
918
919
		/**
920
		 * Fires after donor note added.
921
		 *
922
		 * @since 1.0
923
		 *
924
		 * @param array $donor_notes Donor notes.
925
		 * @param string $new_note New note added.
926
		 * @param int $donor_id Donor id.
927
		 */
928
		do_action( 'give_donor_post_add_note', $this->notes, $new_note, $this->id );
929
930
		// Return the formatted note, so we can test, as well as update any displays
931
		return $new_note;
932
933
	}
934
935
	/**
936
	 * Get the notes column for the donor
937
	 *
938
	 * @since  1.0
939
	 * @access private
940
	 *
941
	 * @return string The Notes for the donor, non-parsed.
942
	 */
943
	private function get_raw_notes() {
944
945
		$all_notes = $this->db->get_column( 'notes', $this->id );
946
947
		return $all_notes;
948
949
	}
950
951
	/**
952
	 * Retrieve a meta field for a donor.
953
	 *
954
	 * @since  1.6
955
	 * @access public
956
	 *
957
	 * @param  string $meta_key The meta key to retrieve. Default is empty.
958
	 * @param  bool $single Whether to return a single value. Default is true.
959
	 *
960
	 * @return mixed            Will be an array if $single is false. Will be value of meta data field if $single is true.
961
	 */
962
	public function get_meta( $meta_key = '', $single = true ) {
963
		return Give()->donor_meta->get_meta( $this->id, $meta_key, $single );
964
	}
965
966
	/**
967
	 * Add a meta data field to a donor.
968
	 *
969
	 * @since  1.6
970
	 * @access public
971
	 *
972
	 * @param  string $meta_key Metadata name. Default is empty.
973
	 * @param  mixed $meta_value Metadata value.
974
	 * @param  bool $unique Optional. Whether the same key should not be added. Default is false.
975
	 *
976
	 * @return bool               False for failure. True for success.
977
	 */
978
	public function add_meta( $meta_key = '', $meta_value, $unique = false ) {
979
		return Give()->donor_meta->add_meta( $this->id, $meta_key, $meta_value, $unique );
980
	}
981
982
	/**
983
	 * Update a meta field based on donor ID.
984
	 *
985
	 * @since  1.6
986
	 * @access public
987
	 *
988
	 * @param  string $meta_key Metadata key. Default is empty.
989
	 * @param  mixed $meta_value Metadata value.
990
	 * @param  mixed $prev_value Optional. Previous value to check before removing. Default is empty.
991
	 *
992
	 * @return bool               False on failure, true if success.
993
	 */
994
	public function update_meta( $meta_key = '', $meta_value, $prev_value = '' ) {
995
		return Give()->donor_meta->update_meta( $this->id, $meta_key, $meta_value, $prev_value );
996
	}
997
998
	/**
999
	 * Remove metadata matching criteria from a donor.
1000
	 *
1001
	 * @since  1.6
1002
	 * @access public
1003
	 *
1004
	 * @param  string $meta_key Metadata name. Default is empty.
1005
	 * @param  mixed $meta_value Optional. Metadata value. Default is empty.
1006
	 *
1007
	 * @return bool               False for failure. True for success.
1008
	 */
1009
	public function delete_meta( $meta_key = '', $meta_value = '' ) {
1010
		return Give()->donor_meta->delete_meta( $this->id, $meta_key, $meta_value );
1011
	}
1012
1013
	/**
1014
	 * Sanitize the data for update/create
1015
	 *
1016
	 * @since  1.0
1017
	 * @access private
1018
	 *
1019
	 * @param  array $data The data to sanitize.
1020
	 *
1021
	 * @return array       The sanitized data, based off column defaults.
1022
	 */
1023
	private function sanitize_columns( $data ) {
1024
1025
		$columns        = $this->db->get_columns();
1026
		$default_values = $this->db->get_column_defaults();
1027
1028
		foreach ( $columns as $key => $type ) {
1029
1030
			// Only sanitize data that we were provided
1031
			if ( ! array_key_exists( $key, $data ) ) {
1032
				continue;
1033
			}
1034
1035
			switch ( $type ) {
1036
1037
				case '%s':
1038
					if ( 'email' == $key ) {
1039
						$data[ $key ] = sanitize_email( $data[ $key ] );
1040
					} elseif ( 'notes' == $key ) {
1041
						$data[ $key ] = strip_tags( $data[ $key ] );
1042
					} else {
1043
						$data[ $key ] = sanitize_text_field( $data[ $key ] );
1044
					}
1045
					break;
1046
1047
				case '%d':
1048
					if ( ! is_numeric( $data[ $key ] ) || (int) $data[ $key ] !== absint( $data[ $key ] ) ) {
1049
						$data[ $key ] = $default_values[ $key ];
1050
					} else {
1051
						$data[ $key ] = absint( $data[ $key ] );
1052
					}
1053
					break;
1054
1055
				case '%f':
1056
					// Convert what was given to a float
1057
					$value = floatval( $data[ $key ] );
1058
1059
					if ( ! is_float( $value ) ) {
1060
						$data[ $key ] = $default_values[ $key ];
1061
					} else {
1062
						$data[ $key ] = $value;
1063
					}
1064
					break;
1065
1066
				default:
1067
					$data[ $key ] = sanitize_text_field( $data[ $key ] );
1068
					break;
1069
1070
			}
1071
		}
1072
1073
		return $data;
1074
	}
1075
1076
	/**
1077
	 * Attach an email to the donor
1078
	 *
1079
	 * @since  1.7
1080
	 * @access public
1081
	 *
1082
	 * @param  string $email The email address to attach to the donor
1083
	 * @param  bool $primary Allows setting the email added as the primary
1084
	 *
1085
	 * @return bool            If the email was added successfully
1086
	 */
1087
	public function add_email( $email = '', $primary = false ) {
1088
		if ( ! is_email( $email ) ) {
1089
			return false;
1090
		}
1091
		$existing = new Give_Donor( $email );
0 ignored issues
show
Documentation introduced by
$email is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1092
1093
		if ( $existing->id > 0 ) {
1094
			// Email address already belongs to another donor
1095
			return false;
1096
		}
1097
1098
		if ( email_exists( $email ) ) {
1099
			$user = get_user_by( 'email', $email );
1100
			if ( $user->ID != $this->user_id ) {
1101
				return false;
1102
			}
1103
		}
1104
1105
		do_action( 'give_donor_pre_add_email', $email, $this->id, $this );
1106
1107
		// Add is used to ensure duplicate emails are not added
1108
		$ret = (bool) $this->add_meta( 'additional_email', $email );
1109
1110
		do_action( 'give_donor_post_add_email', $email, $this->id, $this );
1111
1112
		if ( $ret && true === $primary ) {
1113
			$this->set_primary_email( $email );
1114
		}
1115
1116
		return $ret;
1117
	}
1118
1119
	/**
1120
	 * Remove an email from the donor.
1121
	 *
1122
	 * @since  1.7
1123
	 * @access public
1124
	 *
1125
	 * @param  string $email The email address to remove from the donor.
1126
	 *
1127
	 * @return bool          If the email was removed successfully.
1128
	 */
1129
	public function remove_email( $email = '' ) {
1130
		if ( ! is_email( $email ) ) {
1131
			return false;
1132
		}
1133
1134
		do_action( 'give_donor_pre_remove_email', $email, $this->id, $this );
1135
1136
		$ret = (bool) $this->delete_meta( 'additional_email', $email );
1137
1138
		do_action( 'give_donor_post_remove_email', $email, $this->id, $this );
1139
1140
		return $ret;
1141
	}
1142
1143
	/**
1144
	 * Set an email address as the donor's primary email.
1145
	 *
1146
	 * This will move the donor's previous primary email to an additional email.
1147
	 *
1148
	 * @since  1.7
1149
	 * @access public
1150
	 *
1151
	 * @param  string $new_primary_email The email address to remove from the donor.
1152
	 *
1153
	 * @return bool                      If the email was set as primary successfully.
1154
	 */
1155
	public function set_primary_email( $new_primary_email = '' ) {
1156
		if ( ! is_email( $new_primary_email ) ) {
1157
			return false;
1158
		}
1159
1160
		do_action( 'give_donor_pre_set_primary_email', $new_primary_email, $this->id, $this );
1161
1162
		$existing = new Give_Donor( $new_primary_email );
0 ignored issues
show
Documentation introduced by
$new_primary_email is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1163
1164
		if ( $existing->id > 0 && (int) $existing->id !== (int) $this->id ) {
1165
			// This email belongs to another donor.
1166
			return false;
1167
		}
1168
1169
		$old_email = $this->email;
1170
1171
		// Update donor record with new email.
1172
		$update = $this->update( array( 'email' => $new_primary_email ) );
1173
1174
		// Remove new primary from list of additional emails.
1175
		$remove = $this->remove_email( $new_primary_email );
1176
1177
		// Add old email to additional emails list.
1178
		$add = $this->add_email( $old_email );
1179
1180
		$ret = $update && $remove && $add;
1181
1182
		if ( $ret ) {
1183
			$this->email = $new_primary_email;
1184
		}
1185
1186
		do_action( 'give_donor_post_set_primary_email', $new_primary_email, $this->id, $this );
1187
1188
		return $ret;
1189
	}
1190
1191
	/**
1192
	 * Check if address valid or not.
1193
	 *
1194
	 * @since  2.0
1195
	 * @access private
1196
	 *
1197
	 * @param $address
1198
	 *
1199
	 * @return bool
1200
	 */
1201
	private function is_valid_address( $address ) {
1202
		$is_valid_address = true;
1203
		
1204
		// Address ready to process even if only one value set.
1205
		foreach ( $address as $address_type => $value ) {
1206
			// @todo: Handle state feield validation on basis of country.
1207
			if( in_array( $address_type, array( 'line2', 'state' )) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
1208
				continue;
1209
			}
1210
1211
			if ( empty( $value ) ) {
1212
				$is_valid_address = false;
1213
				break;
1214
			}
1215
		}
1216
1217
		return $is_valid_address;
1218
	}
1219
1220
	/**
1221
	 * Add donor address
1222
	 *
1223
	 * @since  2.0
1224
	 * @access public
1225
	 *
1226
	 * @param string $address_type
1227
	 * @param array  $address {
1228
	 *
1229
	 * @type string  $address2
1230
	 * @type string city
1231
	 * @type string zip
1232
	 * @type string state
1233
	 * @type string country
1234
	 * }
1235
	 *
1236
	 * @return bool
1237
	 */
1238
	public function add_address( $address_type, $address ) {
1239
		// Bailout.
1240
		if ( empty( $address_type ) || ! $this->is_valid_address( $address ) || ! $this->id ) {
1241
			return false;
1242
		}
1243
1244
		// Check if multiple address exist or not and set params.
1245
		$multi_address_id = null;
1246
		if( $is_multi_address = ( false !== strpos( $address_type, '[]' ) ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1247
			$address_type  = $is_multi_address ?
1248
				str_replace( '[]', '', $address_type ) :
1249
				$address_type;
1250
		} elseif ( $is_multi_address = ( false !== strpos( $address_type, '_' ) ) ){
1251
			$multi_address_id =  $is_multi_address ?
0 ignored issues
show
introduced by
Expected 1 space after "="; 2 found
Loading history...
1252
				array_pop( explode( '_', $address_type ) ) :
1253
				$address_type;
1254
1255
			$address_type  = $is_multi_address ?
1256
				array_shift( explode( '_', $address_type ) ) :
1257
				$address_type;
1258
		}
1259
1260
		// Bailout: do not save duplicate orders
1261
		if( $this->is_address_exist( $address_type, $address ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1262
			return false;
1263
		}
1264
1265
		// Set default address.
1266
		$address = wp_parse_args(
1267
			$address,
1268
			array(
1269
				'line1' => '',
1270
				'line2' => '',
1271
				'city'     => '',
1272
				'state'    => '',
1273
				'country'  => '',
1274
				'zip'      => '',
1275
			)
1276
		);
1277
1278
		// Set meta key prefix.
1279
		global $wpdb;
1280
		$meta_key_prefix = "_give_donor_address_{$address_type}_{address_name}";
1281
		$meta_type = Give()->donor_meta->meta_type;
1282
1283
		if ( $is_multi_address ) {
1284
			if ( is_null( $multi_address_id ) ) {
1285
				// Get latest address key to set multi address id.
1286
				$multi_address_id = $wpdb->get_var(
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...
1287
					$wpdb->prepare(
1288
						"
1289
						SELECT meta_key FROM {$wpdb->donormeta}
1290
						WHERE meta_key
1291
						LIKE '%%%s%%'
1292
						AND {$meta_type}_id=%d
1293
						ORDER BY meta_id DESC
1294
						LIMIT 1
1295
						",
1296
						"_give_donor_address_{$address_type}_line1",
1297
						$this->id
1298
					)
1299
				);
1300
1301
				if( ! empty( $multi_address_id ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1302
					$multi_address_id = absint( substr( strrchr( $multi_address_id, '_' ), 1 ) );
1303
					$multi_address_id++;
1304
				} else{
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1305
					$multi_address_id = 0;
1306
				}
1307
			}
1308
1309
			$meta_key_prefix = "_give_donor_address_{$address_type}_{address_name}_{$multi_address_id}";
1310
		}
1311
1312
		// Save donor address.
1313
		foreach ( $address as $type => $value ) {
1314
			$meta_key = str_replace( '{address_name}', $type, $meta_key_prefix );
1315
			Give()->donor_meta->update_meta( $this->id, $meta_key, $value );
1316
		}
1317
1318
		$this->setup_address();
1319
1320
		return true;
1321
	}
1322
1323
	/**
1324
	 * Remove donor address
1325
	 *
1326
	 * @since  2.0
1327
	 * @access public
1328
	 * @global wpdb  $wpdb
1329
	 *
1330
	 * @param string $address_id
1331
	 *
1332
	 * @return bool
1333
	 */
1334
	public function remove_address( $address_id ) {
1335
		global $wpdb;
1336
1337
		// Get address type.
1338
		$is_multi_address = false !== strpos( $address_id, '_' ) ? true : false;
1339
1340
		$address_type = false !== strpos( $address_id, '_' ) ?
1341
			array_shift( explode( '_', $address_id ) ) :
1342
			$address_id;
1343
1344
		$address_count = false !== strpos( $address_id, '_' ) ?
1345
			array_pop( explode( '_', $address_id ) ) :
1346
			null;
1347
1348
		// Set meta key prefix.
1349
		$meta_key_prefix = "_give_donor_address_{$address_type}_%";
1350
		if ( $is_multi_address && is_numeric( $address_count ) ) {
1351
			$meta_key_prefix .= "_{$address_count}";
1352
		}
1353
1354
		$meta_type = Give()->donor_meta->meta_type;
1355
1356
		// Process query.
1357
		$row_affected = $wpdb->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...
1358
			$wpdb->prepare(
1359
				"
1360
				DELETE FROM {$wpdb->donormeta}
1361
				WHERE meta_key
1362
				LIKE '%s'
1363
				AND {$meta_type}_id=%d
1364
				",
1365
				$meta_key_prefix,
1366
				$this->id
1367
			)
1368
		);
1369
1370
		$this->setup_address();
1371
1372
		return (bool) $row_affected;
1373
	}
1374
1375
	/**
1376
	 * Update donor address
1377
	 *
1378
	 * @since  2.0
1379
	 * @access public
1380
	 * @global wpdb  $wpdb
1381
	 *
1382
	 * @param string $address_id
1383
	 * @param array  $address
1384
	 *
1385
	 * @return bool
1386
	 */
1387
	public function update_address( $address_id, $address ) {
1388
		global $wpdb;
1389
1390
		// Get address type.
1391
		$is_multi_address = false !== strpos( $address_id, '_' ) ? true : false;
1392
1393
		$address_type = false !== strpos( $address_id, '_' ) ?
1394
			array_shift( explode( '_', $address_id ) ) :
1395
			$address_id;
1396
1397
		$address_count = false !== strpos( $address_id, '_' ) ?
1398
			array_pop( explode( '_', $address_id ) ) :
1399
			null;
1400
1401
		// Set meta key prefix.
1402
		$meta_key_prefix = "_give_donor_address_{$address_type}_%";
1403
		if ( $is_multi_address && is_numeric( $address_count ) ) {
1404
			$meta_key_prefix .= "_{$address_count}";
1405
		}
1406
1407
		$meta_type = Give()->donor_meta->meta_type;
1408
1409
		// Process query.
1410
		$row_affected = $wpdb->get_results(
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...
1411
			$wpdb->prepare(
1412
				"
1413
				SELECT meta_key FROM {$wpdb->donormeta}
1414
				WHERE meta_key
1415
				LIKE '%s'
1416
				AND {$meta_type}_id=%d
1417
				",
1418
				$meta_key_prefix,
1419
				$this->id
1420
			)
1421
		);
1422
		
1423
		// Return result.
1424
		if( ! count( $row_affected ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1425
			return false;
1426
		}
1427
1428
		// Update address.
1429
		if( ! $this->add_address( $address_id, $address ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1430
			return false;
1431
		}
1432
1433
		$this->setup_address();
1434
1435
		return true;
1436
	}
1437
1438
1439
	/**
1440
	 * Check if donor already has current address
1441
	 *
1442
	 * @since 2.0
1443
	 * @access public
1444
	 *
1445
	 * @param string $current_address_type
1446
	 * @param array $current_address
1447
	 *
1448
	 * @return bool|null
1449
	 */
1450
	public function is_address_exist( $current_address_type, $current_address ) {
1451
		$status = false;
1452
1453
		// Bailout.
1454
		if( empty( $current_address_type ) || empty( $current_address ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1455
			return null;
1456
		}
1457
1458
		// Bailout.
1459
		if( empty( $this->address ) || empty( $this->address[ $current_address_type ] ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1460
			return $status;
1461
		}
1462
1463
		// Get address.
1464
		$address = $this->address[ $current_address_type ];
1465
1466
		switch ( true ){
1467
1468
			// Single address.
1469
			case is_string( end( $address ) ) :
1470
				$status = $this->is_address_match( $current_address, $address );
1471
				break;
1472
1473
			// Multi address.
1474
			case is_array( end( $address ) ):
1475
				// Compare address.
1476
				foreach ( $address as $saved_address ) {
1477
					if( empty( $saved_address ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1478
						continue;
1479
					}
1480
1481
					// Exit loop immediately if address exist.
1482
					if( $status = $this->is_address_match( $current_address, $saved_address ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1483
						break;
1484
					}
1485
				}
1486
				break;
1487
		}
1488
1489
		return $status;
1490
	}
1491
1492
	/**
1493
	 * Compare address.
1494
	 *
1495
	 * @since  2.0
1496
	 * @access private
1497
	 *
1498
	 * @param array $address_1
1499
	 * @param array $address_2
1500
	 *
1501
	 * @return bool
1502
	 */
1503
	private function is_address_match( $address_1, $address_2 ) {
1504
		$result = array_diff( $address_1, $address_2 );
1505
1506
		return empty( $result );
1507
	}
1508
1509
	/**
1510
	 * Split donor name into first name and last name
1511
	 *
1512
	 * @param   int     $id     Donor ID
1513
	 * @since   2.0
1514
	 * @return  object
1515
	 */
1516
	public function split_donor_name( $id ) {
1517
		$first_name = $last_name  = '';
1518
		$donor      = new Give_Donor( $id );
0 ignored issues
show
Documentation introduced by
$id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1519
1520
		$split_donor_name = explode( ' ', $donor->name, 2 );
1521
1522
		// Check for existence of first name after split of donor name.
1523
		if( is_array( $split_donor_name ) && ! empty( $split_donor_name[0] ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1524
			$first_name = $split_donor_name[0];
1525
		}
1526
1527
		// Check for existence of last name after split of donor name.
1528
		if( is_array( $split_donor_name ) && ! empty( $split_donor_name[1] ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1529
			$last_name = $split_donor_name[1];
1530
		}
1531
		return (object) array( 'first_name' => $first_name, 'last_name' => $last_name );
1532
	}
1533
1534
	/**
1535
	 * Retrieves first name of donor with backward compatibility
1536
	 *
1537
	 * @since   2.0
1538
	 * @return  string
1539
	 */
1540
	public function get_first_name() {
1541
		$first_name = $this->get_meta( '_give_donor_first_name');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
1542
		if( ! $first_name ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1543
			$first_name = $this->split_donor_name( $this->id )->first_name;
1544
		}
1545
1546
		return $first_name;
1547
	}
1548
1549
	/**
1550
	 * Retrieves last name of donor with backward compatibility
1551
	 *
1552
	 * @since   2.0
1553
	 * @return  string
1554
	 */
1555
	public function get_last_name() {
1556
		$first_name = $this->get_meta( '_give_donor_first_name');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
1557
		$last_name = $this->get_meta( '_give_donor_last_name');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
1558
1559
		// This condition will prevent unnecessary splitting of donor name to fetch last name.
1560
		if( ! $first_name && ! $last_name ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
1561
			$last_name = $this->split_donor_name( $this->id )->last_name;
1562
		}
1563
1564
		return ( $last_name ) ? $last_name : '';
1565
	}
1566
1567
}
1568