Completed
Push — issues/1849 ( 0646af...ebd8ee )
by Ravinder
17:48
created

formatting.php ➔ give_maybe_sanitize_amount()   B

Complexity

Conditions 7
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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