Completed
Push — issues/1849 ( ebd8ee...14bc78 )
by Ravinder
20:21
created

formatting.php ➔ give_maybe_sanitize_amount()   C

Complexity

Conditions 11
Paths 5

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 14
nc 5
nop 3
dl 0
loc 25
rs 5.2653
c 0
b 0
f 0

How to fix   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
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 25 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Formatting functions for taking care of proper number formats and such
4
 *
5
 * @package     Give
6
 * @subpackage  Functions/Formatting
7
 * @copyright   Copyright (c) 2016, WordImpress
8
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
9
 * @since       1.0
10
 */
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
18
/**
19
 * Get decimal count
20
 *
21
 * @since 1.6
22
 *
23
 * @return mixed
24
 */
25
function give_get_price_decimals() {
26
	return apply_filters( 'give_sanitize_amount_decimals', give_get_option( 'number_decimals', 0 ) );
27
}
28
29
/**
30
 * Get thousand separator
31
 *
32
 * @since 1.6
33
 *
34
 * @return mixed
35
 */
36
function give_get_price_thousand_separator() {
37
	$give_options       = give_get_settings();
38
	$thousand_separator = isset( $give_options['thousands_separator'] ) ? $give_options['thousands_separator'] : ',';
39
	$thousand_separator = empty( $thousand_separator ) ? ' ' : $thousand_separator;
40
41
	return $thousand_separator;
42
}
43
44
/**
45
 * Get decimal separator
46
 *
47
 * @since 1.6
48
 *
49
 * @return mixed
50
 */
51
function give_get_price_decimal_separator() {
52
	$default_decimal_separators = array(
53
		'.' => ',',
54
		',' => '.',
55
	);
56
57
	$thousand_separator = give_get_price_thousand_separator();
58
	$default_decimal_separator = in_array( $thousand_separator, $default_decimal_separators ) ?
59
		$default_decimal_separators[$thousand_separator] :
60
		'.';
61
62
	$decimal_separator = give_get_option( 'decimal_separator', $default_decimal_separator );
63
64
	return $decimal_separator;
65
}
66
67
68
/**
69
 * Sanitize Amount before saving to database
70
 *
71
 * @since      1.8.12
72
 *
73
 * @param  int|float|string $number Expects either a float or a string with a decimal separator only (no thousands)
74
 *
75
 * @return string $amount Newly sanitized amount
76
 */
77
function give_sanitize_amount_for_db( $number ) {
78
	return give_sanitize_amount( $number, 6 );
79
}
80
81
/**
82
 * Sanitize Amount before saving to database
83
 *
84
 * @since      1.8.12
85
 *
86
 * @param  int|float|string $number     Expects either a float or a string with a decimal separator only (no thousands)
87
 * @param  int|bool         $dp         Number of decimals
88
 * @param  bool             $trim_zeros From end of string
89
 *
90
 * @return string $amount Newly sanitized amount
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
91
 */
92
function give_maybe_sanitize_amount( $number, $dp = false, $trim_zeros = false ) {
93
	$thousand_separator = give_get_price_thousand_separator();
94
	$decimal_separator  = give_get_price_decimal_separator();
95
96
	// Bailout.
97
	if( empty( $number ) || ( ! is_numeric( $number ) && ! is_string( $number ) ) ) {
98
		return $number;
99
	}elseif (
100
		( false == strpos( $number, $thousand_separator ) ) &&
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($number, $thousand_separator) of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
101
		( false === strpos( $number, $decimal_separator ) )
102
	) {
103
		return number_format( $number, ( is_bool( $dp ) ? give_get_price_decimals() : $dp ), '.', '' );
104
	}
105
106
	// Handle thousand separator as '.'
107
	if ( '.' === $thousand_separator && false !== strpos( $number, $thousand_separator ) ){
108
		$number_parts = explode( '.', $number );
109
110
		if( 2 < count( $number_parts ) || ( 6 > strlen( $number_parts[1] ) ) ) {
111
			$number = str_replace( '.', '', $number );
112
		}
113
	}
114
115
	return give_sanitize_amount( $number, $dp, $trim_zeros );
116
}
117
118
/**
119
 * Sanitize Amount
120
 *
121
 * Returns a sanitized amount by stripping out thousands separators.
122
 *
123
 * @since      1.0
124
 *
125
 * @param  int|float|string $number Expects either a float or a string with a decimal separator only (no thousands)
126
 * @param  int|bool         $dp Number of decimals
127
 * @param  bool             $trim_zeros From end of string
128
 *
129
 * @return string $amount Newly sanitized amount
130
 */
131
function give_sanitize_amount( $number, $dp = false, $trim_zeros = false ) {
132
133
	// Bailout.
134
	if ( empty( $number ) || ( ! is_numeric( $number ) && ! is_string( $number ) ) ) {
135
		return $number;
136
	}
137
138
	// Remove slash from amount.
139
	// If thousand or decimal separator is set to ' then in $_POST or $_GET param we will get an escaped number.
140
	// To prevent notices and warning remove slash from amount/number.
141
	$number = wp_unslash( $number );
142
143
	$thousand_separator = give_get_price_thousand_separator();
144
145
	$locale   = localeconv();
146
	$decimals = array( give_get_price_decimal_separator(), $locale['decimal_point'], $locale['mon_decimal_point'] );
147
148
	// Remove locale from string
149
	if ( ! is_float( $number ) ) {
150
		$number = str_replace( $decimals, '.', $number );
151
	}
152
153
	// Remove thousand amount formatting if amount has.
154
	// This condition use to add backward compatibility to version before 1.6, because before version 1.6 we were saving formatted amount to db.
155
	// Do not replace thousand separator from price if it is same as decimal separator, because it will be already replace by above code.
156
	if ( ! in_array( $thousand_separator, $decimals ) && ( false !== strpos( $number, $thousand_separator ) ) ) {
157
		$number = str_replace( $thousand_separator, '', $number );
158
	} elseif ( in_array( $thousand_separator, $decimals ) ) {
159
		$number = preg_replace( '/\.(?=.*\.)/', '', $number );
160
	}
161
162
	// Remove non numeric entity before decimal separator.
163
	$number     = preg_replace( '/[^0-9\.]/', '', $number );
164
	$default_dp = give_get_price_decimals();
165
166
	// Reset negative amount to zero.
167
	if ( 0 > $number ) {
168
		$number = number_format( 0, $default_dp, '.' );
169
	}
170
171
	// If number does not have decimal then add number of decimals to it.
172
	if (
173
		false === strpos( $number, '.' )
174
		|| ( $default_dp > strlen( substr( $number, strpos( $number, '.' ) + 1 ) ) )
175
	) {
176
		$number = number_format( $number, $default_dp, '.', '' );
177
	}
178
179
	// Format number by custom number of decimals.
180
	if ( false !== $dp ) {
181
		$dp     = intval( is_bool( $dp ) ? $default_dp : $dp );
182
		$dp     = apply_filters( 'give_sanitize_amount_decimals', $dp, $number );
183
		$number = number_format( floatval( $number ), $dp, '.', '' );
184
	}
185
186
	// Trim zeros.
187
	if ( $trim_zeros && strstr( $number, '.' ) ) {
188
		$number = rtrim( rtrim( $number, '0' ), '.' );
189
	}
190
191
	return apply_filters( 'give_sanitize_amount', $number );
192
}
193
194
/**
195
 * Returns a nicely formatted amount.
196
 *
197
 * @since 1.0
198
 *
199
 * @param string $amount   Price amount to format
200
 * @param bool   $decimals Whether or not to use decimals. Useful when set to false for non-currency numbers.
201
 * @param bool   $sanitize Whether or not to sanitize number.
202
 *
203
 * @return string $amount   Newly formatted amount or Price Not Available
204
 */
205
function give_format_amount( $amount, $decimals = true, $sanitize = true ) {
206
	$formatted     = 0;
207
	$thousands_sep = give_get_price_thousand_separator();
208
	$decimal_sep   = give_get_price_decimal_separator();
209
	$decimals      = $decimals ? give_get_price_decimals() : 0;
210
	$currency      = give_get_currency();
211
212
	if ( ! empty( $amount ) ) {
213
		// Sanitize amount before formatting.
214
		$amount = $sanitize ?
215
			give_maybe_sanitize_amount( $amount, $decimals ) :
216
			number_format( $amount, $decimals, '.', '' );
217
218
		if ( 'INR' === $currency ) {
219
			$decimal_amount = '';
220
221
			// Extract decimals from amount
222
			if ( ( $pos = strpos( $amount, '.' ) ) !== false ) {
223
				if ( ! empty( $decimals ) ) {
224
					$decimal_amount = substr( round( substr( $amount, $pos ), $decimals ), 1 );
225
					$amount         = substr( $amount, 0, $pos );
226
227
					if ( ! $decimal_amount ) {
228
						$decimal_amount = substr( '.0000000000', 0, ( $decimals + 1 ) );
229
					} elseif ( ( $decimals + 1 ) > strlen( $decimal_amount ) ) {
230
						$decimal_amount = substr( "{$decimal_amount}000000000", 0, ( $decimals + 1 ) );
231
					}
232
233
				} else {
234
					$amount = number_format( $amount, $decimals, $decimal_sep, '' );
235
				}
236
			}
237
238
			// Extract last 3 from amount
239
			$result = substr( $amount, - 3 );
240
			$amount = substr( $amount, 0, - 3 );
241
242
			// Apply digits 2 by 2
243
			while ( strlen( $amount ) > 0 ) {
244
				$result = substr( $amount, - 2 ) . $thousands_sep . $result;
245
				$amount = substr( $amount, 0, - 2 );
246
			}
247
248
			$formatted = $result . $decimal_amount;
249
		} else {
250
			$formatted = number_format( $amount, $decimals, $decimal_sep, $thousands_sep );
251
252
		}
253
	}
254
255
	return apply_filters( 'give_format_amount', $formatted, $amount, $decimals, $decimal_sep, $thousands_sep, $currency );
256
}
257
258
259
/**
260
 * Get human readable amount.
261
 *
262
 * Note: This function only support large number formatting from million to trillion
263
 *
264
 * @since 1.6
265
 *
266
 * @use   give_get_price_thousand_separator Get thousand separator.
267
 *
268
 * @param string $amount formatted amount number.
269
 *
270
 * @return float|string  formatted amount number with large number names.
271
 */
272
function give_human_format_large_amount( $amount ) {
273
274
	// Get thousand separator.
275
	$thousands_sep = give_get_price_thousand_separator();
276
277
	// Sanitize amount.
278
	$sanitize_amount = give_maybe_sanitize_amount( $amount );
279
280
	// Explode amount to calculate name of large numbers.
281
	$amount_array = explode( $thousands_sep, $amount );
282
283
	// Calculate amount parts count.
284
	$amount_count_parts = count( $amount_array );
285
286
	// Human format amount (default).
287
	$human_format_amount = $amount;
288
289
	// Calculate large number formatted amount.
290
	if ( 4 < $amount_count_parts ) {
291
		$human_format_amount = sprintf( esc_html__( '%s trillion', 'give' ), round( ( $sanitize_amount / 1000000000000 ), 2 ) );
292
	} elseif ( 3 < $amount_count_parts ) {
293
		$human_format_amount = sprintf( esc_html__( '%s billion', 'give' ), round( ( $sanitize_amount / 1000000000 ), 2 ) );
294
	} elseif ( 2 < $amount_count_parts ) {
295
		$human_format_amount = sprintf( esc_html__( '%s million', 'give' ), round( ( $sanitize_amount / 1000000 ), 2 ) );
296
	}
297
298
	return apply_filters( 'give_human_format_large_amount', $human_format_amount, $amount, $sanitize_amount );
299
}
300
301
/**
302
 * Returns a nicely formatted amount with custom decimal separator.
303
 *
304
 * @since 1.0
305
 *
306
 * @param int|float|string $amount   Formatted or sanitized price
307
 * @param int|bool         $dp       number of decimals
308
 * @param bool             $sanitize Whether or not sanitize number
309
 *
310
 * @return string $amount Newly formatted amount or Price Not Available
311
 */
312
function give_format_decimal( $amount, $dp = false, $sanitize = true ) {
313
	$decimal_separator = give_get_price_decimal_separator();
314
	$formatted_amount  = $sanitize ?
315
		give_maybe_sanitize_amount( $amount, $dp ) :
316
		number_format( $amount, ( is_bool( $dp ) ? give_get_price_decimals() : $dp ), '.', '' );
317
318
	if ( false !== strpos( $formatted_amount, '.' ) ) {
319
		$formatted_amount = str_replace( '.', $decimal_separator, $formatted_amount );
320
	}
321
322
	return apply_filters( 'give_format_decimal', $formatted_amount, $amount, $decimal_separator );
323
}
324
325
/**
326
 * Formats the currency displayed.
327
 *
328
 * @since 1.0
329
 *
330
 * @param string $price The donation amount.
331
 * @param string $currency The currency code.
332
 * @param bool   $decode_currency Whether to decode the currency HTML format or not.
333
 *
334
 * @return mixed|string
335
 */
336
function give_currency_filter( $price = '', $currency = '', $decode_currency = false ) {
337
338
	if ( empty( $currency ) ) {
339
		$currency = give_get_currency();
340
	}
341
342
	$position = give_get_option( 'currency_position', 'before' );
343
344
	$negative = $price < 0;
345
346
	if ( $negative ) {
347
		// Remove proceeding "-".
348
		$price = substr( $price, 1 );
349
	}
350
351
	$symbol = give_currency_symbol( $currency, $decode_currency );
352
353
	switch ( $currency ) :
354
		case 'GBP' :
355
		case 'BRL' :
356
		case 'EUR' :
357
		case 'USD' :
358
		case 'AUD' :
359
		case 'CAD' :
360
		case 'HKD' :
361
		case 'MXN' :
362
		case 'NZD' :
363
		case 'SGD' :
364
		case 'JPY' :
365
		case 'THB' :
366
		case 'INR' :
367
		case 'RIAL' :
368
		case 'TRY' :
369
		case 'RUB' :
370
		case 'SEK' :
371
		case 'PLN' :
372
		case 'PHP' :
373
		case 'TWD' :
374
		case 'MYR' :
375
		case 'CZK' :
376
		case 'DKK' :
377
		case 'HUF' :
378
		case 'ILS' :
379
		case 'MAD' :
380
		case 'KRW' :
381
		case 'ZAR' :
382
			$formatted = ( 'before' === $position ? $symbol . $price : $price . $symbol );
383
			break;
384
		case 'NOK' :
385
			$formatted = ( 'before' === $position ? $symbol . ' ' . $price : $price . ' ' . $symbol );
386
			break;
387
		default :
388
			$formatted = ( 'before' === $position ? $currency . ' ' . $price : $price . ' ' . $currency );
389
			break;
390
	endswitch;
391
392
	/**
393
	 * Filter formatted amount with currency
394
	 *
395
	 * Filter name depends upon current value of currency and currency position.
396
	 * For example :
397
	 *           if currency is USD and currency position is before then
398
	 *           filter name will be give_usd_currency_filter_before
399
	 *
400
	 *           and if currency is USD and currency position is after then
401
	 *           filter name will be give_usd_currency_filter_after
402
	 */
403
	$formatted = apply_filters( 'give_' . strtolower( $currency ) . "_currency_filter_{$position}", $formatted, $currency, $price );
404
405
	if ( $negative ) {
406
		// Prepend the minus sign before the currency sign.
407
		$formatted = '-' . $formatted;
408
	}
409
410
	return $formatted;
411
}
412
413
/**
414
 * Set the number of decimal places per currency
415
 *
416
 * @since 1.0
417
 * @since 1.6 $decimals parameter removed from function params
418
 * *
419
 * @return int $decimals
420
 */
421
function give_currency_decimal_filter() {
422
423
	remove_filter( 'give_sanitize_amount_decimals', 'give_currency_decimal_filter' );
424
425
	// Set default number of decimals.
426
	$decimals = give_get_price_decimals();
427
428
	add_filter( 'give_sanitize_amount_decimals', 'give_currency_decimal_filter' );
429
430
	// Get number of decimals with backward compatibility ( version < 1.6 )
431
	if ( 1 <= func_num_args() ) {
432
		$decimals = ( false === func_get_arg( 0 ) ? $decimals : absint( func_get_arg( 0 ) ) );
433
	}
434
435
	$currency = give_get_currency();
436
437
	switch ( $currency ) {
438
		case 'RIAL' :
439
		case 'JPY' :
440
		case 'TWD' :
441
		case 'HUF' :
442
443
			$decimals = 0;
444
			break;
445
	}
446
447
	return apply_filters( 'give_currency_decimal_count', $decimals, $currency );
448
}
449
450
add_filter( 'give_sanitize_amount_decimals', 'give_currency_decimal_filter' );
451
add_filter( 'give_format_amount_decimals', 'give_currency_decimal_filter' );
452
453
454
/**
455
 * Get date format string on basis of given context.
456
 *
457
 * @since 1.7
458
 *
459
 * @param  string $date_context Date format context name.
460
 *
461
 * @return string                  Date format string
462
 */
463
function give_date_format( $date_context = '' ) {
464
	/**
465
	 * Filter the date context
466
	 *
467
	 * You can add your own date context or use already exist context.
468
	 * For example:
469
	 *    add_filter( 'give_date_format_contexts', 'add_new_date_contexts' );
470
	 *    function add_new_date_contexts( $date_format_contexts ) {
471
	 *        // You can add single context like this $date_format_contexts['checkout'] = 'F j, Y';
472
	 *        // Instead add multiple date context at once.
473
	 *        $new_date_format_contexts = array(
474
	 *            'checkout' => 'F j, Y',
475
	 *            'report'   => 'Y-m-d',
476
	 *            'email'    => 'm/d/Y',
477
	 *        );
478
	 *
479
	 *       // Merge date contexts array only if you are adding multiple date contexts at once otherwise return  $date_format_contexts.
480
	 *       return array_merge( $new_date_format_contexts, $date_format_contexts );
481
	 *
482
	 *    }
483
	 */
484
	$date_format_contexts = apply_filters( 'give_date_format_contexts', array() );
485
486
	// Set date format to default date format.
487
	$date_format = get_option( 'date_format' );
488
489
	// Update date format if we have non empty date format context array and non empty date format string for that context.
490
	if ( $date_context && ! empty( $date_format_contexts ) && array_key_exists( $date_context, $date_format_contexts ) ) {
491
		$date_format = ! empty( $date_format_contexts[ $date_context ] )
492
			? $date_format_contexts[ $date_context ]
493
			: $date_format;
494
	}
495
496
	return apply_filters( 'give_date_format', $date_format );
497
}
498
499
/**
500
 * Get cache key.
501
 *
502
 * @since  1.7
503
 * @deprecated 1.8.7 You can access this function from Give_Cache.
504
 *
505
 * @param  string $action Cache key prefix.
506
 * @param array  $query_args Query array.
507
 *
508
 * @return string
509
 */
510
function give_get_cache_key( $action, $query_args ) {
511
	return Give_Cache::get_key( $action, $query_args );
512
}
513
514
/**
515
 * Clean variables using sanitize_text_field. Arrays are cleaned recursively.
516
 * Non-scalar values are ignored.
517
 *
518
 * @since  1.8
519
 *
520
 * @param  string|array $var
521
 *
522
 * @return string|array
523
 */
524
function give_clean( $var ) {
525
	if ( is_array( $var ) ) {
526
		return array_map( 'give_clean', $var );
527
	} else {
528
		return is_scalar( $var ) ? sanitize_text_field( $var ) : $var;
529
	}
530
}
531
532
/**
533
 * Transforms php.ini notation for numbers (like '2M') to an integer.
534
 *
535
 * @since 1.8
536
 *
537
 * @param $size
538
 *
539
 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
540
 */
541
function give_let_to_num( $size ) {
542
	$l   = substr( $size, - 1 );
543
	$ret = substr( $size, 0, - 1 );
544
	switch ( strtoupper( $l ) ) {
545
		case 'P':
546
			$ret *= 1024;
547
		case 'T':
548
			$ret *= 1024;
549
		case 'G':
550
			$ret *= 1024;
551
		case 'M':
552
			$ret *= 1024;
553
		case 'K':
554
			$ret *= 1024;
555
	}
556
557
	return $ret;
558
}
559
560
/**
561
 * Verify nonce.
562
 *
563
 * @since 1.8
564
 *
565
 * @param        $nonce
566
 * @param int   $action
567
 * @param array $wp_die_args
568
 */
569
function give_validate_nonce( $nonce, $action = - 1, $wp_die_args = array() ) {
570
571
	$default_wp_die_args = array(
572
		'message' => esc_html__( 'Nonce verification has failed.', 'give' ),
573
		'title'   => esc_html__( 'Error', 'give' ),
574
		'args'    => array(
575
			'response' => 403,
576
		),
577
	);
578
579
	$wp_die_args = wp_parse_args( $wp_die_args, $default_wp_die_args );
580
581
	if ( ! wp_verify_nonce( $nonce, $action ) ) {
582
		wp_die(
583
			$wp_die_args['message'],
584
			$wp_die_args['title'],
585
			$wp_die_args['args']
586
		);
587
	}
588
}
589
590
/**
591
 * Check variable and get default or valid value.
592
 *
593
 * Helper function to check if a variable is set, empty, etc.
594
 *
595
 * @since 1.8
596
 *
597
 * @param                   $variable
598
 * @param string (optional) $conditional , default value: isset
599
 * @param bool (optional)   $default , default value: false
600
 *
601
 * @return mixed
602
 */
603
function give_check_variable( $variable, $conditional = '', $default = false ) {
604
605
	switch ( $conditional ) {
606
		case 'isset_empty':
607
			$variable = ( isset( $variable ) && ! empty( $variable ) ) ? $variable : $default;
608
			break;
609
610
		case 'empty':
611
			$variable = ! empty( $variable ) ? $variable : $default;
612
			break;
613
614
		case 'null':
615
			$variable = ! is_null( $variable ) ? $variable : $default;
616
			break;
617
618
		default:
619
			$variable = isset( $variable ) ? $variable : $default;
620
621
	}
622
623
	return $variable;
624
625
}
626