process-donation.php ➔ give_get_donation_form_user()   F
last analyzed

Complexity

Conditions 29
Paths > 20000

Size

Total Lines 85

Duplication

Lines 9
Ratio 10.59 %

Importance

Changes 0
Metric Value
cc 29
nc 69142
nop 1
dl 9
loc 85
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Process Donation
4
 *
5
 * @package     Give
6
 * @subpackage  Functions
7
 * @copyright   Copyright (c) 2016, GiveWP
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
 * Process Donation Form
19
 *
20
 * Handles the donation form process.
21
 *
22
 * @access private
23
 * @since  1.0
24
 *
25
 * @throws ReflectionException Exception Handling.
26
 *
27
 * @return mixed
28
 */
29
function give_process_donation_form() {
30
31
	// Sanitize Posted Data.
32
	$post_data  = give_clean( $_POST ); // WPCS: input var ok, CSRF ok.
33
34
	// Check whether the form submitted via AJAX or not.
35
	$is_ajax = isset( $post_data['give_ajax'] );
36
37
	// Verify donation form nonce.
38
	if ( ! give_verify_donation_form_nonce( $post_data['give-form-hash'], $post_data['give-form-id'] ) ) {
39
		if ( $is_ajax ) {
40
			/**
41
			 * Fires when AJAX sends back errors from the donation form.
42
			 *
43
			 * @since 1.0
44
			 */
45
			do_action( 'give_ajax_donation_errors' );
46
			give_die();
47
		} else {
48
			give_send_back_to_checkout();
49
		}
50
	}
51
52
	/**
53
	 * Fires before processing the donation form.
54
	 *
55
	 * @since 1.0
56
	 */
57
	do_action( 'give_pre_process_donation' );
58
59
	// Validate the form $_POST data.
60
	$valid_data = give_donation_form_validate_fields();
61
62
	/**
63
	 * Fires after validating donation form fields.
64
	 *
65
	 * Allow you to hook to donation form errors.
66
	 *
67
	 * @since 1.0
68
	 *
69
	 * @param bool|array $valid_data Validate fields.
70
	 * @param array $deprecated Deprecated Since 2.0.2. Use $_POST instead.
71
	 */
72
	$deprecated = $post_data;
73
	do_action( 'give_checkout_error_checks', $valid_data, $deprecated );
74
75
	// Process the login form.
76
	if ( isset( $post_data['give_login_submit'] ) ) {
77
		give_process_form_login();
78
	}
79
80
	// Validate the user.
81
	$user = give_get_donation_form_user( $valid_data );
0 ignored issues
show
Bug introduced by
It seems like $valid_data defined by give_donation_form_validate_fields() on line 60 can also be of type boolean; however, give_get_donation_form_user() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
82
83
	if ( false === $valid_data || ! $user || give_get_errors() ) {
84
		if ( $is_ajax ) {
85
			/**
86
			 * Fires when AJAX sends back errors from the donation form.
87
			 *
88
			 * @since 1.0
89
			 */
90
			do_action( 'give_ajax_donation_errors' );
91
			give_die();
92
		} else {
93
			return false;
94
		}
95
	}
96
97
	// If AJAX send back success to proceed with form submission.
98
	if ( $is_ajax ) {
99
		echo 'success';
100
		give_die();
101
	}
102
103
	/**
104
	 * Fires action after donation form field validated.
105
	 *
106
	 * @since 2.2.0
107
	 */
108
	do_action( 'give_process_donation_after_validation' );
109
110
	// Setup user information.
111
	$user_info = array(
112
		'id'         => $user['user_id'],
113
		'title'      => $user['user_title'],
114
		'email'      => $user['user_email'],
115
		'first_name' => $user['user_first'],
116
		'last_name'  => $user['user_last'],
117
		'address'    => $user['address'],
118
	);
119
120
	$auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
121
122
	// Donation form ID.
123
	$form_id = isset( $post_data['give-form-id'] ) ? absint( $post_data['give-form-id'] ) : 0;
124
125
	$price = isset( $post_data['give-amount'] ) ?
126
		(float) apply_filters( 'give_donation_total', give_maybe_sanitize_amount( $post_data['give-amount'], array( 'currency' => give_get_currency( $form_id ) ) ) ) :
127
		'0.00';
128
	$purchase_key = strtolower( md5( $user['user_email'] . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'give', true ) ) );
129
130
	/**
131
	 * Update donation Purchase key.
132
	 *
133
	 * Use this filter to update default donation purchase key
134
	 * and add prefix in Invoice.
135
	 *
136
	 * @since 2.2.4
137
	 *
138
	 * @param string $purchase_key
139
	 * @param string $gateway
140
	 * @param string $purchase_key
141
	 *
142
	 * @return string $purchase_key
143
	 */
144
	$purchase_key = apply_filters(
145
		'give_donation_purchase_key',
146
		$purchase_key,
147
		$valid_data['gateway'],
148
		// Use this purchase key value if you want to generate custom donation purchase key
149
		// because donation purchase key editable by filters and you may get unedited donation purchase key.
150
		$purchase_key
151
	);
152
153
	// Setup donation information.
154
	$donation_data = array(
155
		'price'         => $price,
156
		'purchase_key'  => $purchase_key,
157
		'user_email'    => $user['user_email'],
158
		'date'          => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
159
		'user_info'     => stripslashes_deep( $user_info ),
160
		'post_data'     => $post_data,
161
		'gateway'       => $valid_data['gateway'],
162
		'card_info'     => $valid_data['cc_info'],
163
	);
164
165
	// Add the user data for hooks.
166
	$valid_data['user'] = $user;
167
168
	/**
169
	 * Fires before donation form gateway.
170
	 *
171
	 * Allow you to hook to donation form before the gateway.
172
	 *
173
	 * @since 1.0
174
	 *
175
	 * @param array      $post_data  Array of variables passed via the HTTP POST.
176
	 * @param array      $user_info  Array containing basic user information.
177
	 * @param bool|array $valid_data Validate fields.
178
	 */
179
	do_action( 'give_checkout_before_gateway', $post_data, $user_info, $valid_data );
180
181
	// Sanity check for price.
182
	if ( ! $donation_data['price'] ) {
183
		// Revert to manual.
184
		$donation_data['gateway'] = 'manual';
185
		$_POST['give-gateway']    = 'manual';
186
	}
187
188
	/**
189
	 * Allow the donation data to be modified before it is sent to the gateway.
190
	 *
191
	 * @since 1.7
192
	 */
193
	$donation_data = apply_filters( 'give_donation_data_before_gateway', $donation_data, $valid_data );
194
195
	// Setup the data we're storing in the donation session.
196
	$session_data = $donation_data;
197
198
	// Make sure credit card numbers are never stored in sessions.
199
	unset( $session_data['card_info']['card_number'] );
200
	unset( $session_data['post_data']['card_number'] );
201
202
	// Used for showing data to non logged-in users after donation, and for other plugins needing donation data.
203
	give_set_purchase_session( $session_data );
204
205
	// Send info to the gateway for payment processing.
206
	give_send_to_gateway( $donation_data['gateway'], $donation_data );
207
	give_die();
208
}
209
210
add_action( 'give_purchase', 'give_process_donation_form' );
211
add_action( 'wp_ajax_give_process_donation', 'give_process_donation_form' );
212
add_action( 'wp_ajax_nopriv_give_process_donation', 'give_process_donation_form' );
213
214
/**
215
 * Verify that when a logged in user makes a donation that the email address used doesn't belong to a different customer.
216
 * Note: only for internal use
217
 *
218
 * @see https://github.com/impress-org/give/issues/4025
219
 *
220
 * @since  1.7
221
 * @since  2.4.2 This function runs independently instead of give_checkout_error_checks hook and also edit donor email.
222
 *
223
 * @param  array $valid_data Validated data submitted for the donation.
224
 *
225
 * @return void
226
 */
227
function give_check_logged_in_user_for_existing_email( &$valid_data ) {
228
229
	// Verify that the email address belongs to this donor.
230
	if ( is_user_logged_in() ) {
231
232
		$donor = new Give_Donor( get_current_user_id(), true );
233
234
		// Bailout: check if wp user is existing donor or not.
235
		if ( ! $donor->id ) {
236
			return;
237
		}
238
239
		$submitted_email = strtolower( $valid_data['user_email'] );
240
241
		$donor_emails = array_map( 'strtolower', $donor->emails );
242
		$email_index  = array_search( $submitted_email, $donor_emails, true );
243
244
		// If donor matched with email then return set formatted email from database.
245
		if ( false !== $email_index ) {
246
			$valid_data['user_email'] = $donor->emails[ $email_index ];
247
248
			return;
249
		}
250
251
		// If this email address is not registered with this customer, see if it belongs to any other customer.
252
		$found_donor = new Give_Donor( $submitted_email );
253
254
		if ( $found_donor->id > 0 ) {
255
			give_set_error(
256
				'give-customer-email-exists',
257
				sprintf(
258
				/* translators: 1. Donor Email, 2. Submitted Email */
259
					__( 'You are logged in as %1$s, and are submitting a donation as %2$s, which is an existing donor. To ensure that the email address is tied to the correct donor, please submit this donation from a logged-out browser, or choose another email address.', 'give' ),
260
					$donor->email,
261
					$submitted_email
262
				)
263
			);
264
		}
265
	}
266
}
267
268
/**
269
 * Process the checkout login form
270
 *
271
 * @access private
272
 * @since  1.0
273
 *
274
 * @return void
275
 */
276
function give_process_form_login() {
277
278
	$is_ajax   = ! empty( $_POST['give_ajax'] ) ? give_clean( $_POST['give_ajax'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
279
	$referrer  = wp_get_referer();
280
	$user_data = give_donation_form_validate_user_login();
281
282
	if ( give_get_errors() || $user_data['user_id'] < 1 ) {
283
		if ( $is_ajax ) {
284
			/**
285
			 * Fires when AJAX sends back errors from the donation form.
286
			 *
287
			 * @since 1.0
288
			 */
289
			ob_start();
290
			do_action( 'give_ajax_donation_errors' );
291
			$message = ob_get_contents();
292
			ob_end_clean();
293
			wp_send_json_error( $message );
294
		} else {
295
			wp_safe_redirect( $referrer );
296
			exit;
297
		}
298
	}
299
300
	give_log_user_in( $user_data['user_id'], $user_data['user_login'], $user_data['user_pass'] );
301
302
	if ( $is_ajax ) {
303
		$message = Give_Notices::print_frontend_notice(
304
			sprintf(
305
				/* translators: %s: user first name */
306
				esc_html__( 'Welcome %s! You have successfully logged into your account.', 'give' ),
307
				( ! empty( $user_data['user_first'] ) ) ? $user_data['user_first'] : $user_data['user_login']
308
			),
309
			false,
310
			'success'
311
		);
312
313
		wp_send_json_success( $message );
314
	} else {
315
		wp_safe_redirect( $referrer );
316
	}
317
}
318
319
add_action( 'wp_ajax_give_process_donation_login', 'give_process_form_login' );
320
add_action( 'wp_ajax_nopriv_give_process_donation_login', 'give_process_form_login' );
321
322
/**
323
 * Donation Form Validate Fields.
324
 *
325
 * @access private
326
 * @since  1.0
327
 *
328
 * @return bool|array
329
 */
330
function give_donation_form_validate_fields() {
331
332
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
333
334
	// Validate Honeypot First.
335
	if ( ! empty( $post_data['give-honeypot'] ) ) {
336
		give_set_error( 'invalid_honeypot', esc_html__( 'Honeypot field detected. Go away bad bot!', 'give' ) );
337
	}
338
339
	// Check spam detect.
340
	if (
341
		isset( $post_data['action'] )
342
		&& give_is_setting_enabled( give_get_option( 'akismet_spam_protection' ) )
343
		&& give_is_spam_donation()
344
	) {
345
		give_set_error( 'spam_donation', __( 'This donation has been flagged as spam. Please try again.', 'give' ) );
346
	}
347
348
	// Start an array to collect valid data.
349
	$valid_data = array(
350
		'gateway'          => give_donation_form_validate_gateway(), // Gateway fallback (amount is validated here).
351
		'need_new_user'    => false,     // New user flag.
352
		'need_user_login'  => false,     // Login user flag.
353
		'logged_user_data' => array(),   // Logged user collected data.
354
		'new_user_data'    => array(),   // New user collected data.
355
		'login_user_data'  => array(),   // Login user collected data.
356
		'guest_user_data'  => array(),   // Guest user collected data.
357
		'cc_info'          => give_donation_form_validate_cc(), // Credit card info.
358
	);
359
360
	$form_id = (int) $post_data['give-form-id'];
361
362
	// Validate agree to terms.
363
	if ( give_is_terms_enabled( $form_id ) ) {
364
		give_donation_form_validate_agree_to_terms();
365
	}
366
367
	if ( is_user_logged_in() ) {
368
369
		// Collect logged in user data.
370
		$valid_data['logged_in_user'] = give_donation_form_validate_logged_in_user();
371
	} elseif (
372
		isset( $post_data['give-purchase-var'] )
373
		&& 'needs-to-register' === $post_data['give-purchase-var']
374
		&& ! empty( $post_data['give_create_account'] )
375
	) {
376
377
		// Set new user registration as required.
378
		$valid_data['need_new_user'] = true;
379
380
		// Validate new user data.
381
		$valid_data['new_user_data'] = give_donation_form_validate_new_user();
382
	} elseif (
383
		isset( $post_data['give-purchase-var'] )
384
		&& 'needs-to-login' === $post_data['give-purchase-var']
385
	) {
386
387
		// Set user login as required.
388
		$valid_data['need_user_login'] = true;
389
390
		// Validate users login info.
391
		$valid_data['login_user_data'] = give_donation_form_validate_user_login();
392
	} else {
393
394
		// Not registering or logging in, so setup guest user data.
395
		$valid_data['guest_user_data'] = give_donation_form_validate_guest_user();
396
	}
397
398
	// Return collected data.
399
	return $valid_data;
400
}
401
402
/**
403
 * Detect spam donation.
404
 *
405
 * @since 1.8.14
406
 *
407
 * @return bool|mixed
408
 */
409
function give_is_spam_donation() {
410
	$spam = false;
411
412
	$user_agent = (string) isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
413
414
	if ( strlen( $user_agent ) < 2 ) {
415
		$spam = true;
416
	}
417
418
	// Allow developer to customized Akismet spam detect API call and it's response.
419
	return apply_filters( 'give_spam', $spam );
420
}
421
422
/**
423
 * Donation Form Validate Gateway
424
 *
425
 * Validate the gateway and donation amount.
426
 *
427
 * @access private
428
 * @since  1.0
429
 *
430
 * @return string
431
 */
432
function give_donation_form_validate_gateway() {
433
434
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
435
	$form_id   = ! empty( $post_data['give-form-id'] ) ? $post_data['give-form-id'] : 0;
436
	$amount    = ! empty( $post_data['give-amount'] ) ? give_maybe_sanitize_amount( $post_data['give-amount'] ) : 0;
437
	$gateway   = ! empty( $post_data['give-gateway'] ) ? $post_data['give-gateway'] : 0;
438
439
	// Bailout, if payment gateway is not submitted with donation form data.
440
	if ( empty( $gateway ) ) {
441
442
		give_set_error( 'empty_gateway', __( 'The donation form will process with a valid payment gateway.', 'give' ) );
443
444
	} elseif ( ! give_is_gateway_active( $gateway ) ) {
445
446
		give_set_error( 'invalid_gateway', __( 'The selected payment gateway is not enabled.', 'give' ) );
447
448
	} elseif ( empty( $amount ) ) {
449
450
		give_set_error( 'invalid_donation_amount', __( 'Please insert a valid donation amount.', 'give' ) );
451
452
	} elseif ( ! give_verify_minimum_price( 'minimum' ) ) {
453
454
		give_set_error(
455
			'invalid_donation_minimum',
456
			sprintf(
457
				/* translators: %s: minimum donation amount */
458
				__( 'This form has a minimum donation amount of %s.', 'give' ),
459
				give_currency_filter(
460
					give_format_amount( give_get_form_minimum_price( $form_id ),
0 ignored issues
show
Documentation introduced by
give_get_form_minimum_price($form_id) is of type false|double, 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...
461
						array(
462
							'sanitize' => false,
463
						)
464
					)
465
				)
466
			)
467
		);
468
	} elseif ( ! give_verify_minimum_price( 'maximum' ) ) {
469
470
		give_set_error(
471
			'invalid_donation_maximum',
472
			sprintf(
473
				/* translators: %s: Maximum donation amount */
474
				__( 'This form has a maximum donation amount of %s.', 'give' ),
475
				give_currency_filter(
476
					give_format_amount( give_get_form_maximum_price( $form_id ),
0 ignored issues
show
Documentation introduced by
give_get_form_maximum_price($form_id) is of type false|double, 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...
477
						array(
478
							'sanitize' => false,
479
						)
480
					)
481
				)
482
			)
483
		);
484
	} // End if().
485
486
	return $gateway;
487
488
}
489
490
/**
491
 * Donation Form Validate Minimum or Maximum Donation Amount
492
 *
493
 * @access private
494
 * @since  1.3.6
495
 * @since  2.1 Added support for give maximum amount.
496
 * @since  2.1.3 Added new filter to modify the return value.
497
 *
498
 * @param string $amount_range Which amount needs to verify? minimum or maximum.
499
 *
500
 * @return bool
501
 */
502
function give_verify_minimum_price( $amount_range = 'minimum' ) {
503
504
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
505
	$form_id   = ! empty( $post_data['give-form-id'] ) ? $post_data['give-form-id'] : 0;
506
	$amount    = ! empty( $post_data['give-amount'] ) ? give_maybe_sanitize_amount( $post_data['give-amount'], array( 'currency' => give_get_currency( $form_id ) ) ) : 0;
507
	$price_id  = isset( $post_data['give-price-id'] ) ? absint( $post_data['give-price-id'] ) : '';
508
509
	$variable_prices = give_has_variable_prices( $form_id );
510
	$price_ids       = array_map( 'absint', give_get_variable_price_ids( $form_id ) );
511
	$verified_stat   = false;
512
513
	if ( $variable_prices && in_array( $price_id, $price_ids, true ) ) {
514
515
		$price_level_amount = give_get_price_option_amount( $form_id, $price_id );
516
517
		if ( $price_level_amount == $amount ) {
518
			$verified_stat = true;
519
		}
520
	}
521
522
	if ( ! $verified_stat ) {
523
		switch ( $amount_range ) {
524
			case 'minimum' :
525
				$verified_stat = ( give_get_form_minimum_price( $form_id ) > $amount ) ? false : true;
526
				break;
527
			case 'maximum' :
528
				$verified_stat = ( give_get_form_maximum_price( $form_id ) < $amount ) ? false : true;
529
				break;
530
		}
531
	}
532
533
	/**
534
	 * Filter the verify amount
535
	 *
536
	 * @since 2.1.3
537
	 *
538
	 * @param bool    $verified_stat Was verification passed or not?
539
	 * @param string  $amount_range  Type of the amount.
540
	 * @param integer $form_id       Give Donation Form ID.
541
	 */
542
	return apply_filters( 'give_verify_minimum_maximum_price', $verified_stat, $amount_range, $form_id );
543
}
544
545
/**
546
 * Donation form validate agree to "Terms and Conditions".
547
 *
548
 * @access private
549
 * @since  1.0
550
 *
551
 * @return void
552
 */
553
function give_donation_form_validate_agree_to_terms() {
554
555
	$agree_to_terms = ! empty( $_POST['give_agree_to_terms'] ) ? give_clean( $_POST['give_agree_to_terms'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
556
557
	// Proceed only, if donor agreed to terms.
558
	if ( ! $agree_to_terms ) {
559
560
		// User did not agree.
561
		give_set_error( 'agree_to_terms', apply_filters( 'give_agree_to_terms_text', __( 'You must agree to the terms and conditions.', 'give' ) ) );
562
	}
563
}
564
565
/**
566
 * Donation Form Required Fields.
567
 *
568
 * @access private
569
 * @since  1.0
570
 *
571
 * @param  int $form_id Donation Form ID.
572
 *
573
 * @return array
574
 */
575
function give_get_required_fields( $form_id ) {
576
577
	$posted_data  = give_clean( filter_input_array( INPUT_POST ) );
578
	$payment_mode = give_get_chosen_gateway( $form_id );
579
580
	$required_fields = array(
581
		'give_email' => array(
582
			'error_id'      => 'invalid_email',
583
			'error_message' => __( 'Please enter a valid email address.', 'give' ),
584
		),
585
		'give_first' => array(
586
			'error_id'      => 'invalid_first_name',
587
			'error_message' => __( 'Please enter your first name.', 'give' ),
588
		),
589
	);
590
591
	$name_title_prefix = give_is_name_title_prefix_required( $form_id );
592
	if ( $name_title_prefix ) {
593
		$required_fields['give_title'] = array(
594
			'error_id'      => 'invalid_title',
595
			'error_message' => __( 'Please enter your title.', 'give' ),
596
		);
597
	}
598
	
599
	// If credit card fields related actions exists then check for the cc fields validations.
600
	if (
601
		has_action("give_{$payment_mode}_cc_form", 'give_get_cc_form' ) ||
602
		has_action('give_cc_form', 'give_get_cc_form' )
603
	) {
604
		
605
		// Validate card number field for empty check.
606 View Code Duplication
		if (
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...
607
			isset( $posted_data['card_number'] ) &&
608
			empty( $posted_data['card_number'] )
609
		) {
610
			$required_fields['card_number'] = array(
611
				'error_id'      => 'empty_card_number',
612
				'error_message' => __( 'Please enter a credit card number.', 'give' ),
613
			);
614
		}
615
		
616
		// Validate card cvc field for empty check.
617 View Code Duplication
		if (
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...
618
			isset( $posted_data['card_cvc'] ) &&
619
			empty( $posted_data['card_cvc'] )
620
		) {
621
			$required_fields['card_cvc'] = array(
622
				'error_id'      => 'empty_card_cvc',
623
				'error_message' => __( 'Please enter a credit card CVC information.', 'give' ),
624
			);
625
		}
626
		
627
		// Validate card name field for empty check.
628 View Code Duplication
		if (
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...
629
			isset( $posted_data['card_name'] ) &&
630
			empty( $posted_data['card_name'] )
631
		) {
632
			$required_fields['card_name'] = array(
633
				'error_id'      => 'empty_card_name',
634
				'error_message' => __( 'Please enter a name of your credit card account holder.', 'give' ),
635
			);
636
		}
637
		
638
		// Validate card expiry field for empty check.
639 View Code Duplication
		if (
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...
640
			isset( $posted_data['card_expiry'] ) &&
641
			empty( $posted_data['card_expiry'] )
642
		) {
643
			$required_fields['card_expiry'] = array(
644
				'error_id'      => 'empty_card_expiry',
645
				'error_message' => __( 'Please enter a credit card expiry date.', 'give' ),
646
			);
647
		}
648
	}
649
650
	$require_address = give_require_billing_address( $payment_mode );
651
652
	if ( $require_address ) {
653
		$required_fields['card_address']    = array(
654
			'error_id'      => 'invalid_card_address',
655
			'error_message' => __( 'Please enter your primary billing address.', 'give' ),
656
		);
657
		$required_fields['card_zip']        = array(
658
			'error_id'      => 'invalid_zip_code',
659
			'error_message' => __( 'Please enter your zip / postal code.', 'give' ),
660
		);
661
		$required_fields['card_city']       = array(
662
			'error_id'      => 'invalid_city',
663
			'error_message' => __( 'Please enter your billing city.', 'give' ),
664
		);
665
		$required_fields['billing_country'] = array(
666
			'error_id'      => 'invalid_country',
667
			'error_message' => __( 'Please select your billing country.', 'give' ),
668
		);
669
670
671
		$required_fields['card_state'] = array(
672
			'error_id'      => 'invalid_state',
673
			'error_message' => __( 'Please enter billing state / province / County.', 'give' ),
674
		);
675
676
		$country = ! empty( $_POST['billing_country'] ) ? give_clean( $_POST['billing_country'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
677
678
		// Check if billing country already exists.
679
		if ( $country ) {
680
681
			// Check if states is empty or not.
682
			if ( array_key_exists( $country, give_states_not_required_country_list() ) ) {
683
				// If states is empty remove the required fields of state in billing cart.
684
				unset( $required_fields['card_state'] );
685
			}
686
687
			// Check if city is empty or not.
688
			if ( array_key_exists( $country, give_city_not_required_country_list() ) ) {
689
				// If states is empty remove the required fields of city in billing cart.
690
				unset( $required_fields['card_city'] );
691
			}
692
		}
693
	} // End if().
694
695
	if ( give_is_company_field_enabled( $form_id ) ) {
696
		$form_option    = give_get_meta( $form_id, '_give_company_field', true );
697
		$global_setting = give_get_option( 'company_field' );
698
699
		$is_company_field_required = false;
700
701
		if ( ! empty( $form_option ) && give_is_setting_enabled( $form_option, array( 'required' ) ) ) {
0 ignored issues
show
Documentation introduced by
array('required') is of type array<integer,string,{"0":"string"}>, but the function expects a string|null.

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...
702
			$is_company_field_required = true;
703
704
		} elseif ( 'global' === $form_option && give_is_setting_enabled( $global_setting, array( 'required' ) ) ) {
0 ignored issues
show
Documentation introduced by
array('required') is of type array<integer,string,{"0":"string"}>, but the function expects a string|null.

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...
705
			$is_company_field_required = true;
706
707
		} elseif ( empty( $form_option ) && give_is_setting_enabled( $global_setting, array( 'required' ) ) ) {
0 ignored issues
show
Documentation introduced by
array('required') is of type array<integer,string,{"0":"string"}>, but the function expects a string|null.

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...
708
			$is_company_field_required = true;
709
710
		}
711
712
		if ( $is_company_field_required ) {
713
			$required_fields['give_company_name'] = array(
714
				'error_id'      => 'invalid_company',
715
				'error_message' => __( 'Please enter Company Name.', 'give' ),
716
			);
717
		}
718
	}
719
720
	/**
721
	 * Filters the donation form required field.
722
	 *
723
	 * @since 1.7
724
	 */
725
	$required_fields = apply_filters( 'give_donation_form_required_fields', $required_fields, $form_id );
726
727
	return $required_fields;
728
729
}
730
731
/**
732
 * Check if the Billing Address is required
733
 *
734
 * @since  1.0.1
735
 *
736
 * @param string $payment_mode Payment Mode.
737
 *
738
 * @return bool
739
 */
740
function give_require_billing_address( $payment_mode ) {
741
742
	$return          = false;
743
	$billing_country = ! empty( $_POST['billing_country'] ) ? give_clean( $_POST['billing_country'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
744
745
	if ( $billing_country || did_action( "give_{$payment_mode}_cc_form" ) || did_action( 'give_cc_form' ) ) {
746
		$return = true;
747
	}
748
749
	// Let payment gateways and other extensions determine if address fields should be required.
750
	return apply_filters( 'give_require_billing_address', $return );
751
752
}
753
754
/**
755
 * Donation Form Validate Logged In User.
756
 *
757
 * @access private
758
 * @since  1.0
759
 *
760
 * @return array
761
 */
762
function give_donation_form_validate_logged_in_user() {
763
764
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
765
	$user_id   = get_current_user_id();
766
	$form_id   = ! empty( $post_data['give-form-id'] ) ? $post_data['give-form-id'] : 0;
767
768
	// Start empty array to collect valid user data.
769
	$valid_user_data = array(
770
771
		// Assume there will be errors.
772
		'user_id' => - 1,
773
	);
774
775
	// Proceed only, if valid $user_id found.
776
	if ( $user_id > 0 ) {
777
778
		// Get the logged in user data.
779
		$user_data = get_userdata( $user_id );
780
781
		// Validate Required Form Fields.
782
		give_validate_required_form_fields( $form_id );
783
784
		// Verify data.
785
		if ( is_object( $user_data ) && $user_data->ID > 0 ) {
786
			// Collected logged in user data.
787
			$valid_user_data = array(
788
				'user_id'    => $user_id,
789
				'user_email' => ! empty( $post_data['give_email'] )
790
					? sanitize_email( $post_data['give_email'] )
791
					: $user_data->user_email,
792
				'user_first' => ! empty( $post_data['give_first'] )
793
					? $post_data['give_first']
794
					: $user_data->first_name,
795
				'user_last'  => ! empty( $post_data['give_last'] )
796
					? $post_data['give_last']
797
					: $user_data->last_name,
798
			);
799
800
			// Validate essential form fields.
801
			give_donation_form_validate_name_fields( $post_data );
0 ignored issues
show
Bug introduced by
It seems like $post_data defined by give_clean($_POST) on line 764 can also be of type string; however, give_donation_form_validate_name_fields() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
802
803
			give_check_logged_in_user_for_existing_email( $valid_user_data );
804
805
			if ( ! is_email( $valid_user_data['user_email'] ) ) {
806
				give_set_error( 'email_invalid', esc_html__( 'Invalid email.', 'give' ) );
807
			}
808
		} else {
809
810
			// Set invalid user information error.
811
			give_set_error( 'invalid_user', esc_html__( 'The user information is invalid.', 'give' ) );
812
		}
813
	}
814
815
	// Return user data.
816
	return $valid_user_data;
817
}
818
819
/**
820
 * Donate Form Validate New User
821
 *
822
 * @access private
823
 * @since  1.0
824
 *
825
 * @return array
826
 */
827
function give_donation_form_validate_new_user() {
828
	// Default user data.
829
	$auto_generated_password = wp_generate_password();
830
	$default_user_data       = array(
831
		'give-form-id'           => '',
832
		'user_id'                => - 1, // Assume there will be errors.
833
		'user_first'             => '',
834
		'user_last'              => '',
835
		'give_user_login'        => false,
836
		'give_email'             => false,
837
		'give_user_pass'         => $auto_generated_password,
838
		'give_user_pass_confirm' => $auto_generated_password,
839
	);
840
841
	// Get data.
842
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
843
	$user_data = wp_parse_args( $post_data, $default_user_data );
844
845
	$form_id = absint( $user_data['give-form-id'] );
846
	$nonce   = ! empty( $post_data['give-form-user-register-hash'] ) ? $post_data['give-form-user-register-hash'] : '';
847
848
	// Validate user creation nonce.
849
	if ( ! wp_verify_nonce( $nonce, "give_form_create_user_nonce_{$form_id}" ) ) {
850
		give_set_error( 'invalid_nonce', __( 'We\'re unable to recognize your session. Please refresh the screen to try again; otherwise contact your website administrator for assistance.', 'give' ) );
851
	}
852
853
	$registering_new_user = false;
854
855
	give_donation_form_validate_name_fields( $user_data );
856
857
	// Start an empty array to collect valid user data.
858
	$valid_user_data = array(
859
860
		// Assume there will be errors.
861
		'user_id'    => - 1,
862
863
		// Get first name.
864
		'user_first' => $user_data['give_first'],
865
866
		// Get last name.
867
		'user_last'  => $user_data['give_last'],
868
869
		// Get Password.
870
		'user_pass'  => $user_data['give_user_pass'],
871
	);
872
873
	// Validate Required Form Fields.
874
	give_validate_required_form_fields( $form_id );
875
876
	// Set Email as Username.
877
	$valid_user_data['user_login'] = $user_data['give_email'];
878
879
	// Check if we have an email to verify.
880
	if ( give_validate_user_email( $user_data['give_email'], $registering_new_user ) ) {
881
		$valid_user_data['user_email'] = $user_data['give_email'];
882
	}
883
884
	return $valid_user_data;
885
}
886
887
/**
888
 * Donation Form Validate User Login
889
 *
890
 * @access private
891
 * @since  1.0
892
 *
893
 * @return array
894
 */
895
function give_donation_form_validate_user_login() {
896
897
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
898
899
	// Start an array to collect valid user data.
900
	$valid_user_data = array(
901
902
		// Assume there will be errors.
903
		'user_id' => - 1,
904
	);
905
906
	// Bailout, if Username is empty.
907
	if ( empty( $post_data['give_user_login'] ) ) {
908
		give_set_error( 'must_log_in', __( 'Please enter your username or email to log in.', 'give' ) );
909
910
		return $valid_user_data;
911
	}
912
913
	$give_user_login = strip_tags( $post_data['give_user_login'] );
914
	if ( is_email( $give_user_login ) ) {
915
		// Get the user data by email.
916
		$user_data = get_user_by( 'email', $give_user_login );
917
	} else {
918
		// Get the user data by login.
919
		$user_data = get_user_by( 'login', $give_user_login );
920
	}
921
922
	// Check if user exists.
923
	if ( $user_data ) {
924
925
		// Get password.
926
		$user_pass = ! empty( $post_data['give_user_pass'] ) ? $post_data['give_user_pass'] : false;
927
928
		// Check user_pass.
929
		if ( $user_pass ) {
930
931
			// Check if password is valid.
932
			if ( ! wp_check_password( $user_pass, $user_data->user_pass, $user_data->ID ) ) {
933
934
				$current_page_url = site_url() . '/' . get_page_uri();
935
936
				// Incorrect password.
937
				give_set_error(
938
					'password_incorrect',
939
					sprintf(
940
						'%1$s <a href="%2$s">%3$s</a>',
941
						__( 'The password you entered is incorrect.', 'give' ),
942
						wp_lostpassword_url( $current_page_url ),
943
						__( 'Reset Password', 'give' )
944
					)
945
				);
946
947
			} else {
948
949
				// Repopulate the valid user data array.
950
				$valid_user_data = array(
951
					'user_id'    => $user_data->ID,
952
					'user_login' => $user_data->user_login,
953
					'user_email' => $user_data->user_email,
954
					'user_first' => $user_data->first_name,
955
					'user_last'  => $user_data->last_name,
956
					'user_pass'  => $user_pass,
957
				);
958
			}
959
		} else {
960
			// Empty password.
961
			give_set_error( 'password_empty', __( 'Enter a password.', 'give' ) );
962
		}
963
	} else {
964
		// No username.
965
		give_set_error( 'username_incorrect', __( 'The username you entered does not exist.', 'give' ) );
966
	} // End if().
967
968
	return $valid_user_data;
969
}
970
971
/**
972
 * Donation Form Validate Guest User
973
 *
974
 * @access private
975
 * @since  1.0
976
 *
977
 * @return array
978
 */
979
function give_donation_form_validate_guest_user() {
980
981
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
982
	$form_id   = ! empty( $post_data['give-form-id'] ) ? $post_data['give-form-id'] : 0;
983
984
	// Start an array to collect valid user data.
985
	$valid_user_data = array(
986
		// Set a default id for guests.
987
		'user_id' => 0,
988
	);
989
990
	// Validate name fields.
991
	give_donation_form_validate_name_fields( $post_data );
0 ignored issues
show
Bug introduced by
It seems like $post_data defined by give_clean($_POST) on line 981 can also be of type string; however, give_donation_form_validate_name_fields() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
992
993
	// Validate Required Form Fields.
994
	give_validate_required_form_fields( $form_id );
995
996
	// Get the guest email.
997
	$guest_email = ! empty( $post_data['give_email'] ) ? $post_data['give_email'] : false;
998
999
	// Check email.
1000
	if ( $guest_email && strlen( $guest_email ) > 0 ) {
1001
1002
		// Validate email.
1003
		if ( ! is_email( $guest_email ) ) {
1004
1005
			// Invalid email.
1006
			give_set_error( 'email_invalid', __( 'Invalid email.', 'give' ) );
1007
1008
		} else {
1009
1010
			// All is good to go.
1011
			$valid_user_data['user_email'] = $guest_email;
1012
1013
			// Get user_id from donor if exist.
1014
			$donor = new Give_Donor( $guest_email );
1015
1016
			if ( $donor->id ) {
1017
				$donor_email_index = array_search(
1018
					strtolower( $guest_email ),
1019
					array_map( 'strtolower', $donor->emails ),
1020
					true
1021
				);
1022
1023
				$valid_user_data['user_id'] = $donor->user_id;
1024
1025
				// Set email to original format.
1026
				// @see https://github.com/impress-org/give/issues/4025
1027
				$valid_user_data['user_email'] = $donor->emails[ $donor_email_index ];
1028
			}
1029
		}
1030
	} else {
1031
		// No email.
1032
		give_set_error( 'email_empty', __( 'Enter an email.', 'give' ) );
1033
	}
1034
1035
	return $valid_user_data;
1036
}
1037
1038
/**
1039
 * Register And Login New User
1040
 *
1041
 * @param array $user_data User Data.
1042
 *
1043
 * @access  private
1044
 * @since   1.0
1045
 *
1046
 * @return  integer
1047
 */
1048
function give_register_and_login_new_user( $user_data = array() ) {
1049
	// Verify the array.
1050
	if ( empty( $user_data ) ) {
1051
		return - 1;
1052
	}
1053
1054
	if ( give_get_errors() ) {
1055
		return - 1;
1056
	}
1057
1058
	$user_args = apply_filters( 'give_insert_user_args', array(
1059
		'user_login'      => isset( $user_data['user_login'] ) ? $user_data['user_login'] : '',
1060
		'user_pass'       => isset( $user_data['user_pass'] ) ? $user_data['user_pass'] : '',
1061
		'user_email'      => isset( $user_data['user_email'] ) ? $user_data['user_email'] : '',
1062
		'first_name'      => isset( $user_data['user_first'] ) ? $user_data['user_first'] : '',
1063
		'last_name'       => isset( $user_data['user_last'] ) ? $user_data['user_last'] : '',
1064
		'user_registered' => date( 'Y-m-d H:i:s' ),
1065
		'role'            => give_get_option( 'donor_default_user_role', 'give_donor' ),
1066
	), $user_data );
1067
1068
	// Insert new user.
1069
	$user_id = wp_insert_user( $user_args );
1070
1071
	// Validate inserted user.
1072
	if ( is_wp_error( $user_id ) ) {
1073
		return - 1;
1074
	}
1075
1076
	// Allow themes and plugins to filter the user data.
1077
	$user_data = apply_filters( 'give_insert_user_data', $user_data, $user_args );
1078
1079
	/**
1080
	 * Fires after inserting user.
1081
	 *
1082
	 * @since 1.0
1083
	 *
1084
	 * @param int $user_id User id.
1085
	 * @param array $user_data Array containing user data.
1086
	 */
1087
	do_action( 'give_insert_user', $user_id, $user_data );
1088
1089
	/**
1090
	 * Filter allow user to alter if user when to login or not when user is register for the first time.
1091
	 *
1092
	 * @since 1.8.13
1093
	 *
1094
	 * return bool True if login with registration and False if only want to register.
1095
	 */
1096
	if ( true === (bool) apply_filters( 'give_log_user_in_on_register', true ) ) {
1097
		// Login new user.
1098
		give_log_user_in( $user_id, $user_data['user_login'], $user_data['user_pass'] );
1099
	}
1100
1101
	// Return user id.
1102
	return $user_id;
1103
}
1104
1105
/**
1106
 * Get Donation Form User
1107
 *
1108
 * @param array $valid_data Valid Data.
1109
 *
1110
 * @access  private
1111
 * @since   1.0
1112
 *
1113
 * @return  array|bool
1114
 */
1115
function give_get_donation_form_user( $valid_data = array() ) {
1116
1117
	// Initialize user.
1118
	$user      = false;
1119
	$is_ajax   = defined( 'DOING_AJAX' ) && DOING_AJAX;
1120
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
1121
1122
	if ( $is_ajax ) {
1123
1124
		// Do not create or login the user during the ajax submission (check for errors only).
1125
		return true;
1126
	} elseif ( is_user_logged_in() ) {
1127
1128
		// Set the valid user as the logged in collected data.
1129
		$user = $valid_data['logged_in_user'];
1130
	} elseif ( true === $valid_data['need_new_user'] || true === $valid_data['need_user_login'] ) {
1131
1132
		// New user registration.
1133
		if ( true === $valid_data['need_new_user'] ) {
1134
1135
			// Set user.
1136
			$user = $valid_data['new_user_data'];
1137
1138
			// Register and login new user.
1139
			$user['user_id'] = give_register_and_login_new_user( $user );
1140
1141
		} elseif ( true === $valid_data['need_user_login'] && ! $is_ajax ) {
1142
1143
			/**
1144
			 * The login form is now processed in the give_process_donation_login() function.
1145
			 * This is still here for backwards compatibility.
1146
			 * This also allows the old login process to still work if a user removes the checkout login submit button.
1147
			 *
1148
			 * This also ensures that the donor is logged in correctly if they click "Donation" instead of submitting the login form, meaning the donor is logged in during the donation process.
1149
			 */
1150
			$user = $valid_data['login_user_data'];
1151
1152
			// Login user.
1153
			give_log_user_in( $user['user_id'], $user['user_login'], $user['user_pass'] );
1154
		}
1155
	} // End if().
1156
1157
	// Check guest checkout.
1158
	if ( false === $user && false === give_logged_in_only( $post_data['give-form-id'] ) ) {
1159
1160
		// Set user.
1161
		$user = $valid_data['guest_user_data'];
1162
	}
1163
1164
	// Verify we have an user.
1165
	if ( false === $user || empty( $user ) ) {
1166
		return false;
1167
	}
1168
1169
	// Get user first name.
1170 View Code Duplication
	if ( ! isset( $user['user_first'] ) || strlen( trim( $user['user_first'] ) ) < 1 ) {
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...
1171
		$user['user_first'] = isset( $post_data['give_first'] ) ? strip_tags( trim( $post_data['give_first'] ) ) : '';
1172
	}
1173
1174
	// Get user last name.
1175 View Code Duplication
	if ( ! isset( $user['user_last'] ) || strlen( trim( $user['user_last'] ) ) < 1 ) {
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...
1176
		$user['user_last'] = isset( $post_data['give_last'] ) ? strip_tags( trim( $post_data['give_last'] ) ) : '';
1177
	}
1178
1179
	// Add Title Prefix to user information.
1180 View Code Duplication
	if ( empty( $user['user_title'] ) || strlen( trim( $user['user_title'] ) ) < 1 ) {
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...
1181
		$user['user_title'] = ! empty( $post_data['give_title'] ) ? strip_tags( trim( $post_data['give_title'] ) ) : '';
1182
	}
1183
1184
	// Get the user's billing address details.
1185
	$user['address']            = array();
1186
	$user['address']['line1']   = ! empty( $post_data['card_address'] ) ? $post_data['card_address'] : false;
1187
	$user['address']['line2']   = ! empty( $post_data['card_address_2'] ) ? $post_data['card_address_2'] : false;
1188
	$user['address']['city']    = ! empty( $post_data['card_city'] ) ? $post_data['card_city'] : false;
1189
	$user['address']['state']   = ! empty( $post_data['card_state'] ) ? $post_data['card_state'] : false;
1190
	$user['address']['zip']     = ! empty( $post_data['card_zip'] ) ? $post_data['card_zip'] : false;
1191
	$user['address']['country'] = ! empty( $post_data['billing_country'] ) ? $post_data['billing_country'] : false;
1192
1193
	if ( empty( $user['address']['country'] ) ) {
1194
		$user['address'] = false;
1195
	} // End if().
1196
1197
	// Return valid user.
1198
	return $user;
1199
}
1200
1201
/**
1202
 * Validates the credit card info.
1203
 *
1204
 * @access  private
1205
 * @since   1.0
1206
 *
1207
 * @return  array
1208
 */
1209
function give_donation_form_validate_cc() {
1210
1211
	$card_data = give_get_donation_cc_info();
1212
1213
	// Validate the card zip.
1214
	if ( ! empty( $card_data['card_zip'] ) ) {
1215
		if ( ! give_donation_form_validate_cc_zip( $card_data['card_zip'], $card_data['card_country'] ) ) {
1216
			give_set_error( 'invalid_cc_zip', __( 'The zip / postal code you entered for your billing address is invalid.', 'give' ) );
1217
		}
1218
	}
1219
1220
	// Ensure no spaces.
1221
	if ( ! empty( $card_data['card_number'] ) ) {
1222
		$card_data['card_number'] = str_replace( '+', '', $card_data['card_number'] ); // no "+" signs.
1223
		$card_data['card_number'] = str_replace( ' ', '', $card_data['card_number'] ); // No spaces.
1224
	}
1225
1226
	// This should validate card numbers at some point too.
1227
	return $card_data;
1228
}
1229
1230
/**
1231
 * Get credit card info.
1232
 *
1233
 * @access private
1234
 * @since  1.0
1235
 *
1236
 * @return array
1237
 */
1238
function give_get_donation_cc_info() {
1239
1240
	// Sanitize the values submitted with donation form.
1241
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
1242
1243
	$cc_info                   = array();
1244
	$cc_info['card_name']      = ! empty( $post_data['card_name'] ) ? $post_data['card_name'] : '';
1245
	$cc_info['card_number']    = ! empty( $post_data['card_number'] ) ? $post_data['card_number'] : '';
1246
	$cc_info['card_cvc']       = ! empty( $post_data['card_cvc'] ) ? $post_data['card_cvc'] : '';
1247
	$cc_info['card_exp_month'] = ! empty( $post_data['card_exp_month'] ) ? $post_data['card_exp_month'] : '';
1248
	$cc_info['card_exp_year']  = ! empty( $post_data['card_exp_year'] ) ? $post_data['card_exp_year'] : '';
1249
	$cc_info['card_address']   = ! empty( $post_data['card_address'] ) ? $post_data['card_address'] : '';
1250
	$cc_info['card_address_2'] = ! empty( $post_data['card_address_2'] ) ? $post_data['card_address_2'] : '';
1251
	$cc_info['card_city']      = ! empty( $post_data['card_city'] ) ? $post_data['card_city'] : '';
1252
	$cc_info['card_state']     = ! empty( $post_data['card_state'] ) ? $post_data['card_state'] : '';
1253
	$cc_info['card_country']   = ! empty( $post_data['billing_country'] ) ? $post_data['billing_country'] : '';
1254
	$cc_info['card_zip']       = ! empty( $post_data['card_zip'] ) ? $post_data['card_zip'] : '';
1255
1256
	// Return cc info.
1257
	return $cc_info;
1258
}
1259
1260
/**
1261
 * Validate zip code based on country code
1262
 *
1263
 * @since  1.0
1264
 *
1265
 * @param int    $zip          ZIP Code.
1266
 * @param string $country_code Country Code.
1267
 *
1268
 * @return bool|mixed
1269
 */
1270
function give_donation_form_validate_cc_zip( $zip = 0, $country_code = '' ) {
1271
	$ret = false;
1272
1273
	if ( empty( $zip ) || empty( $country_code ) ) {
1274
		return $ret;
1275
	}
1276
1277
	$country_code = strtoupper( $country_code );
1278
1279
	$zip_regex = array(
1280
		'AD' => 'AD\d{3}',
1281
		'AM' => '(37)?\d{4}',
1282
		'AR' => '^([A-Z]{1}\d{4}[A-Z]{3}|[A-Z]{1}\d{4}|\d{4})$',
1283
		'AS' => '96799',
1284
		'AT' => '\d{4}',
1285
		'AU' => '^(0[289][0-9]{2})|([1345689][0-9]{3})|(2[0-8][0-9]{2})|(290[0-9])|(291[0-4])|(7[0-4][0-9]{2})|(7[8-9][0-9]{2})$',
1286
		'AX' => '22\d{3}',
1287
		'AZ' => '\d{4}',
1288
		'BA' => '\d{5}',
1289
		'BB' => '(BB\d{5})?',
1290
		'BD' => '\d{4}',
1291
		'BE' => '^[1-9]{1}[0-9]{3}$',
1292
		'BG' => '\d{4}',
1293
		'BH' => '((1[0-2]|[2-9])\d{2})?',
1294
		'BM' => '[A-Z]{2}[ ]?[A-Z0-9]{2}',
1295
		'BN' => '[A-Z]{2}[ ]?\d{4}',
1296
		'BR' => '\d{5}[\-]?\d{3}',
1297
		'BY' => '\d{6}',
1298
		'CA' => '^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$',
1299
		'CC' => '6799',
1300
		'CH' => '^[1-9][0-9][0-9][0-9]$',
1301
		'CK' => '\d{4}',
1302
		'CL' => '\d{7}',
1303
		'CN' => '\d{6}',
1304
		'CR' => '\d{4,5}|\d{3}-\d{4}',
1305
		'CS' => '\d{5}',
1306
		'CV' => '\d{4}',
1307
		'CX' => '6798',
1308
		'CY' => '\d{4}',
1309
		'CZ' => '\d{3}[ ]?\d{2}',
1310
		'DE' => '\b((?:0[1-46-9]\d{3})|(?:[1-357-9]\d{4})|(?:[4][0-24-9]\d{3})|(?:[6][013-9]\d{3}))\b',
1311
		'DK' => '^([D-d][K-k])?( |-)?[1-9]{1}[0-9]{3}$',
1312
		'DO' => '\d{5}',
1313
		'DZ' => '\d{5}',
1314
		'EC' => '([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?',
1315
		'EE' => '\d{5}',
1316
		'EG' => '\d{5}',
1317
		'ES' => '^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$',
1318
		'ET' => '\d{4}',
1319
		'FI' => '\d{5}',
1320
		'FK' => 'FIQQ 1ZZ',
1321
		'FM' => '(9694[1-4])([ \-]\d{4})?',
1322
		'FO' => '\d{3}',
1323
		'FR' => '^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$',
1324
		'GE' => '\d{4}',
1325
		'GF' => '9[78]3\d{2}',
1326
		'GL' => '39\d{2}',
1327
		'GN' => '\d{3}',
1328
		'GP' => '9[78][01]\d{2}',
1329
		'GR' => '\d{3}[ ]?\d{2}',
1330
		'GS' => 'SIQQ 1ZZ',
1331
		'GT' => '\d{5}',
1332
		'GU' => '969[123]\d([ \-]\d{4})?',
1333
		'GW' => '\d{4}',
1334
		'HM' => '\d{4}',
1335
		'HN' => '(?:\d{5})?',
1336
		'HR' => '\d{5}',
1337
		'HT' => '\d{4}',
1338
		'HU' => '\d{4}',
1339
		'ID' => '\d{5}',
1340
		'IE' => '((D|DUBLIN)?([1-9]|6[wW]|1[0-8]|2[024]))?',
1341
		'IL' => '\d{5}',
1342
		'IN' => '^[1-9][0-9][0-9][0-9][0-9][0-9]$', // India.
1343
		'IO' => 'BBND 1ZZ',
1344
		'IQ' => '\d{5}',
1345
		'IS' => '\d{3}',
1346
		'IT' => '^(V-|I-)?[0-9]{5}$',
1347
		'JO' => '\d{5}',
1348
		'JP' => '\d{3}-\d{4}',
1349
		'KE' => '\d{5}',
1350
		'KG' => '\d{6}',
1351
		'KH' => '\d{5}',
1352
		'KR' => '\d{3}[\-]\d{3}',
1353
		'KW' => '\d{5}',
1354
		'KZ' => '\d{6}',
1355
		'LA' => '\d{5}',
1356
		'LB' => '(\d{4}([ ]?\d{4})?)?',
1357
		'LI' => '(948[5-9])|(949[0-7])',
1358
		'LK' => '\d{5}',
1359
		'LR' => '\d{4}',
1360
		'LS' => '\d{3}',
1361
		'LT' => '\d{5}',
1362
		'LU' => '\d{4}',
1363
		'LV' => '\d{4}',
1364
		'MA' => '\d{5}',
1365
		'MC' => '980\d{2}',
1366
		'MD' => '\d{4}',
1367
		'ME' => '8\d{4}',
1368
		'MG' => '\d{3}',
1369
		'MH' => '969[67]\d([ \-]\d{4})?',
1370
		'MK' => '\d{4}',
1371
		'MN' => '\d{6}',
1372
		'MP' => '9695[012]([ \-]\d{4})?',
1373
		'MQ' => '9[78]2\d{2}',
1374
		'MT' => '[A-Z]{3}[ ]?\d{2,4}',
1375
		'MU' => '(\d{3}[A-Z]{2}\d{3})?',
1376
		'MV' => '\d{5}',
1377
		'MX' => '\d{5}',
1378
		'MY' => '\d{5}',
1379
		'NC' => '988\d{2}',
1380
		'NE' => '\d{4}',
1381
		'NF' => '2899',
1382
		'NG' => '(\d{6})?',
1383
		'NI' => '((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?',
1384
		'NL' => '^[1-9][0-9]{3}\s?([a-zA-Z]{2})?$',
1385
		'NO' => '\d{4}',
1386
		'NP' => '\d{5}',
1387
		'NZ' => '\d{4}',
1388
		'OM' => '(PC )?\d{3}',
1389
		'PF' => '987\d{2}',
1390
		'PG' => '\d{3}',
1391
		'PH' => '\d{4}',
1392
		'PK' => '\d{5}',
1393
		'PL' => '\d{2}-\d{3}',
1394
		'PM' => '9[78]5\d{2}',
1395
		'PN' => 'PCRN 1ZZ',
1396
		'PR' => '00[679]\d{2}([ \-]\d{4})?',
1397
		'PT' => '\d{4}([\-]\d{3})?',
1398
		'PW' => '96940',
1399
		'PY' => '\d{4}',
1400
		'RE' => '9[78]4\d{2}',
1401
		'RO' => '\d{6}',
1402
		'RS' => '\d{5}',
1403
		'RU' => '\d{6}',
1404
		'SA' => '\d{5}',
1405
		'SE' => '^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$',
1406
		'SG' => '\d{6}',
1407
		'SH' => '(ASCN|STHL) 1ZZ',
1408
		'SI' => '\d{4}',
1409
		'SJ' => '\d{4}',
1410
		'SK' => '\d{3}[ ]?\d{2}',
1411
		'SM' => '4789\d',
1412
		'SN' => '\d{5}',
1413
		'SO' => '\d{5}',
1414
		'SZ' => '[HLMS]\d{3}',
1415
		'TC' => 'TKCA 1ZZ',
1416
		'TH' => '\d{5}',
1417
		'TJ' => '\d{6}',
1418
		'TM' => '\d{6}',
1419
		'TN' => '\d{4}',
1420
		'TR' => '\d{5}',
1421
		'TW' => '\d{3}(\d{2})?',
1422
		'UA' => '\d{5}',
1423
		'UK' => '^(GIR|[A-Z]\d[A-Z\d]??|[A-Z]{2}\d[A-Z\d]??)[ ]??(\d[A-Z]{2})$',
1424
		'US' => '^\d{5}([\-]?\d{4})?$',
1425
		'UY' => '\d{5}',
1426
		'UZ' => '\d{6}',
1427
		'VA' => '00120',
1428
		'VE' => '\d{4}',
1429
		'VI' => '008(([0-4]\d)|(5[01]))([ \-]\d{4})?',
1430
		'WF' => '986\d{2}',
1431
		'YT' => '976\d{2}',
1432
		'YU' => '\d{5}',
1433
		'ZA' => '\d{4}',
1434
		'ZM' => '\d{5}',
1435
	);
1436
1437
	if ( ! isset( $zip_regex[ $country_code ] ) || preg_match( '/' . $zip_regex[ $country_code ] . '/i', $zip ) ) {
1438
		$ret = true;
1439
	}
1440
1441
	return apply_filters( 'give_is_zip_valid', $ret, $zip, $country_code );
1442
}
1443
1444
/**
1445
 * Validate donation amount and auto set correct donation level id on basis of amount.
1446
 *
1447
 * Note: If amount does not match to donation level amount then level id will be auto select to first match level id on basis of amount.
1448
 *
1449
 * @param array $valid_data List of Valid Data.
1450
 *
1451
 * @return bool
1452
 */
1453
function give_validate_donation_amount( $valid_data ) {
0 ignored issues
show
Unused Code introduced by
The parameter $valid_data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1454
1455
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
1456
1457
	/* @var Give_Donate_Form $form */
1458
	$form = new Give_Donate_Form( $post_data['give-form-id'] );
1459
1460
	// Get the form currency.
1461
	$form_currency = give_get_currency( $post_data['give-form-id'] );
1462
1463
	$donation_level_matched = false;
1464
1465
	if ( $form->is_set_type_donation_form() ) {
1466
1467
		// Sanitize donation amount.
1468
		$post_data['give-amount'] = give_maybe_sanitize_amount( $post_data['give-amount'], array( 'currency' => $form_currency ) );
1469
1470
		// Backward compatibility.
1471
		if ( $form->is_custom_price( $post_data['give-amount'] ) ) {
1472
			$post_data['give-price-id'] = 'custom';
1473
		}
1474
1475
		$donation_level_matched = true;
1476
1477
	} elseif ( $form->is_multi_type_donation_form() ) {
1478
1479
		$variable_prices = $form->get_prices();
1480
1481
		// Bailout.
1482
		if ( ! $variable_prices ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $variable_prices of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1483
			return false;
1484
		}
1485
1486
		// Sanitize donation amount.
1487
		$post_data['give-amount']     = give_maybe_sanitize_amount( $post_data['give-amount'], array( 'currency' => $form_currency ) );
1488
		$variable_price_option_amount = give_maybe_sanitize_amount( give_get_price_option_amount( $post_data['give-form-id'], $post_data['give-price-id'] ), array( 'currency' => $form_currency ) );
1489
		$new_price_id                 = '';
1490
1491
		if ( $post_data['give-amount'] === $variable_price_option_amount ) {
1492
			return true;
1493
		}
1494
1495
		if ( $form->is_custom_price( $post_data['give-amount'] ) ) {
1496
			$new_price_id = 'custom';
1497
		} else {
1498
1499
			// Find correct donation level from all donation levels.
1500
			foreach ( $variable_prices as $variable_price ) {
1501
1502
				// Sanitize level amount.
1503
				$variable_price['_give_amount'] = give_maybe_sanitize_amount( $variable_price['_give_amount'] );
1504
1505
				// Set first match donation level ID.
1506
				if ( $post_data['give-amount'] === $variable_price['_give_amount'] ) {
1507
					$new_price_id = $variable_price['_give_id']['level_id'];
1508
					break;
1509
				}
1510
			}
1511
		}
1512
1513
		// If donation amount is not find in donation levels then check if form has custom donation feature enable or not.
1514
		// If yes then set price id to custom if amount is greater then custom minimum amount (if any).
1515
		if ( $post_data['give-price-id'] === $new_price_id ) {
1516
			$donation_level_matched = true;
1517
		}
1518
	} // End if().
1519
1520
	if ( ! $donation_level_matched ) {
1521
		give_set_error(
1522
			'invalid_donation_amount',
1523
			sprintf(
1524
			/* translators: %s: invalid donation amount */
1525
				__( 'Donation amount %s is invalid.', 'give' ),
1526
				give_currency_filter(
1527
					give_format_amount( $post_data['give-amount'], array( 'sanitize' => false, ) )
1528
				)
1529
			)
1530
		);
1531
	}
1532
}
1533
1534
add_action( 'give_checkout_error_checks', 'give_validate_donation_amount', 10, 1 );
1535
1536
/**
1537
 * Validate Required Form Fields.
1538
 *
1539
 * @param int $form_id Form ID.
1540
 *
1541
 * @since 2.0
1542
 */
1543
function give_validate_required_form_fields( $form_id ) {
1544
1545
	// Sanitize values submitted with donation form.
1546
	$post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
1547
1548
	// Loop through required fields and show error messages.
1549
	foreach ( give_get_required_fields( $form_id ) as $field_name => $value ) {
1550
1551
		// Clean Up Data of the input fields.
1552
		$field_value = $post_data[ $field_name ];
1553
1554
		// Check whether the required field is empty, then show the error message.
1555
		if ( in_array( $value, give_get_required_fields( $form_id ), true ) && empty( $field_value ) ) {
1556
			give_set_error( $value['error_id'], $value['error_message'] );
1557
		}
1558
	}
1559
}
1560
1561
/**
1562
 * Validates and checks if name fields are valid or not.
1563
 *
1564
 * @param array $post_data List of post data.
1565
 *
1566
 * @since 2.1
1567
 *
1568
 * @return void
1569
 */
1570
function give_donation_form_validate_name_fields( $post_data ) {
1571
1572
	$is_alpha_first_name = ( ! is_email( $post_data['give_first'] ) && ! preg_match( '~[0-9]~', $post_data['give_first'] ) );
1573
	$is_alpha_last_name  = ( ! is_email( $post_data['give_last'] ) && ! preg_match( '~[0-9]~', $post_data['give_last'] ) );
1574
1575
	if ( ! $is_alpha_first_name || ( ! empty( $post_data['give_last'] ) && ! $is_alpha_last_name ) ) {
1576
		give_set_error( 'invalid_name', esc_html__( 'The First Name and Last Name fields cannot contain an email address or numbers.', 'give' ) );
1577
	}
1578
}
1579