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

Give_Payment   D

Complexity

Total Complexity 212

Size/Duplication

Total Lines 2119
Duplicated Lines 6.98 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 79.31%

Importance

Changes 0
Metric Value
dl 148
loc 2119
rs 4.4102
c 0
b 0
f 0
ccs 625
cts 788
cp 0.7931
wmc 212
lcom 1
cbo 4

60 Methods

Rating   Name   Duplication   Size   Complexity  
A setup_ip() 0 5 1
A __construct() 0 8 2
A __get() 0 14 2
A __set() 0 15 4
A __isset() 0 7 2
B setup_payment() 0 91 6
A update_payment_setup() 0 3 1
F insert_payment() 0 112 20
D save() 0 248 42
C add_donation() 9 80 12
B remove_donation() 0 30 4
A add_note() 0 8 2
A increase_subtotal() 0 6 1
A decrease_subtotal() 0 10 2
A recalculate_total() 0 3 1
C update_status() 0 76 12
A refund() 0 7 1
C get_meta() 6 36 8
B update_meta() 0 30 6
B process_refund() 43 43 4
A process_failure() 0 3 1
B process_pending() 28 28 4
B process_cancelled() 28 28 4
B process_revoked() 28 28 4
B maybe_alter_stats() 0 24 5
A delete_sales_logs() 0 15 1
A setup_completed_date() 0 11 4
A setup_mode() 0 3 1
A setup_import() 0 3 1
A setup_total() 0 14 4
A setup_subtotal() 0 5 1
A setup_currency() 0 5 2
A setup_gateway() 0 5 1
A setup_transaction_id() 0 10 2
A setup_donor_id() 0 5 1
A setup_user_id() 0 5 1
A setup_email() 0 9 3
C setup_user_info() 6 64 13
A setup_address() 0 13 2
A setup_form_title() 0 6 1
A setup_form_id() 0 6 1
A setup_price_id() 0 5 1
A setup_payment_key() 0 5 1
A setup_payment_number() 0 16 3
A array_convert() 0 3 1
A is_completed() 0 3 2
A get_completed_date() 0 3 1
A get_subtotal() 0 3 1
A get_currency() 0 3 1
A get_gateway() 0 3 1
A get_transaction_id() 0 3 1
A get_ip() 0 3 1
A get_donor_id() 0 3 1
A get_user_id() 0 3 1
A get_email() 0 3 1
A get_user_info() 0 3 1
A get_address() 0 3 1
A get_key() 0 3 1
A get_form_id() 0 3 1
A get_number() 0 3 1

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_Payment 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_Payment, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Payments
4
 *
5
 * @package     Give
6
 * @subpackage  Classes/Give_Payment
7
 * @copyright   Copyright (c) 2016, WordImpress
8
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
9
 * @since       1.5
10
 */
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * Give_Payment Class
19
 *
20
 * This class is for working with payments in Give.
21
 *
22
 * @property int        $ID
23
 * @property bool       $new
24
 * @property string     $number
25
 * @property string     $mode
26
 * @property string     $import
27
 * @property string     $key
28
 * @property string     $form_title
29
 * @property string|int $form_id
30
 * @property string|int $price_id
31
 * @property string|int $total
32
 * @property string|int $subtotal
33
 * @property string     $post_status
34
 * @property string     $date
35
 * @property string     $postdate
36
 * @property string     $status
37
 * @property string     $email
38
 * @property array      $payment_meta
39
 * @property string     $customer_id
40
 * @property string     $completed_date
41
 * @property string     $currency
42
 * @property string     $ip
43
 * @property array      $user_info
44
 * @property string     $gateway
45
 * @property string     $user_id
46
 * @property string     $first_name
47
 * @property string     $last_name
48
 * @property string     $parent_payment
49
 * @property string     $transaction_id
50
 * @property string     $old_status
51
 *
52
 * @since 1.5
53
 */
54
final class Give_Payment {
55
56
	/**
57
	 * The Payment ID.
58
	 *
59
	 * @since  1.5
60
	 *
61
	 * @var    int
62
	 */
63
	public $ID = 0;
64
65
	/**
66
	 * Protected non-read $_ID.
67
	 *
68
	 * @var int
69
	 */
70
	protected $_ID = 0;
71
72
	/**
73
	 * Identify if the payment is a new one or existing.
74
	 *
75
	 * @since  1.5
76
	 * @access protected
77
	 *
78
	 * @var    boolean
79
	 */
80
	protected $new = false;
81
82
	/**
83
	 * The Payment number (for use with sequential payments).
84
	 *
85
	 * @since  1.5
86
	 * @access protected
87
	 *
88
	 * @var    string
89
	 */
90
	protected $number = '';
91
92
	/**
93
	 * The Gateway mode the payment was made in.
94
	 *
95
	 * @since  1.5
96
	 * @access protected
97
	 *
98
	 * @var    string
99
	 */
100
	protected $mode = 'live';
101
102
	/**
103
	 * Is donations is Import or not.
104
	 *
105
	 * @since  1.8.13
106
	 * @access protected
107
	 *
108
	 * @var    bool
109
	 */
110
	protected $import = false;
111
112
	/**
113
	 * The unique donation payment key.
114
	 *
115
	 * @since  1.5
116
	 * @access protected
117
	 *
118
	 * @var    string
119
	 */
120
	protected $key = '';
121
122
	/**
123
	 * The Donation Form Title
124
	 *
125
	 * @since  1.5
126
	 * @access protected
127
	 *
128
	 * @var    string
129
	 */
130
	protected $form_title = 0;
131
132
	/**
133
	 * The Donation Form ID
134
	 *
135
	 * @since  1.5
136
	 * @access protected
137
	 *
138
	 * @var    string
139
	 */
140
	protected $form_id = 0;
141
142
	/**
143
	 * The Donation Form Price ID
144
	 *
145
	 * @since  1.5
146
	 * @access protected
147
	 *
148
	 * @var    string|int
149
	 */
150
	protected $price_id = 0;
151
152
	/**
153
	 * The total amount of the donation payment.
154
	 *
155
	 * @since  1.5
156
	 * @access protected
157
	 *
158
	 * @var    float
159
	 */
160
	protected $total = 0.00;
161
162
	/**
163
	 * The Subtotal fo the payment.
164
	 *
165
	 * @since  1.5
166
	 * @access protected
167
	 *
168
	 * @var    float
169
	 */
170
	protected $subtotal = 0;
171
172
	/**
173
	 * The date the payment was created
174
	 *
175
	 * @since  1.5
176
	 * @access protected
177
	 *
178
	 * @var    string
179
	 */
180
	protected $date = '';
181
182
	/**
183
	 * The date the payment post was created.
184
	 *
185
	 * @var string
186
	 */
187
	protected $post_date = '';
188
189
	/**
190
	 * The date the payment was marked as 'complete'.
191
	 *
192
	 * @since  1.5
193
	 * @access protected
194
	 *
195
	 * @var    string
196
	 */
197
	protected $completed_date = '';
198
199
	/**
200
	 * The status of the donation payment.
201
	 *
202
	 * @since  1.5
203
	 * @access protected
204
	 *
205
	 * @var    string
206
	 */
207
	protected $status = 'pending';
208
209
	/**
210
	 * @var string
211
	 */
212
	protected $post_status = 'pending'; // Same as $status but here for backwards compat
213
214
	/**
215
	 * When updating, the old status prior to the change
216
	 *
217
	 * @since  1.5
218
	 * @access protected
219
	 *
220
	 * @var    string
221
	 */
222
	protected $old_status = '';
223
224
	/**
225
	 * The display name of the current payment status.
226
	 *
227
	 * @since  1.5
228
	 * @access protected
229
	 *
230
	 * @var    string
231
	 */
232
	protected $status_nicename = '';
233
234
	/**
235
	 * The donor ID that made the payment.
236
	 *
237
	 * @since  1.5
238
	 * @access protected
239
	 *
240
	 * @var    integer
241
	 */
242
	protected $customer_id = null;
243
244
	/**
245
	 * The Donor ID (if logged in) that made the payment
246
	 *
247
	 * @since  1.8.13
248
	 * @access protected
249
	 *
250
	 * @var    integer
251
	 */
252
	protected $donor_id = 0;
253
254
	/**
255
	 * The User ID (if logged in) that made the payment
256
	 *
257
	 * @since  1.5
258
	 * @access protected
259
	 *
260
	 * @var    integer
261
	 */
262
	protected $user_id = 0;
263
264
	/**
265
	 * The first name of the payee
266
	 *
267
	 * @since  1.5
268
	 * @access protected
269
	 *
270
	 * @var    string
271
	 */
272
	protected $first_name = '';
273
274
	/**
275
	 * The last name of the payee
276
	 *
277
	 * @since  1.5
278
	 * @access protected
279
	 *
280
	 * @var    string
281
	 */
282
	protected $last_name = '';
283
284
	/**
285
	 * The email used for the payment
286
	 *
287
	 * @since  1.5
288
	 * @access protected
289
	 *
290
	 * @var    string
291
	 */
292
	protected $email = '';
293
294
	/**
295
	 * Legacy (not to be accessed) array of user information
296
	 *
297
	 * @since  1.5
298 52
	 * @access private
299
	 *
300 52
	 * @var    array
301 52
	 */
302
	private $user_info = array();
303
304 52
	/**
305 52
	 * Legacy (not to be accessed) payment meta array
306
	 *
307
	 * @since  1.5
308
	 * @access private
309
	 *
310
	 * @var    array
311
	 */
312
	private $payment_meta = array();
313
314
	/**
315
	 * The physical address used for the payment if provided
316 52
	 *
317
	 * @since  1.5
318 52
	 * @access protected
319
	 *
320 52
	 * @var    array
321
	 */
322 52
	protected $address = array();
323
324 52
	/**
325
	 * The transaction ID returned by the gateway
326
	 *
327
	 * @since  1.5
328 52
	 * @access protected
329
	 *
330
	 * @var    string
331
	 */
332
	protected $transaction_id = '';
333
334
	/**
335
	 * IP Address payment was made from
336
	 *
337
	 * @since  1.5
338
	 * @access protected
339
	 *
340
	 * @var    string
341 52
	 */
342 52
	protected $ip = '';
343
344 52
	/**
345 52
	 * The gateway used to process the payment
346 52
	 *
347
	 * @since  1.5
348 52
	 * @access protected
349 52
	 *
350 52
	 * @var    string
351
	 */
352 52
	protected $gateway = '';
353 52
354 52
	/**
355 52
	 * The the payment was made with
356
	 *
357
	 * @since  1.5
358
	 * @access protected
359
	 *
360
	 * @var    string
361
	 */
362
	protected $currency = '';
363
364
	/**
365
	 * Array of items that have changed since the last save() was run.
366 42
	 * This is for internal use, to allow fewer update_payment_meta calls to be run.
367 42
	 *
368 42
	 * @since  1.5
369
	 * @access private
370
	 *
371
	 * @var    array
372
	 */
373
	private $pending;
374
375
	/**
376
	 * The parent payment (if applicable)
377
	 *
378
	 * @since  1.5
379
	 * @access protected
380
	 *
381
	 * @var    integer
382
	 */
383 52
	protected $parent_payment = 0;
384 52
385
	/**
386 52
	 * Setup the Give Payments class
387
	 *
388
	 * @since  1.5
389
	 * @access public
390 52
	 *
391
	 * @param  int|bool $payment_id A given payment
392 52
	 *
393 7
	 * @return mixed void|false
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
394
	 */
395
	public function __construct( $payment_id = false ) {
396 52
397
		if ( empty( $payment_id ) ) {
398
			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...
399
		}
400
401 52
		$this->setup_payment( $payment_id );
0 ignored issues
show
Bug introduced by
It seems like $payment_id defined by parameter $payment_id on line 395 can also be of type boolean; however, Give_Payment::setup_payment() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
402
	}
403
404 52
	/**
405
	 * Magic GET function.
406
	 *
407 52
	 * @since  1.5
408
	 * @access public
409
	 *
410 52
	 * @param  string $key The property.
411
	 *
412
	 * @return mixed        The value.
413 52
	 */
414 52
	public function __get( $key ) {
415 52
416 52
		if ( method_exists( $this, 'get_' . $key ) ) {
417 52
418 52
			$value = call_user_func( array( $this, 'get_' . $key ) );
419 52
420
		} else {
421 52
422 52
			$value = $this->$key;
423
424
		}
425 52
426
		return $value;
427
	}
428 52
429 52
	/**
430 52
	 * Magic SET function
431 52
	 *
432
	 * Sets up the pending array for the save method
433
	 *
434 52
	 * @since  1.5
435 52
	 * @access public
436
	 *
437
	 * @param  string $key   The property name
438 52
	 * @param  mixed  $value The value of the property
439 52
	 */
440 52
	public function __set( $key, $value ) {
441 52
		$ignore = array( '_ID' );
442 52
443 52
		if ( 'status' === $key ) {
444 52
			$this->old_status = $this->status;
445 52
		}
446
447
		if ( ! in_array( $key, $ignore ) ) {
448 52
			$this->pending[ $key ] = $value;
449 52
		}
450 52
451 52
		if ( '_ID' !== $key ) {
452 52
			$this->$key = $value;
453
		}
454
	}
455 52
456
	/**
457 52
	 * Magic ISSET function, which allows empty checks on protected elements
458
	 *
459
	 * @since  1.5
460
	 * @access public
461
	 *
462
	 * @param  string $name The attribute to get
463
	 *
464
	 * @return boolean|null       If the item is set or not
465
	 */
466 52
	public function __isset( $name ) {
467
		if ( property_exists( $this, $name ) ) {
468
			return false === empty( $this->$name );
469 52
		} else {
470 52
			return null;
471 31
		}
472 52
	}
473
474 22
	/**
475 21
	 * Setup payment properties
476 21
	 *
477
	 * @since  1.5
478
	 * @access private
479 52
	 *
480
	 * @param  int $payment_id The payment ID
481 1
	 *
482 1
	 * @return bool            If the setup was successful or not
483 1
	 */
484 1
	private function setup_payment( $payment_id ) {
485
		$this->pending = array();
486
487 52
		if ( empty( $payment_id ) ) {
488
			return false;
489 1
		}
490 1
491
		$payment = get_post( $payment_id );
492 1
493
		if ( ! $payment || is_wp_error( $payment ) ) {
494
			return false;
495 52
		}
496 52
497 52
		if ( 'give_payment' !== $payment->post_type ) {
498 52
			return false;
499 52
		}
500 52
501 52
		/**
502 52
		 * Fires before payment setup.
503
		 *
504 52
		 * Allow extensions to perform actions before the payment is loaded.
505 52
		 *
506 52
		 * @since 1.5
507 52
		 *
508 52
		 * @param Give_Payment $this       Payment object.
509 52
		 * @param int          $payment_id The ID of the payment.
510 52
		 */
511 52
		do_action( 'give_pre_setup_payment', $this, $payment_id );
512 52
513
		// Primary Identifier.
514 52
		$this->ID = absint( $payment_id );
515 52
516 52
		// Protected ID that can never be changed.
517 52
		$this->_ID = absint( $payment_id );
518 52
519 52
		// We have a payment, get the generic payment_meta item to reduce calls to it.
520 52
		$this->payment_meta = $this->get_meta();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_meta() of type * is incompatible with the declared type array of property $payment_meta.

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...
521 52
522
		// Status and Dates.
523
		$this->date           = $payment->post_date;
524 52
		$this->post_date      = $payment->post_date;
525
		$this->completed_date = $this->setup_completed_date();
526 52
		$this->status         = $payment->post_status;
527
		$this->post_status    = $this->status;
528 52
		$this->mode           = $this->setup_mode();
529 52
		$this->import         = $this->setup_import();
530
		$this->parent_payment = $payment->post_parent;
531 52
532
		$all_payment_statuses  = give_get_payment_statuses();
533 52
		$this->status_nicename = array_key_exists( $this->status, $all_payment_statuses ) ? $all_payment_statuses[ $this->status ] : ucfirst( $this->status );
534
535
		// Currency Based.
536
		$this->total    = $this->setup_total();
537 52
		$this->subtotal = $this->setup_subtotal();
538 52
		$this->currency = $this->setup_currency();
539 52
540
		// Gateway based.
541 52
		$this->gateway        = $this->setup_gateway();
542
		$this->transaction_id = $this->setup_transaction_id();
543
544 52
		// User based.
545 52
		$this->ip          = $this->setup_ip();
546 52
		$this->customer_id = $this->setup_donor_id();
547 52
		$this->user_id     = $this->setup_user_id();
548
		$this->email       = $this->setup_email();
549 52
		$this->user_info   = $this->setup_user_info();
550
		$this->address     = $this->setup_address();
551 52
		$this->first_name  = $this->user_info['first_name'];
552
		$this->last_name   = $this->user_info['last_name'];
553 52
554 52
		// Other Identifiers.
555 52
		$this->form_title = $this->setup_form_title();
556
		$this->form_id    = $this->setup_form_id();
0 ignored issues
show
Documentation Bug introduced by
The property $form_id was declared of type string, but $this->setup_form_id() is of type integer. 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...
557 52
		$this->price_id   = $this->setup_price_id();
558 52
		$this->key        = $this->setup_payment_key();
559
		$this->number     = $this->setup_payment_number();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->setup_payment_number() can also be of type integer. However, the property $number 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...
560
561
		/**
562
		 * Fires after payment setup.
563
		 *
564
		 * Allow extensions to add items to this object via hook.
565 52
		 *
566 52
		 * @since 1.5
567 52
		 *
568
		 * @param Give_Payment $this       Payment object.
569 52
		 * @param int          $payment_id The ID of the payment.
570
		 */
571
		do_action( 'give_setup_payment', $this, $payment_id );
572
573
		return true;
574
	}
575
576
	/**
577
	 * Payment class object is storing various meta value in object parameter.
578
	 * So if user is updating payment meta but not updating payment object, then payment meta values will not reflect/changes on payment meta automatically
579
	 * and you can still access payment meta old value in any old payment object ( previously created ) which can cause to show or save wrong payment data.
580 52
	 * To prevent that user can use this function after updating any payment meta value ( in bulk or single update ).
581
	 *
582 52
	 * @since  1.6
583
	 * @access public
584
	 *
585 52
	 * @param  int $payment_id Payment ID.
586
	 *
587 52
	 * @return void
588
	 */
589 52
	public function update_payment_setup( $payment_id ) {
590
		$this->setup_payment( $payment_id );
591
	}
592 52
593
	/**
594
	 * Create the base of a payment.
595 52
	 *
596
	 * @since  1.5
597
	 * @access private
598 52
	 *
599 1
	 * @return int|bool False on failure, the payment ID on success.
600 1
	 */
601
	private function insert_payment() {
602
603 52
		// Construct the payment title.
604
		$payment_title = '';
605 52
		if ( ! empty( $this->first_name ) && ! empty( $this->last_name ) ) {
606 52
			$payment_title = $this->first_name . ' ' . $this->last_name;
607
		} elseif ( ! empty( $this->first_name ) && empty( $this->last_name ) ) {
608 52
			$payment_title = $this->first_name;
609
		} elseif ( ! empty( $this->email ) && is_email( $this->email ) ) {
610
			$payment_title = $this->email;
611
		}
612 52
613
		// Set Key.
614 52
		if ( empty( $this->key ) ) {
615
616 52
			$auth_key             = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
617 52
			$this->key            = strtolower( md5( $this->email . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'give', true ) ) );  // Unique key
618
			$this->pending['key'] = $this->key;
619 52
		}
620
621 52
		// Set IP.
622
		if ( empty( $this->ip ) ) {
623 52
624
			$this->ip            = give_get_ip();
625 52
			$this->pending['ip'] = $this->ip;
626
627
		}
628 1
629
		$payment_data = array(
630 1
			'price'        => $this->total,
631 1
			'date'         => $this->date,
632
			'user_email'   => $this->email,
633 1
			'purchase_key' => $this->key,
634 1
			'form_title'   => $this->form_title,
635 1
			'form_id'      => $this->form_id,
636
			'donor_id'     => $this->donor_id,
637 1
			'price_id'     => $this->price_id,
638 1
			'currency'     => $this->currency,
639 1
			'user_info'    => array(
640
				'id'         => $this->user_id,
641 1
				'email'      => $this->email,
642 1
				'first_name' => $this->first_name,
643 52
				'last_name'  => $this->last_name,
644
				'address'    => $this->address,
645 2
			),
646
			'status'       => $this->status,
647 2
		);
648 2
649 2
		$args = apply_filters( 'give_insert_payment_args', array(
650
			'post_title'    => $payment_title,
651
			'post_status'   => $this->status,
652 2
			'post_type'     => 'give_payment',
653 2
			'post_date'     => ! empty( $this->date ) ? $this->date : null,
654 2
			'post_date_gmt' => ! empty( $this->date ) ? get_gmt_from_date( $this->date ) : null,
655 2
			'post_parent'   => $this->parent_payment,
656
		), $payment_data );
657 2
658 2
		// Create a blank payment
659
		$payment_id = wp_insert_post( $args );
660 2
661 2
		if ( ! empty( $payment_id ) ) {
662 2
663
			$this->ID  = $payment_id;
664 2
			$this->_ID = $payment_id;
665 2
666 1
			$donor = new stdClass;
667 2
668
			/**
669 2
			 * Filter donor class after the donation is completed and before customer table is updated.
670 1
			 *
671 1
			 * @since 1.8.13
672 1
			 */
673
			$donor = apply_filters( 'give_update_donor_information', $donor, $payment_id, $payment_data, $args );
674 1
675 1
			if ( did_action( 'give_pre_process_donation' ) && is_user_logged_in() ) {
676 2
				$donor = new Give_Donor( get_current_user_id(), true );
677
678 52
				// Donor is logged in but used a different email to purchase with so assign to their donor record.
679
				if ( ! empty( $donor->id ) && $this->email !== $donor->email ) {
680 52
					$donor->add_email( $this->email );
681 52
				}
682
			}
683 52
684
			if ( empty( $donor->id ) ) {
685
				$donor = new Give_Donor( $this->email );
0 ignored issues
show
Documentation introduced by
$this->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...
686
			}
687
688
			if ( empty( $donor->id ) ) {
689
690
				$donor_data = array(
691
					'name'    => ! is_email( $payment_title ) ? $this->first_name . ' ' . $this->last_name : '',
692
					'email'   => $this->email,
693
					'user_id' => $this->user_id,
694
				);
695
696
				$donor->create( $donor_data );
697
698
			}
699
700
			$this->customer_id            = $donor->id;
701
			$this->pending['customer_id'] = $this->customer_id;
702
			$donor->attach_payment( $this->ID, false );
703
704
			$this->payment_meta = apply_filters( 'give_payment_meta', $this->payment_meta, $payment_data );
705
706
			$this->update_meta( '_give_payment_meta', $this->payment_meta );
707
			$this->new = true;
708
		}// End if().
709
710
		return $this->ID;
711 52
712 52
	}
713 52
714
	/**
715 52
	 * Save
716 52
	 *
717 52
	 * Once items have been set, an update is needed to save them to the database.
718
	 *
719 52
	 * @access public
720 52
	 *
721 52
	 * @return bool  True of the save occurred, false if it failed or wasn't needed
722
	 */
723 52
	public function save() {
724 20
725 20
		$saved = false;
726
727 52
		// Must have an ID.
728 52
		if ( empty( $this->ID ) ) {
729 52
730
			$payment_id = $this->insert_payment();
731 52
732 52
			if ( false === $payment_id ) {
733 52
				$saved = false;
734
			} else {
735 52
				$this->ID = $payment_id;
0 ignored issues
show
Documentation Bug introduced by
It seems like $payment_id can also be of type boolean. However, the property $ID is declared as type integer. 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...
736 52
			}
737 52
		}
738
739 52
		// Set ID if not matching.
740 52
		if ( $this->ID !== $this->_ID ) {
741 52
			$this->ID = $this->_ID;
742
		}
743 52
744 52
		// If we have something pending, let's save it.
745 52
		if ( ! empty( $this->pending ) ) {
746
747 52
			$total_increase = 0;
748 52
			$total_decrease = 0;
749 52
750
			foreach ( $this->pending as $key => $value ) {
751 52
752 52
				switch ( $key ) {
753 52
754
					case 'donations':
755 52
						// Update totals for pending donations.
756 52
						foreach ( $this->pending[ $key ] as $item ) {
757 52
758
							$quantity = isset( $item['quantity'] ) ? $item['quantity'] : 1;
759 52
							$price_id = isset( $item['price_id'] ) ? $item['price_id'] : 0;
760
761
							switch ( $item['action'] ) {
762
763 52
								case 'add':
764 52
765 52
									$price = $item['price'];
766
767 52
									if ( 'publish' === $this->status || 'complete' === $this->status ) {
768 52
769 52
										// Add donation to logs.
770
										$log_date = date_i18n( 'Y-m-d G:i:s', current_time( 'timestamp' ) );
771 52
										give_record_donation_in_log( $item['id'], $this->ID, $price_id, $log_date );
772 20
773 20
										$form = new Give_Donate_Form( $item['id'] );
774
										$form->increase_sales( $quantity );
775 52
										$form->increase_earnings( $price );
776
777 2
										$total_increase += $price;
778 2
									}
779 2
									break;
780 2
781
								case 'remove':
782 2
									$log_args = array(
783 2
										'post_type'   => 'give_log',
784
										'post_parent' => $item['id'],
785 52
										'numberposts' => $quantity,
786 42
										'meta_query'  => array(
0 ignored issues
show
introduced by
Detected usage of meta_query, possible slow query.
Loading history...
787 42
											array(
788
												'key'     => '_give_log_payment_id',
789 52
												'value'   => $this->ID,
790
												'compare' => '=',
791 52
											),
792 52
											array(
793 52
												'key'     => '_give_log_price_id',
794
												'value'   => $price_id,
795 52
												'compare' => '=',
796 52
											),
797
										),
798 52
									);
799 52
800 52
									$found_logs = get_posts( $log_args );
801 52
									foreach ( $found_logs as $log ) {
802 52
										wp_delete_post( $log->ID, true );
803
									}
804 52
805
									if ( 'publish' === $this->status || 'complete' === $this->status ) {
806 42
										$form = new Give_Donate_Form( $item['id'] );
807
										$form->decrease_sales( $quantity );
808 42
										$form->decrease_earnings( $item['amount'] );
809 42
810
										$total_decrease += $item['amount'];
811 1
									}
812
									break;
813 1
814 1
							}// End switch().
815
						}// End foreach().
816 42
						break;
817
818
					case 'status':
819 1
						$this->update_status( $this->status );
820 1
						break;
821
822 1
					case 'gateway':
823
						$this->update_meta( '_give_payment_gateway', $this->gateway );
824 42
						break;
825
826 52
					case 'mode':
827
						$this->update_meta( '_give_payment_mode', $this->mode );
828
						break;
829 52
830 52
					case 'transaction_id':
831 52
						$this->update_meta( '_give_payment_transaction_id', $this->transaction_id );
832 52
						break;
833 52
834 52
					case 'ip':
835 52
						$this->update_meta( '_give_payment_user_ip', $this->ip );
836
						break;
837 52
838 52
					case 'customer_id':
839
						$this->update_meta( '_give_payment_customer_id', $this->customer_id );
840
						break;
841 52
842 52
					case 'user_id':
843 52
						$this->update_meta( '_give_payment_user_id', $this->user_id );
844 52
						$this->user_info['id'] = $this->user_id;
845 52
						break;
846 52
847
					case 'form_title':
848 52
						$this->update_meta( '_give_payment_form_title', $this->form_title );
849 52
						break;
850 52
851
					case 'form_id':
852 52
						$this->update_meta( '_give_payment_form_id', $this->form_id );
853 52
						break;
854 52
855
					case 'price_id':
856 52
						$this->update_meta( '_give_payment_price_id', $this->price_id );
857
						break;
858
859
					case 'first_name':
860
						$this->user_info['first_name'] = $this->first_name;
861
						break;
862
863
					case 'last_name':
864
						$this->user_info['last_name'] = $this->last_name;
865
						break;
866
867
					case 'address':
868
						$this->user_info['address'] = $this->address;
869
						break;
870 52
871
					case 'email':
872 52
						$this->update_meta( '_give_payment_user_email', $this->email );
873
						break;
874
875 52
					case 'key':
876
						$this->update_meta( '_give_payment_purchase_key', $this->key );
877
						break;
878
879
					case 'number':
880
						$this->update_meta( '_give_payment_number', $this->number );
881 52
						break;
882 52
883 52
					case 'date':
884 52
						$args = array(
885
							'ID'        => $this->ID,
886 52
							'post_date' => $this->date,
887
							'edit_date' => true,
888
						);
889 52
890 52
						wp_update_post( $args );
891 52
						break;
892
893
					case 'completed_date':
894 2
						$this->update_meta( '_give_completed_date', $this->completed_date );
895 1
						break;
896 1
897
					case 'parent_payment':
898 1
						$args = array(
899
							'ID'          => $this->ID,
900
							'post_parent' => $this->parent_payment,
901 1
						);
902 1
903 1
						wp_update_post( $args );
904 1
						break;
905 1
906 1
					default:
907
						/**
908 1
						 * Fires while saving payment.
909
						 *
910
						 * @since 1.7
911
						 *
912 1
						 * @param Give_Payment $this Payment object.
913
						 */
914 1
						do_action( 'give_payment_save', $this, $key );
915
						break;
916
				}// End switch().
917
			}// End foreach().
918
919
			if ( 'pending' !== $this->status ) {
920 52
921 52
				$donor = new Give_Donor( $this->customer_id );
0 ignored issues
show
Documentation introduced by
$this->customer_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...
922
923
				$total_change = $total_increase - $total_decrease;
924 52
				if ( $total_change < 0 ) {
925 52
926 52
					$total_change = - ( $total_change );
927 52
					// Decrease the donor's donation stats.
928 52
					$donor->decrease_value( $total_change );
929
					give_decrease_total_earnings( $total_change );
930
931 52
				} elseif ( $total_change > 0 ) {
932
933
					// Increase the donor's donation stats.
934
					$donor->increase_value( $total_change );
935
					give_increase_total_earnings( $total_change );
936 52
937 52
				}
938 52
			}
939 52
940 52
			$this->update_meta( '_give_payment_total', give_sanitize_amount_for_db( $this->total ) );
941 52
942 52
			$new_meta = array(
943
				'form_title' => $this->form_title,
944 52
				'form_id'    => $this->form_id,
945
				'price_id'   => $this->price_id,
946 52
				'currency'   => $this->currency,
947
				'user_info'  => $this->user_info,
948 52
			);
949
950 52
			$meta        = $this->get_meta();
951
			$merged_meta = array_merge( $meta, $new_meta );
952
953
			// Only save the payment meta if it's changed.
954
			if ( md5( serialize( $meta ) ) !== md5( serialize( $merged_meta ) ) ) {
955
				$updated = $this->update_meta( '_give_payment_meta', $merged_meta );
0 ignored issues
show
Documentation introduced by
$merged_meta is of type array<string,string|integer|array>, but the function expects a string.

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...
956
				if ( false !== $updated ) {
957
					$saved = true;
0 ignored issues
show
Unused Code introduced by
$saved is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
958
				}
959
			}
960
961
			$this->pending = array();
962
			$saved         = true;
963
		}// End if().
964 2
965
		if ( true === $saved ) {
966
			$this->setup_payment( $this->ID );
967
		}
968 2
969 2
		return $saved;
970 2
	}
971 2
972 2
	/**
973
	 * Add a donation to a given payment
974 2
	 *
975
	 * @since  1.5
976
	 * @access public
977 2
	 *
978
	 * @param  int   $form_id The donation form to add
979
	 * @param  array $args    Other arguments to pass to the function
980
	 * @param  array $options List of donation options
981 2
	 *
982 2
	 * @return bool           True when successful, false otherwise
983 2
	 */
984 2
	public function add_donation( $form_id = 0, $args = array(), $options = array() ) {
985 2
986 2
		$donation = new Give_Donate_Form( $form_id );
0 ignored issues
show
Documentation introduced by
$form_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...
987
988 2
		// Bail if this post isn't a give donation form.
989
		if ( ! $donation || $donation->post_type !== 'give_forms' ) {
0 ignored issues
show
introduced by
Found "!== '". Use Yoda Condition checks, you must
Loading history...
990 2
			return false;
991
		}
992 2
993
		// Set some defaults.
994
		$defaults = array(
995
			'price'    => false,
996
			'price_id' => false,
997
		);
998
999
		$args = wp_parse_args( apply_filters( 'give_payment_add_donation_args', $args, $donation->ID ), $defaults );
1000
1001
		// Allow overriding the price.
1002
		if ( false !== $args['price'] ) {
1003
			$donation_amount = $args['price'];
1004
		} else {
1005
1006
			// Deal with variable pricing.
1007
			if ( give_has_variable_prices( $donation->ID ) ) {
1008
				$prices          = give_get_meta( $form_id, '_give_donation_levels', true );
1009
				$donation_amount = '';
1010
				// Loop through prices.
1011 View Code Duplication
				foreach ( $prices as $price ) {
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...
1012
					// Find a match between price_id and level_id.
1013
					// First verify array keys exists THEN make the match.
1014
					if ( ( isset( $args['price_id'] ) && isset( $price['_give_id']['level_id'] ) )
1015
					     && $args['price_id'] == $price['_give_id']['level_id']
1016
					) {
1017
						$donation_amount = $price['_give_amount'];
1018
					}
1019
				}
1020
				// Fallback to the lowest price point.
1021
				if ( $donation_amount == '' ) {
1022
					$donation_amount  = give_get_lowest_price_option( $donation->ID );
1023
					$args['price_id'] = give_get_lowest_price_id( $donation->ID );
1024
				}
1025
			} else {
1026
				// Simple form price.
1027
				$donation_amount = give_get_form_price( $donation->ID );
1028
			}
1029
		}
1030
1031
		// Sanitizing the price here so we don't have a dozen calls later.
1032
		$donation_amount = give_maybe_sanitize_amount( $donation_amount );
1033
		$total           = round( $donation_amount, give_currency_decimal_filter() );
1034
1035
		// Add Options.
1036
		$default_options = array();
1037
		if ( false !== $args['price_id'] ) {
1038
			$default_options['price_id'] = (int) $args['price_id'];
1039
		}
1040
		$options = wp_parse_args( $options, $default_options );
1041
1042
		// Do not allow totals to go negative.
1043
		if ( $total < 0 ) {
1044
			$total = 0;
1045
		}
1046
1047
		$donation = array(
1048
			'name'     => $donation->post_title,
1049
			'id'       => $donation->ID,
1050
			'price'    => round( $total, give_currency_decimal_filter() ),
1051
			'subtotal' => round( $total, give_currency_decimal_filter() ),
1052
			'price_id' => $args['price_id'],
1053
			'action'   => 'add',
1054
			'options'  => $options,
1055
		);
1056
1057
		$this->pending['donations'][] = $donation;
1058
1059
		$this->increase_subtotal( $total );
1060
1061
		return true;
1062
1063
	}
1064
1065
	/**
1066
	 * Remove a donation from the payment
1067
	 *
1068
	 * @since  1.5
1069
	 * @access public
1070
	 *
1071
	 * @param  int   $form_id The form ID to remove
1072
	 * @param  array $args    Arguments to pass to identify (quantity, amount, price_id)
1073
	 *
1074
	 * @return bool           If the item was removed or not
1075
	 */
1076
	public function remove_donation( $form_id, $args = array() ) {
1077
1078
		// Set some defaults.
1079
		$defaults = array(
1080
			'quantity' => 1,
1081
			'price'    => false,
1082
			'price_id' => false,
1083
		);
1084
		$args     = wp_parse_args( $args, $defaults );
1085
1086
		$form = new Give_Donate_Form( $form_id );
0 ignored issues
show
Documentation introduced by
$form_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...
1087
1088
		// Bail if this post isn't a valid give donation form.
1089
		if ( ! $form || $form->post_type !== 'give_forms' ) {
0 ignored issues
show
introduced by
Found "!== '". Use Yoda Condition checks, you must
Loading history...
1090
			return false;
1091
		}
1092
1093
		$pending_args             = $args;
1094
		$pending_args['id']       = $form_id;
1095
		$pending_args['amount']   = $this->total;
1096
		$pending_args['price_id'] = false !== $args['price_id'] ? (int) $args['price_id'] : false;
1097
		$pending_args['quantity'] = $args['quantity'];
1098
		$pending_args['action']   = 'remove';
1099
1100
		$this->pending['donations'][] = $pending_args;
1101
1102
		$this->decrease_subtotal( $this->total );
1103
1104
		return true;
1105
	}
1106
1107
1108
	/**
1109
	 * Add a note to a payment
1110
	 *
1111
	 * @since  1.5
1112
	 * @access public
1113
	 *
1114
	 * @param  string $note The note to add
1115
	 *
1116
	 * @return bool           If the note was specified or not
1117
	 */
1118
	public function add_note( $note = false ) {
1119
		// Bail if no note specified.
1120
		if ( ! $note ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $note of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1121
			return false;
1122
		}
1123
1124
		give_insert_payment_note( $this->ID, $note );
1125
	}
1126
1127
	/**
1128
	 * Increase the payment's subtotal
1129
	 *
1130
	 * @since  1.5
1131
	 * @access private
1132
	 *
1133
	 * @param  float $amount The amount to increase the payment subtotal by.
1134
	 *
1135
	 * @return void
1136
	 */
1137
	private function increase_subtotal( $amount = 0.00 ) {
1138
		$amount         = (float) $amount;
1139
		$this->subtotal += $amount;
1140
1141
		$this->recalculate_total();
1142
	}
1143
1144
	/**
1145
	 * Decrease the payment's subtotal.
1146
	 *
1147
	 * @since  1.5
1148
	 * @access private
1149
	 *
1150
	 * @param  float $amount The amount to decrease the payment subtotal by.
1151
	 *
1152
	 * @return void
1153
	 */
1154
	private function decrease_subtotal( $amount = 0.00 ) {
1155
		$amount         = (float) $amount;
1156
		$this->subtotal -= $amount;
1157
1158
		if ( $this->subtotal < 0 ) {
1159
			$this->subtotal = 0;
0 ignored issues
show
Documentation Bug introduced by
The property $subtotal was declared of type double, but 0 is of type integer. 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...
1160
		}
1161
1162
		$this->recalculate_total();
1163
	}
1164
1165
	/**
1166
	 * Set or update the total for a payment.
1167
	 *
1168
	 * @since  1.5
1169
	 * @access private
1170
	 *
1171
	 * @return void
1172 52
	 */
1173 52
	private function recalculate_total() {
1174 52
		$this->total = $this->subtotal;
1175
	}
1176 52
1177 52
	/**
1178
	 * Set the payment status and run any status specific changes necessary.
1179
	 *
1180
	 * @since  1.5
1181
	 * @access public
1182
	 *
1183
	 * @param  string|bool $status The status to set the payment to.
1184
	 *
1185
	 * @return bool   $updated Returns if the status was successfully updated.
1186
	 */
1187
	public function update_status( $status = false ) {
1188 2
1189 2
		// standardize the 'complete(d)' status.
1190 2
		if ( $status == 'completed' || $status == 'complete' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
1191
			$status = 'publish';
1192 2
		}
1193
1194
		$old_status = ! empty( $this->old_status ) ? $this->old_status : false;
1195
1196 2
		if ( $old_status === $status ) {
1197 2
			return false; // Don't permit status changes that aren't changes.
1198
		}
1199
1200
		$do_change = apply_filters( 'give_should_update_payment_status', true, $this->ID, $status, $old_status );
1201
1202
		$updated = false;
1203
1204
		if ( $do_change ) {
1205
1206
			/**
1207
			 * Fires before changing payment status.
1208
			 *
1209
			 * @since 1.5
1210
			 *
1211
			 * @param int    $payment_id Payments ID.
1212
			 * @param string $status     The new status.
1213
			 * @param string $old_status The old status.
1214
			 */
1215
			do_action( 'give_before_payment_status_change', $this->ID, $status, $old_status );
1216
1217
			$update_fields = array(
1218
				'ID'          => $this->ID,
1219
				'post_status' => $status,
1220
				'edit_date'   => current_time( 'mysql' ),
1221
			);
1222
1223
			$updated = wp_update_post( apply_filters( 'give_update_payment_status_fields', $update_fields ) );
1224
1225
			$all_payment_statuses  = give_get_payment_statuses();
1226
			$this->status_nicename = array_key_exists( $status, $all_payment_statuses ) ? $all_payment_statuses[ $status ] : ucfirst( $status );
1227
1228
			// Process any specific status functions.
1229
			switch ( $status ) {
1230
				case 'refunded':
1231
					$this->process_refund();
1232
					break;
1233
				case 'failed':
1234
					$this->process_failure();
0 ignored issues
show
Unused Code introduced by
The call to the method Give_Payment::process_failure() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
1235
					break;
1236
				case 'pending':
1237
					$this->process_pending();
1238
					break;
1239
				case 'cancelled':
1240
					$this->process_cancelled();
1241 52
					break;
1242 52
				case 'revoked':
1243 52
					$this->process_revoked();
1244
					break;
1245
			}
1246
1247
			/**
1248
			 * Fires after changing payment status.
1249
			 *
1250
			 * @since 1.5
1251
			 *
1252
			 * @param int    $payment_id Payment ID.
1253
			 * @param string $status     The new status.
1254 52
			 * @param string $old_status The old status.
1255
			 */
1256
			do_action( 'give_update_payment_status', $this->ID, $status, $old_status );
1257 52
1258 39
		}// End if().
1259 39
1260
		return $updated;
1261 52
1262
	}
1263 52
1264 52
	/**
1265
	 * Change the status of the payment to refunded, and run the necessary changes
1266
	 *
1267 42
	 * @since  1.5
1268
	 * @access public
1269 42
	 *
1270
	 * @return void
1271
	 */
1272 42
	public function refund() {
1273
		$this->old_status        = $this->status;
1274 42
		$this->status            = 'refunded';
1275
		$this->pending['status'] = $this->status;
1276
1277 42
		$this->save();
1278 42
	}
1279 42
1280 42
	/**
1281
	 * Get a post meta item for the payment
1282 42
	 *
1283
	 * @since  1.5
1284 42
	 * @access public
1285 42
	 *
1286
	 * @param  string  $meta_key The Meta Key
1287
	 * @param  boolean $single   Return single item or array
1288
	 *
1289 42
	 * @return mixed             The value from the post meta
1290 4
	 */
1291 4
	public function get_meta( $meta_key = '_give_payment_meta', $single = true ) {
1292 42
1293
		$meta = give_get_meta( $this->ID, $meta_key, $single );
1294
1295 42
		if ( $meta_key === '_give_payment_meta' ) {
0 ignored issues
show
introduced by
Found "=== '". Use Yoda Condition checks, you must
Loading history...
1296 3
			$meta = (array) $meta;
1297 3
1298
			if ( empty( $meta['key'] ) ) {
1299
				$meta['key'] = $this->setup_payment_key();
1300 42
			}
1301
1302 42
			if ( empty( $meta['form_title'] ) ) {
1303
				$meta['form_title'] = $this->setup_form_title();
1304 42
			}
1305
1306
			if ( empty( $meta['email'] ) ) {
1307
				$meta['email'] = $this->setup_email();
1308
			}
1309
1310
			if ( empty( $meta['date'] ) ) {
1311
				$meta['date'] = get_post_field( 'post_date', $this->ID );
1312
			}
1313
		}
1314 4
1315 4
		$meta = apply_filters( "give_get_payment_meta_{$meta_key}", $meta, $this->ID );
1316 4
1317 4
		// Security check.
1318 View Code Duplication
		if ( is_serialized( $meta ) ) {
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...
1319 4
			preg_match( '/[oO]\s*:\s*\d+\s*:\s*"\s*(?!(?i)(stdClass))/', $meta, $matches );
1320 4
			if ( ! empty( $matches ) ) {
1321
				$meta = array();
1322
			}
1323
		}
1324
1325
		return apply_filters( 'give_get_payment_meta', $meta, $this->ID, $meta_key );
1326
	}
1327
1328
	/**
1329
	 * Update the post meta
1330
	 *
1331
	 * @since  1.5
1332 52
	 * @access public
1333
	 *
1334 52
	 * @param  string $meta_key   The meta key to update
1335
	 * @param  string $meta_value The meta value
1336 52
	 * @param  string $prev_value Previous meta value
1337
	 *
1338 52
	 * @return int|bool           Meta ID if the key didn't exist, true on successful update, false on failure
1339 52
	 */
1340 52
	public function update_meta( $meta_key = '', $meta_value = '', $prev_value = '' ) {
1341
		if ( empty( $meta_key ) ) {
1342 52
			return false;
1343 52
		}
1344 52
1345
		if ( $meta_key == 'key' || $meta_key == 'date' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
1346 52
1347 52
			$current_meta              = $this->get_meta();
1348 52
			$current_meta[ $meta_key ] = $meta_value;
1349
1350 52
			$meta_key   = '_give_payment_meta';
1351 52
			$meta_value = $current_meta;
1352 52
1353 52
		} elseif ( $meta_key == 'email' || $meta_key == '_give_payment_user_email' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
1354
1355 52
			$meta_value = apply_filters( "give_update_payment_meta_{$meta_key}", $meta_value, $this->ID );
1356
			give_update_meta( $this->ID, '_give_payment_user_email', $meta_value );
1357 52
1358
			$current_meta                       = $this->get_meta();
1359
			$current_meta['user_info']['email'] = $meta_value;
1360
1361
			$meta_key   = '_give_payment_meta';
1362
			$meta_value = $current_meta;
1363
1364
		}
1365
1366
		$meta_value = apply_filters( "give_update_payment_meta_{$meta_key}", $meta_value, $this->ID );
1367
1368
		return give_update_meta( $this->ID, $meta_key, $meta_value, $prev_value );
1369
	}
1370
1371 52
	/**
1372 52
	 * When a payment is set to a status of 'refunded' process the necessary actions to reduce stats
1373
	 *
1374
	 * @since  1.5
1375
	 * @access private
1376 52
	 *
1377
	 * @return void
1378
	 */
1379 View Code Duplication
	private function process_refund() {
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...
1380
		$process_refund = true;
1381
1382
		// If the payment was not in publish or revoked status, don't decrement stats as they were never incremented.
1383
		if ( 'publish' != $this->old_status || 'refunded' != $this->status ) {
1384 52
			$process_refund = false;
1385
		}
1386 52
1387 52
		// Allow extensions to filter for their own payment types, Example: Recurring Payments.
1388
		$process_refund = apply_filters( 'give_should_process_refund', $process_refund, $this );
1389 52
1390 52
		if ( false === $process_refund ) {
1391
			return;
1392 52
		}
1393 52
1394
		/**
1395 52
		 * Fires before refunding payment.
1396
		 *
1397 52
		 * @since 1.5
1398
		 *
1399 52
		 * @param Give_Payment $this Payment object.
1400
		 */
1401
		do_action( 'give_pre_refund_payment', $this );
1402
1403
		$decrease_earnings       = apply_filters( 'give_decrease_store_earnings_on_refund', true, $this );
1404
		$decrease_customer_value = apply_filters( 'give_decrease_customer_value_on_refund', true, $this );
1405
		$decrease_purchase_count = apply_filters( 'give_decrease_customer_purchase_count_on_refund', true, $this );
1406
1407
		$this->maybe_alter_stats( $decrease_earnings, $decrease_customer_value, $decrease_purchase_count );
1408
		$this->delete_sales_logs();
1409 4
1410 4
		// @todo: Refresh only range related stat cache
1411
		give_delete_donation_stats();
1412
1413 4
		/**
1414
		 * Fires after refunding payment.
1415
		 *
1416
		 * @since 1.5
1417
		 *
1418 4
		 * @param Give_Payment $this Payment object.
1419
		 */
1420 4
		do_action( 'give_post_refund_payment', $this );
1421
	}
1422
1423
	/**
1424 4
	 * Process when a payment is set to failed
1425
	 *
1426 4
	 * @since  1.5
1427 4
	 * @access private
1428 4
	 *
1429
	 * @return void
1430 4
	 */
1431 4
	private function process_failure() {
1432
1433
	}
1434 4
1435
	/**
1436 4
	 * Process when a payment moves to pending
1437 4
	 *
1438
	 * @since  1.5
1439
	 * @access private
1440
	 *
1441
	 * @return void
1442
	 */
1443 View Code Duplication
	private function process_pending() {
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...
1444
		$process_pending = true;
1445
1446
		// If the payment was not in publish or revoked status, don't decrement stats as they were never incremented.
1447
		if ( 'publish' != $this->old_status || 'pending' != $this->status ) {
1448
			$process_pending = false;
1449
		}
1450
1451
		// Allow extensions to filter for their own payment types, Example: Recurring Payments.
1452
		$process_pending = apply_filters( 'give_should_process_pending', $process_pending, $this );
1453
1454
		if ( false === $process_pending ) {
1455
			return;
1456 3
		}
1457 3
1458
		$decrease_earnings       = apply_filters( 'give_decrease_earnings_on_pending', true, $this );
1459
		$decrease_donor_value    = apply_filters( 'give_decrease_donor_value_on_pending', true, $this );
1460 3
		$decrease_donation_count = apply_filters( 'give_decrease_donors_donation_count_on_pending', true, $this );
1461 1
1462 1
		$this->maybe_alter_stats( $decrease_earnings, $decrease_donor_value, $decrease_donation_count );
1463
		$this->delete_sales_logs();
1464
1465 3
		$this->completed_date = false;
0 ignored issues
show
Documentation Bug introduced by
The property $completed_date was declared of type string, but false is of type false. 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...
1466
		$this->update_meta( '_give_completed_date', '' );
1467 3
1468 1
		// @todo: Refresh only range related stat cache
1469
		give_delete_donation_stats();
1470
	}
1471 2
1472 2
	/**
1473 2
	 * Process when a payment moves to cancelled.
1474
	 *
1475 2
	 * @since  1.5
1476 2
	 * @access private
1477
	 *
1478 2
	 * @return void
1479 2
	 */
1480 View Code Duplication
	private function process_cancelled() {
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...
1481
		$process_cancelled = true;
1482 2
1483 2
		// If the payment was not in publish or revoked status, don't decrement stats as they were never incremented.
1484
		if ( 'publish' != $this->old_status || 'cancelled' != $this->status ) {
1485
			$process_cancelled = false;
1486
		}
1487
1488
		// Allow extensions to filter for their own payment types, Example: Recurring Payments.
1489
		$process_cancelled = apply_filters( 'give_should_process_cancelled', $process_cancelled, $this );
1490
1491
		if ( false === $process_cancelled ) {
1492
			return;
1493
		}
1494
1495
		$decrease_earnings       = apply_filters( 'give_decrease_earnings_on_cancelled', true, $this );
1496 6
		$decrease_donor_value    = apply_filters( 'give_decrease_donor_value_on_cancelled', true, $this );
1497
		$decrease_donation_count = apply_filters( 'give_decrease_donors_donation_count_on_cancelled', true, $this );
1498 6
1499
		$this->maybe_alter_stats( $decrease_earnings, $decrease_donor_value, $decrease_donation_count );
1500
		$this->delete_sales_logs();
1501 6
1502 4
		$this->completed_date = false;
0 ignored issues
show
Documentation Bug introduced by
The property $completed_date was declared of type string, but false is of type false. 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...
1503 4
		$this->update_meta( '_give_completed_date', '' );
1504
1505
		// @todo: Refresh only range related stat cache
1506 6
		give_delete_donation_stats();
1507
	}
1508 6
1509
	/**
1510 6
	 * Process when a payment moves to revoked.
1511 4
	 *
1512 4
	 * @since  1.5
1513
	 * @return void
1514 6
	 */
1515 4 View Code Duplication
	private function process_revoked() {
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...
1516 4
		$process_revoked = true;
1517
1518 6
		// If the payment was not in publish, don't decrement stats as they were never incremented.
1519
		if ( 'publish' != $this->old_status || 'revoked' != $this->status ) {
1520 6
			$process_revoked = false;
1521
		}
1522
1523
		// Allow extensions to filter for their own payment types, Example: Recurring Payments.
1524
		$process_revoked = apply_filters( 'give_should_process_revoked', $process_revoked, $this );
1525
1526
		if ( false === $process_revoked ) {
1527
			return;
1528 6
		}
1529 6
1530
		$decrease_earnings       = apply_filters( 'give_decrease_earnings_on_revoked', true, $this );
1531
		$decrease_donor_value    = apply_filters( 'give_decrease_donor_value_on_revoked', true, $this );
1532 6
		$decrease_donation_count = apply_filters( 'give_decrease_donors_donation_count_on_revoked', true, $this );
1533 6
1534 6
		$this->maybe_alter_stats( $decrease_earnings, $decrease_donor_value, $decrease_donation_count );
1535
		$this->delete_sales_logs();
1536
1537 6
		$this->completed_date = false;
0 ignored issues
show
Documentation Bug introduced by
The property $completed_date was declared of type string, but false is of type false. 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...
1538 6
		$this->update_meta( '_give_completed_date', '' );
1539 6
1540
		// @todo: Refresh only range related stat cache
1541 6
		give_delete_donation_stats();
1542 6
	}
1543
1544
	/**
1545
	 * Used during the process of moving to refunded or pending, to decrement stats
1546
	 *
1547
	 * @since  1.5
1548
	 * @access private
1549
	 *
1550
	 * @param  bool $alter_store_earnings          If the method should alter the store earnings
1551
	 * @param  bool $alter_customer_value          If the method should reduce the donor value
1552
	 * @param  bool $alter_customer_purchase_count If the method should reduce the donor's purchase count
1553
	 *
1554
	 * @return void
1555
	 */
1556
	private function maybe_alter_stats( $alter_store_earnings, $alter_customer_value, $alter_customer_purchase_count ) {
1557
1558
		give_undo_donation( $this->ID );
1559 52
1560 52
		// Decrease store earnings.
1561
		if ( true === $alter_store_earnings ) {
1562 52
			give_decrease_total_earnings( $this->total );
1563 52
		}
1564
1565
		// Decrement the stats for the donor.
1566 42
		if ( ! empty( $this->customer_id ) ) {
1567
1568 42
			$donor = new Give_Donor( $this->customer_id );
0 ignored issues
show
Documentation introduced by
$this->customer_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...
1569
1570
			if ( true === $alter_customer_value ) {
1571
				$donor->decrease_value( $this->total );
1572
			}
1573
1574
			if ( true === $alter_customer_purchase_count ) {
1575
				$donor->decrease_donation_count();
1576
			}
1577 52
		}
1578 52
1579
	}
1580
1581
	/**
1582
	 * Delete sales logs for this donation
1583
	 *
1584
	 * @since  1.5
1585
	 * @access private
1586
	 *
1587 52
	 * @return void
1588 52
	 */
1589
	private function delete_sales_logs() {
1590 52
		global $give_logs;
1591 2
1592 2
		// Remove related sale log entries.
1593
		$give_logs->delete_logs(
1594 2
			null,
1595
			'sale',
1596
			array(
1597 2
				array(
1598
					'key'   => '_give_log_payment_id',
1599 52
					'value' => $this->ID,
1600
				),
1601
			)
1602
		);
1603
	}
1604
1605
	/**
1606
	 * Setup functions only, these are not to be used by developers.
1607
	 * These functions exist only to allow the setup routine to be backwards compatible with our old
1608 52
	 * helper functions.
1609 52
	 *
1610
	 * These will run whenever setup_payment is called, which should only be called once.
1611 52
	 * To update an attribute, update it directly instead of re-running the setup routine
1612
	 */
1613
1614
	/**
1615
	 * Setup the payment completed date
1616
	 *
1617
	 * @since  1.5
1618
	 * @access private
1619
	 *
1620 52
	 * @return string The date the payment was completed
1621 52
	 */
1622
	private function setup_completed_date() {
1623 52
		$payment = get_post( $this->ID );
1624 52
1625
		if ( 'pending' == $payment->post_status || 'preapproved' == $payment->post_status ) {
1626
			return false; // This payment was never completed.
1627
		}
1628
1629
		$date = ( $date = $this->get_meta( '_give_completed_date', true ) ) ? $date : $payment->modified_date;
1630 52
1631
		return $date;
1632
	}
1633
1634
	/**
1635
	 * Setup the payment mode
1636
	 *
1637
	 * @since  1.5
1638
	 * @access private
1639
	 *
1640 52
	 * @return string The payment mode
1641 52
	 */
1642
	private function setup_mode() {
1643 52
		return $this->get_meta( '_give_payment_mode' );
1644
	}
1645
1646
	/**
1647
	 * Setup the payment import data
1648
	 *
1649
	 * @since  1.8.13
1650
	 * @access private
1651
	 *
1652 52
	 * @return bool The payment import
1653 52
	 */
1654
	private function setup_import() {
1655 52
		return (bool) $this->get_meta( '_give_payment_import' );
1656
	}
1657
1658
	/**
1659
	 * Setup the payment total
1660
	 *
1661
	 * @since  1.5
1662
	 * @access private
1663
	 *
1664 52
	 * @return float The payment total
1665 52
	 */
1666
	private function setup_total() {
1667 52
		$amount = $this->get_meta( '_give_payment_total', true );
1668
1669
		if ( empty( $amount ) && '0.00' != $amount ) {
1670
			$meta = $this->get_meta( '_give_payment_meta', true );
1671
			$meta = maybe_unserialize( $meta );
1672
1673
			if ( isset( $meta['amount'] ) ) {
1674
				$amount = $meta['amount'];
1675
			}
1676 52
		}
1677 52
1678
		return round( floatval( $amount ), give_currency_decimal_filter() );
1679 52
	}
1680
1681 52
	/**
1682 52
	 * Setup the payment subtotal
1683
	 *
1684 52
	 * @since  1.5
1685
	 * @access private
1686 52
	 *
1687
	 * @return float The subtotal of the payment
1688
	 */
1689
	private function setup_subtotal() {
1690
		$subtotal = $this->total;
1691
1692
		return $subtotal;
1693
	}
1694
1695 52
	/**
1696 52
	 * Setup the currency code
1697
	 *
1698 52
	 * @since  1.5
1699
	 * @access private
1700
	 *
1701
	 * @return string The currency for the payment
1702
	 */
1703
	private function setup_currency() {
1704
		$currency = isset( $this->payment_meta['currency'] ) ? $this->payment_meta['currency'] : apply_filters( 'give_payment_currency_default', give_get_currency(), $this );
1705
1706
		return $currency;
1707 52
	}
1708 52
1709
	/**
1710 52
	 * Setup the gateway used for the payment
1711
	 *
1712
	 * @since  1.5
1713
	 * @access private
1714
	 *
1715
	 * @return string The gateway
1716
	 */
1717
	private function setup_gateway() {
1718
		$gateway = $this->get_meta( '_give_payment_gateway', true );
1719 52
1720 52
		return $gateway;
1721
	}
1722 52
1723
	/**
1724
	 * Setup the donation ID
1725
	 *
1726
	 * @since  1.5
1727
	 * @access private
1728
	 *
1729
	 * @return string The donation ID
1730
	 */
1731 52
	private function setup_transaction_id() {
1732 52
		$transaction_id = $this->get_meta( '_give_payment_transaction_id', true );
1733
1734 52
		if ( empty( $transaction_id ) ) {
1735 2
			$gateway        = $this->gateway;
1736 2
			$transaction_id = apply_filters( "give_get_payment_transaction_id-{$gateway}", $this->ID );
1737
		}
1738 52
1739
		return $transaction_id;
1740
	}
1741
1742
	/**
1743
	 * Setup the IP Address for the payment
1744
	 *
1745
	 * @since  1.5
1746
	 * @access private
1747 52
	 *
1748
	 * @return string The IP address for the payment
1749 52
	 */
1750 52
	private function setup_ip() {
1751 52
		$ip = $this->get_meta( '_give_payment_user_ip', true );
1752
1753 52
		return $ip;
1754 52
	}
1755
1756 52
	/**
1757
	 * Setup the donor ID.
1758
	 *
1759
	 * @since  1.5
1760
	 * @access private
1761
	 *
1762
	 * @return int The Donor ID.
1763
	 */
1764
	private function setup_donor_id() {
1765
		$customer_id = $this->get_meta( '_give_payment_customer_id', true );
1766
1767
		return $customer_id;
1768
	}
1769
1770
	/**
1771 52
	 * Setup the User ID associated with the donation
1772 52
	 *
1773 52
	 * @since  1.5
1774 52
	 * @access private
1775 52
	 *
1776
	 * @return int The User ID
1777
	 */
1778
	private function setup_user_id() {
1779 21
		$user_id = $this->get_meta( '_give_payment_user_id', true );
1780 21
1781
		return $user_id;
1782 21
	}
1783 21
1784
	/**
1785 21
	 * Setup the email address for the donation.
1786 21
	 *
1787 21
	 * @since  1.5
1788
	 * @access private
1789 21
	 *
1790 21
	 * @return string The email address for the payment.
1791
	 */
1792
	private function setup_email() {
1793
		$email = $this->get_meta( '_give_payment_user_email', true );
1794
1795
		if ( empty( $email ) && $this->customer_id ) {
1796 52
			$email = Give()->donors->get_column( 'email', $this->customer_id );
1797
		}
1798 52
1799
		return $email;
1800
	}
1801 52
1802
	/**
1803
	 * Setup the user info.
1804
	 *
1805
	 * @since  1.5
1806
	 * @access private
1807
	 *
1808
	 * @return array The user info associated with the payment.
1809
	 */
1810
	private function setup_user_info() {
1811 52
		$defaults = array(
1812
			'first_name' => $this->first_name,
1813 52
			'last_name'  => $this->last_name,
1814 52
		);
1815 52
1816 52
		$user_info = isset( $this->payment_meta['user_info'] ) ? $this->payment_meta['user_info'] : array();
1817 52
1818 52 View Code Duplication
		if ( is_serialized( $user_info ) ) {
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...
1819
			preg_match( '/[oO]\s*:\s*\d+\s*:\s*"\s*(?!(?i)(stdClass))/', $user_info, $matches );
1820 52
			if ( ! empty( $matches ) ) {
1821
				$user_info = array();
1822 52
			}
1823
		}
1824
1825
		$user_info = wp_parse_args( $user_info, $defaults );
1826
1827
		if ( empty( $user_info ) ) {
1828
			// Get the donor, but only if it's been created.
1829
			$donor = new Give_Donor( $this->customer_id );
0 ignored issues
show
Documentation introduced by
$this->customer_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...
1830
1831 52
			if ( $donor->id > 0 ) {
1832
				$name      = explode( ' ', $donor->name, 2 );
1833 52
				$user_info = array(
1834
					'first_name' => $name[0],
1835 52
					'last_name'  => $name[1],
1836
					'email'      => $donor->email,
1837
					'discount'   => 'none',
1838
				);
1839
			}
1840
		} else {
1841
			// Get the donor, but only if it's been created.
1842
			$donor = new Give_Donor( $this->customer_id );
0 ignored issues
show
Documentation introduced by
$this->customer_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...
1843
			if ( $donor->id > 0 ) {
1844 52
				foreach ( $user_info as $key => $value ) {
1845
					if ( ! empty( $value ) ) {
1846 52
						continue;
1847
					}
1848 52
1849
					switch ( $key ) {
1850
						case 'first_name':
1851
							$name = explode( ' ', $donor->name, 2 );
1852
1853
							$user_info[ $key ] = $name[0];
1854
							break;
1855
1856
						case 'last_name':
1857 52
							$name      = explode( ' ', $donor->name, 2 );
1858 52
							$last_name = ! empty( $name[1] ) ? $name[1] : '';
1859
1860 52
							$user_info[ $key ] = $last_name;
1861
							break;
1862
1863
						case 'email':
1864
							$user_info[ $key ] = $donor->email;
1865
							break;
1866
					}
1867
				}
1868
			}
1869 52
		}// End if().
1870 52
1871
		return $user_info;
1872 52
1873
	}
1874
1875
	/**
1876
	 * Setup the Address for the payment.
1877
	 *
1878
	 * @since  1.5
1879
	 * @access private
1880
	 *
1881 52
	 * @return array The Address information for the payment.
1882 52
	 */
1883
	private function setup_address() {
1884 52
1885
		$address = ! empty( $this->payment_meta['user_info']['address'] ) ? $this->payment_meta['user_info']['address'] : array(
1886 20
			'line1'   => '',
1887
			'line2'   => '',
1888 20
			'city'    => '',
1889
			'country' => '',
1890 2
			'state'   => '',
1891
			'zip'     => '',
1892 2
		);
1893
1894 20
		return $address;
1895
	}
1896 52
1897
	/**
1898
	 * Setup the form title.
1899
	 *
1900
	 * @since  1.5
1901
	 * @access private
1902
	 *
1903
	 * @return string The Form Title.
1904
	 */
1905
	private function setup_form_title() {
1906
1907
		$form_id = $this->get_meta( '_give_payment_form_title', true );
1908
1909
		return $form_id;
1910
	}
1911
1912
	/**
1913
	 * Setup the form ID.
1914 42
	 *
1915 42
	 * @since  1.5
1916
	 * @access private
1917
	 *
1918
	 * @return int The Form ID
1919
	 */
1920
	private function setup_form_id() {
1921
1922
		$form_id = $this->get_meta( '_give_payment_form_id', true );
1923
1924
		return $form_id;
1925
	}
1926
1927
	/**
1928
	 * Setup the price ID.
1929
	 *
1930
	 * @since  1.5
1931
	 * @access private
1932
	 *
1933
	 * @return int The Form Price ID.
1934 42
	 */
1935 42
	private function setup_price_id() {
1936
		$price_id = $this->get_meta( '_give_payment_price_id', true );
1937
1938
		return $price_id;
1939
	}
1940
1941
	/**
1942
	 * Setup the payment key.
1943
	 *
1944 42
	 * @since  1.5
1945 42
	 * @access private
1946
	 *
1947
	 * @return string The Payment Key.
1948
	 */
1949
	private function setup_payment_key() {
1950
		$key = $this->get_meta( '_give_payment_purchase_key', true );
1951
1952
		return $key;
1953
	}
1954 11
1955 11
	/**
1956
	 * Setup the payment number.
1957
	 *
1958
	 * @since  1.5
1959
	 * @access private
1960
	 *
1961
	 * @return int|string Integer by default, or string if sequential order numbers is enabled.
1962
	 */
1963
	private function setup_payment_number() {
1964
		$number = $this->ID;
1965
1966
		if ( give_get_option( 'enable_sequential' ) ) {
1967
1968
			$number = $this->get_meta( '_give_payment_number', true );
1969
1970
			if ( ! $number ) {
1971
1972
				$number = $this->ID;
1973
1974 52
			}
1975 52
		}
1976
1977
		return $number;
1978
	}
1979
1980
	/**
1981
	 * Converts this object into an array for special cases.
1982
	 *
1983
	 * @access public
1984 41
	 *
1985 41
	 * @return array The payment object as an array.
1986
	 */
1987
	public function array_convert() {
1988
		return get_object_vars( $this );
1989
	}
1990
1991
1992
	/**
1993
	 * Flag to check if donation is completed or not.
1994 42
	 *
1995 42
	 * @since  1.8
1996
	 * @access public
1997
	 *
1998
	 * @return bool
1999
	 */
2000
	public function is_completed() {
2001
		return ( 'publish' === $this->status && $this->completed_date );
2002
	}
2003
2004 43
	/**
2005 43
	 * Retrieve payment completion date.
2006
	 *
2007
	 * @since  1.5
2008
	 * @access private
2009
	 *
2010
	 * @return string Date payment was completed.
2011
	 */
2012
	private function get_completed_date() {
2013
		return apply_filters( 'give_payment_completed_date', $this->completed_date, $this->ID, $this );
2014
	}
2015
2016
	/**
2017
	 * Retrieve payment subtotal.
2018
	 *
2019
	 * @since  1.5
2020
	 * @access private
2021
	 *
2022
	 * @return float Payment subtotal.
2023
	 */
2024 52
	private function get_subtotal() {
2025 52
		return apply_filters( 'give_get_payment_subtotal', $this->subtotal, $this->ID, $this );
2026
	}
2027
2028
	/**
2029
	 * Retrieve payment currency.
2030
	 *
2031
	 * @since  1.5
2032
	 * @access private
2033
	 *
2034 52
	 * @return string Payment currency code.
2035 52
	 */
2036
	private function get_currency() {
2037
		return apply_filters( 'give_payment_currency_code', $this->currency, $this->ID, $this );
2038
	}
2039
2040
	/**
2041
	 * Retrieve payment gateway.
2042
	 *
2043
	 * @since  1.5
2044 42
	 * @access private
2045 42
	 *
2046
	 * @return string Gateway used.
2047
	 */
2048
	private function get_gateway() {
2049
		return apply_filters( 'give_payment_gateway', $this->gateway, $this->ID, $this );
2050
	}
2051
2052
	/**
2053
	 * Retrieve donation ID.
2054
	 *
2055
	 * @since  1.5
2056
	 * @access private
2057
	 *
2058
	 * @return string Donation ID from merchant processor.
2059
	 */
2060
	private function get_transaction_id() {
2061
		return apply_filters( 'give_get_payment_transaction_id', $this->transaction_id, $this->ID, $this );
2062
	}
2063
2064
	/**
2065
	 * Retrieve payment IP
2066
	 *
2067
	 * @since  1.5
2068
	 * @access private
2069
	 *
2070
	 * @return string Payment IP address
2071
	 */
2072
	private function get_ip() {
2073
		return apply_filters( 'give_payment_user_ip', $this->ip, $this->ID, $this );
2074
	}
2075
2076
	/**
2077
	 * Retrieve payment donor ID.
2078
	 *
2079
	 * @since  1.5
2080
	 * @access private
2081
	 *
2082
	 * @return int Payment donor ID.
2083
	 */
2084
	private function get_donor_id() {
2085
		return apply_filters( 'give_payment_customer_id', $this->customer_id, $this->ID, $this );
2086
	}
2087
2088
	/**
2089
	 * Retrieve payment user ID.
2090
	 *
2091
	 * @since  1.5
2092
	 * @access private
2093
	 *
2094
	 * @return int Payment user ID.
2095
	 */
2096
	private function get_user_id() {
2097
		return apply_filters( 'give_payment_user_id', $this->user_id, $this->ID, $this );
2098
	}
2099
2100
	/**
2101
	 * Retrieve payment email.
2102
	 *
2103
	 * @since  1.5
2104
	 * @access private
2105
	 *
2106
	 * @return string Payment donor email.
2107
	 */
2108
	private function get_email() {
2109
		return apply_filters( 'give_payment_user_email', $this->email, $this->ID, $this );
2110
	}
2111
2112
	/**
2113
	 * Retrieve payment user info.
2114
	 *
2115
	 * @since  1.5
2116
	 * @access private
2117
	 *
2118
	 * @return array Payment user info.
2119
	 */
2120
	private function get_user_info() {
2121
		return apply_filters( 'give_payment_meta_user_info', $this->user_info, $this->ID, $this );
2122
	}
2123
2124
	/**
2125
	 * Retrieve payment billing address.
2126
	 *
2127
	 * @since  1.5
2128
	 * @access private
2129
	 *
2130
	 * @return array Payment billing address.
2131
	 */
2132
	private function get_address() {
2133
		return apply_filters( 'give_payment_address', $this->address, $this->ID, $this );
2134
	}
2135
2136
	/**
2137
	 * Retrieve payment key.
2138
	 *
2139
	 * @since  1.5
2140
	 * @access private
2141
	 *
2142
	 * @return string Payment key.
2143
	 */
2144
	private function get_key() {
2145
		return apply_filters( 'give_payment_key', $this->key, $this->ID, $this );
2146
	}
2147
2148
	/**
2149
	 * Retrieve payment form id
2150
	 *
2151
	 * @since  1.5
2152
	 * @access private
2153
	 *
2154
	 * @return string Payment form id
2155
	 */
2156
	private function get_form_id() {
2157
		return apply_filters( 'give_payment_form_id', $this->form_id, $this->ID, $this );
2158
	}
2159
2160
	/**
2161
	 * Retrieve payment number
2162
	 *
2163
	 * @since  1.5
2164
	 * @access private
2165
	 *
2166
	 * @return int|string Payment number
2167
	 */
2168
	private function get_number() {
2169
		return apply_filters( 'give_payment_number', $this->number, $this->ID, $this );
2170
	}
2171
2172
}
2173