1
|
|
|
<?php |
|
|
|
|
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 http://opensource.org/licenses/gpl-2.0.php 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
|
52 |
|
/** |
30
|
52 |
|
* Get thousand separator |
31
|
52 |
|
* |
32
|
|
|
* @since 1.6 |
33
|
|
|
* |
34
|
52 |
|
* @return mixed |
35
|
|
|
*/ |
36
|
|
|
function give_get_price_thousand_separator() { |
37
|
|
|
return give_get_option( 'thousands_separator', ',' ); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Get decimal separator |
42
|
52 |
|
* |
43
|
|
|
* @since 1.6 |
44
|
|
|
* |
45
|
|
|
* @return mixed |
46
|
52 |
|
*/ |
47
|
|
|
function give_get_price_decimal_separator() { |
48
|
|
|
return give_get_option( 'decimal_separator', '.' ); |
49
|
|
|
} |
50
|
52 |
|
|
51
|
52 |
|
/** |
52
|
52 |
|
* Sanitize Amount |
53
|
|
|
* |
54
|
52 |
|
* Returns a sanitized amount by stripping out thousands separators. |
55
|
|
|
* |
56
|
|
|
* @since 1.0 |
57
|
|
|
* |
58
|
52 |
|
* @param int|float|string $number Expects either a float or a string with a decimal separator only (no thousands) |
59
|
|
|
* @param int|bool $dp Number of decimals |
60
|
|
|
* @param bool $trim_zeros From end of string |
61
|
|
|
* |
62
|
|
|
* |
63
|
|
|
* @return string $amount Newly sanitized amount |
64
|
|
|
*/ |
65
|
|
|
function give_sanitize_amount( $number, $dp = false, $trim_zeros = false ) { |
66
|
|
|
|
67
|
|
|
// Bailout. |
68
|
|
|
if( empty( $number ) ) { |
69
|
|
|
return $number; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
$thousand_separator = give_get_price_thousand_separator(); |
73
|
43 |
|
|
74
|
43 |
|
$locale = localeconv(); |
75
|
|
|
$decimals = array( give_get_price_decimal_separator(), $locale['decimal_point'], $locale['mon_decimal_point'] ); |
76
|
|
|
|
77
|
43 |
|
// Remove locale from string |
78
|
|
|
if ( ! is_float( $number ) ) { |
79
|
|
|
$number = str_replace( $decimals, '.', $number ); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
// Remove thousand amount formatting if amount has. |
83
|
|
|
// This condition use to add backward compatibility to version before 1.6, because before version 1.6 we were saving formatted amount to db. |
84
|
43 |
|
// Do not replace thousand separator from price if it is same as decimal separator, because it will be already replace by above code. |
85
|
|
|
if( ! in_array( $thousand_separator, $decimals ) && ( false !== strpos( $number, $thousand_separator ) ) ) { |
86
|
|
|
$number = str_replace( $thousand_separator, '', $number ); |
87
|
|
|
} |
88
|
|
|
|
89
|
43 |
|
// Remove non numeric entity before decimal separator. |
90
|
|
|
$number = preg_replace( '/[^0-9\.]/', '', $number ); |
91
|
|
|
$default_dp = give_get_price_decimals(); |
92
|
|
|
|
93
|
|
|
// Format number of decimals in number. |
94
|
|
|
if( false !== $dp ) { |
95
|
|
|
$dp = intval( empty( $dp ) ? $default_dp : $dp ); |
96
|
|
|
$dp = apply_filters( 'give_sanitize_amount_decimals', $dp, $number ); |
97
|
|
|
$number = number_format( floatval( $number ), $dp, '.', '' ); |
98
|
|
|
} |
99
|
43 |
|
|
100
|
|
|
// Reset negative amount to zero. |
101
|
|
|
if ( 0 > $number ) { |
102
|
|
|
$number = number_format( 0, $default_dp, '.' ); |
103
|
43 |
|
} |
104
|
2 |
|
|
105
|
2 |
|
// If number does not have decimal then add number of decimals to it. |
106
|
|
|
if( |
107
|
43 |
|
false === strpos( $number, '.' ) |
108
|
|
|
|| ( $default_dp > strlen( substr( $number, strpos( $number , '.' ) + 1 ) ) ) |
109
|
43 |
|
) { |
110
|
|
|
$number = number_format( $number, $default_dp, '.', '' ); |
111
|
43 |
|
} |
112
|
|
|
|
113
|
|
|
// Trim zeros. |
114
|
|
|
if ( $trim_zeros && strstr( $number, '.' ) ) { |
115
|
|
|
$number = rtrim( rtrim( $number, '0' ), '.' ); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
return apply_filters( 'give_sanitize_amount', $number ); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Returns a nicely formatted amount. |
123
|
|
|
* |
124
|
|
|
* @since 1.0 |
125
|
|
|
* |
126
|
|
|
* @param string $amount Price amount to format |
127
|
|
|
* @param bool|string $decimals Whether or not to use decimals. Useful when set to false for non-currency numbers. |
128
|
|
|
* |
129
|
|
|
* @return string $amount Newly formatted amount or Price Not Available |
130
|
|
|
*/ |
131
|
|
|
function give_format_amount( $amount, $decimals = true ) { |
132
|
|
|
|
133
|
|
|
$thousands_sep = give_get_option( 'thousands_separator', ',' ); |
134
|
|
|
$decimal_sep = give_get_option( 'decimal_separator', '.' ); |
135
|
|
|
|
136
|
|
|
// Format the amount |
137
|
|
|
if ( $decimal_sep == ',' && false !== ( $sep_found = strpos( $amount, $decimal_sep ) ) ) { |
138
|
|
|
$whole = substr( $amount, 0, $sep_found ); |
139
|
|
|
$part = substr( $amount, $sep_found + 1, ( strlen( $amount ) - 1 ) ); |
140
|
|
|
$amount = $whole . '.' . $part; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
// Strip , from the amount (if set as the thousands separator) |
144
|
|
|
if ( $thousands_sep == ',' && false !== ( $found = strpos( $amount, $thousands_sep ) ) ) { |
145
|
|
|
$amount = str_replace( ',', '', $amount ); |
146
|
45 |
|
} |
147
|
45 |
|
|
148
|
45 |
|
// Strip . from the amount (if set as the thousands separator) AND , set to decimal separator |
149
|
|
|
if ( $thousands_sep == '.' && $decimal_sep == ',' && false !== ( $found = strpos( $amount, $thousands_sep ) ) ) { |
150
|
45 |
|
$amount = explode( '.', $amount ); |
151
|
|
|
$array_count = count( $amount ); |
152
|
45 |
|
if ( $decimals == true ) { |
153
|
|
|
unset( $amount[ $array_count - 1 ] ); |
154
|
45 |
|
} |
155
|
|
|
$amount = implode( '', $amount ); |
156
|
|
|
} |
157
|
|
|
|
158
|
45 |
|
// Strip ' ' from the amount (if set as the thousands separator) |
159
|
|
|
if ( $thousands_sep == ' ' && false !== ( $found = strpos( $amount, $thousands_sep ) ) ) { |
160
|
45 |
|
$amount = str_replace( ' ', '', $amount ); |
161
|
|
|
} |
162
|
45 |
|
|
163
|
45 |
|
if ( empty( $amount ) ) { |
164
|
45 |
|
$amount = 0; |
165
|
45 |
|
} |
166
|
45 |
|
|
167
|
45 |
|
$decimals = give_get_price_decimals(); |
168
|
45 |
|
|
169
|
45 |
|
$formatted = number_format( $amount, $decimals, $decimal_sep, $thousands_sep ); |
170
|
45 |
|
|
171
|
45 |
|
return apply_filters( 'give_format_amount', $formatted, $amount, $decimals, $decimal_sep, $thousands_sep ); |
172
|
45 |
|
} |
173
|
45 |
|
|
174
|
45 |
|
|
175
|
45 |
|
/** |
176
|
45 |
|
* Get human readable amount. |
177
|
45 |
|
* |
178
|
45 |
|
* Note: This function only support large number formatting from million to trillion |
179
|
45 |
|
* |
180
|
45 |
|
* @since 1.6 |
181
|
45 |
|
* |
182
|
45 |
|
* @use give_get_price_thousand_separator Get thousand separator. |
183
|
45 |
|
* |
184
|
45 |
|
* @param string $amount formatted amount number. |
185
|
45 |
|
* @return float|string formatted amount number with large number names. |
186
|
45 |
|
*/ |
187
|
45 |
|
function give_human_format_large_amount( $amount ) { |
188
|
45 |
|
|
189
|
45 |
|
// Get thousand separator. |
190
|
45 |
|
$thousands_sep = give_get_price_thousand_separator(); |
191
|
45 |
|
|
192
|
|
|
// Sanitize amount. |
193
|
|
|
$sanitize_amount = give_sanitize_amount( $amount ); |
194
|
|
|
|
195
|
|
|
// Explode amount to calculate name of large numbers. |
196
|
|
|
$amount_array = explode( $thousands_sep, $amount ); |
197
|
|
|
|
198
|
|
|
// Calculate amount parts count. |
199
|
45 |
|
$amount_count_parts = count( $amount_array ); |
200
|
45 |
|
|
201
|
|
|
// Calculate large number formatted amount. |
202
|
|
|
if ( 4 < $amount_count_parts ){ |
203
|
|
|
$sanitize_amount = sprintf( esc_html__( '%s trillion', 'give' ), round( ( $sanitize_amount / 1000000000000 ), 2 ) ); |
204
|
|
|
} elseif ( 3 < $amount_count_parts ){ |
205
|
|
|
$sanitize_amount = sprintf( esc_html__( '%s billion', 'give' ), round( ( $sanitize_amount / 1000000000 ), 2 )); |
206
|
|
|
} elseif ( 2 < $amount_count_parts ) { |
207
|
|
|
$sanitize_amount = sprintf( esc_html__( '%s million', 'give' ), round( ( $sanitize_amount / 1000000), 2 ) ); |
208
|
|
|
} else{ |
209
|
|
|
$sanitize_amount = give_format_amount( $amount ); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
return apply_filters( 'give_human_format_large_amount', $sanitize_amount, $amount ); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Returns a nicely formatted amount with custom decimal separator. |
217
|
|
|
* |
218
|
|
|
* @since 1.0 |
219
|
|
|
* |
220
|
|
|
* @param int|float|string $amount Formatted or sanitized price |
221
|
|
|
* @param int|bool $dp number of decimals |
222
|
|
|
* |
223
|
|
|
* @return string $amount Newly formatted amount or Price Not Available |
224
|
|
|
*/ |
225
|
|
|
function give_format_decimal( $amount, $dp = false ){ |
226
|
|
|
$decimal_separator = give_get_price_decimal_separator(); |
227
|
|
|
$formatted_amount = give_sanitize_amount( $amount, $dp ); |
228
|
|
|
|
229
|
|
|
if( false !== strpos( $formatted_amount, '.' ) ) { |
230
|
|
|
$formatted_amount = str_replace( '.', $decimal_separator, $formatted_amount ); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
return apply_filters( 'give_format_decimal', $formatted_amount, $amount, $decimal_separator ); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
|
237
|
|
|
/** |
238
|
45 |
|
* Format Multi-level Amount |
239
|
|
|
* |
240
|
|
|
* Loops through CMB2 repeater field and updates amount field using give_format_amount() |
241
|
|
|
* |
242
|
|
|
* @param $field_args |
243
|
45 |
|
* @param $field |
244
|
|
|
* |
245
|
|
|
* @return bool |
246
|
|
|
*/ |
247
|
|
|
function give_format_admin_multilevel_amount( $field_args, $field ) { |
|
|
|
|
248
|
|
|
|
249
|
|
|
if ( empty( $field->value ) ) { |
250
|
|
|
return false; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
$field->value = give_format_decimal( $field->value ); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
55 |
|
* Formats the currency display |
258
|
|
|
* |
259
|
|
|
* @since 1.0 |
260
|
55 |
|
* |
261
|
55 |
|
* @param string $price |
262
|
55 |
|
* @param string $currency |
263
|
55 |
|
* |
264
|
|
|
* @return mixed|string|void |
265
|
|
|
*/ |
266
|
|
|
function give_currency_filter( $price = '', $currency = '' ) { |
267
|
|
|
|
268
|
|
|
if ( empty( $currency ) ) { |
269
|
55 |
|
$currency = give_get_currency(); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
$position = give_get_option( 'currency_position', 'before' ); |
273
|
|
|
|
274
|
|
|
$negative = $price < 0; |
275
|
|
|
|
276
|
|
|
if ( $negative ) { |
277
|
|
|
// Remove proceeding "-". |
278
|
|
|
$price = substr( $price, 1 ); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
$symbol = give_currency_symbol( $currency ); |
282
|
|
|
|
283
|
|
|
switch ( $currency ): |
284
|
|
|
case 'GBP' : |
285
|
|
|
case 'BRL' : |
286
|
|
|
case 'EUR' : |
287
|
|
|
case 'USD' : |
288
|
|
|
case 'AUD' : |
289
|
|
|
case 'CAD' : |
290
|
|
|
case 'HKD' : |
291
|
|
|
case 'MXN' : |
292
|
|
|
case 'NZD' : |
293
|
|
|
case 'SGD' : |
294
|
|
|
case 'JPY' : |
295
|
|
|
case 'THB' : |
296
|
|
|
case 'INR' : |
297
|
|
|
case 'RIAL' : |
298
|
|
|
case 'TRY' : |
299
|
|
|
case 'RUB' : |
300
|
|
|
case 'SEK' : |
301
|
|
|
case 'PLN' : |
302
|
|
|
case 'PHP' : |
303
|
|
|
case 'TWD' : |
304
|
|
|
case 'MYR' : |
305
|
|
|
case 'CZK' : |
306
|
|
|
case 'DKK' : |
307
|
|
|
case 'HUF' : |
308
|
|
|
case 'ILS' : |
309
|
|
|
case 'MAD' : |
310
|
|
|
case 'KRW' : |
311
|
|
|
case 'ZAR' : |
312
|
|
|
$formatted = ( 'before' === $position ? $symbol . $price : $price . $symbol ); |
313
|
|
|
break; |
314
|
|
|
case 'NOK' : |
315
|
|
|
$formatted = ( 'before' === $position ? $symbol . ' ' . $price : $price . ' ' . $symbol ); |
316
|
|
|
break; |
317
|
|
|
default : |
318
|
|
|
$formatted = ( 'before' === $position ? $currency . ' ' . $price : $price . ' ' . $currency ); |
319
|
|
|
break; |
320
|
|
|
endswitch; |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Filter formatted amount with currency |
324
|
|
|
* |
325
|
|
|
* Filter name depends upon current value of currency and currency position. |
326
|
|
|
* For example : |
327
|
|
|
* if currency is USD and currency position is before then |
328
|
|
|
* filter name will be give_usd_currency_filter_before |
329
|
|
|
* |
330
|
|
|
* and if currency is USD and currency position is after then |
331
|
|
|
* filter name will be give_usd_currency_filter_after |
332
|
|
|
* |
333
|
|
|
*/ |
334
|
|
|
$formatted = apply_filters( 'give_' . strtolower( $currency ) . "_currency_filter_{$position}", $formatted, $currency, $price ); |
335
|
|
|
|
336
|
|
|
if ( $negative ) { |
337
|
|
|
// Prepend the minus sign before the currency sign. |
338
|
|
|
$formatted = '-' . $formatted; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
return $formatted; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Set the number of decimal places per currency |
346
|
|
|
* |
347
|
|
|
* @since 1.0 |
348
|
|
|
* @since 1.6 $decimals parameter removed from function params |
349
|
|
|
** |
350
|
|
|
* @return int $decimals |
351
|
|
|
*/ |
352
|
|
|
function give_currency_decimal_filter() { |
353
|
|
|
|
354
|
|
|
remove_filter( 'give_sanitize_amount_decimals', 'give_currency_decimal_filter' ); |
355
|
|
|
|
356
|
|
|
// Set default number of decimals. |
357
|
|
|
$decimals = give_get_price_decimals(); |
358
|
|
|
|
359
|
|
|
add_filter( 'give_sanitize_amount_decimals', 'give_currency_decimal_filter' ); |
360
|
|
|
|
361
|
|
|
|
362
|
|
|
// Get number of decimals with backward compatibility ( version < 1.6 ) |
363
|
|
|
if( 1 <= func_num_args() ){ |
364
|
|
|
$decimals = ( false === func_get_arg( 0 ) ? $decimals : absint( func_get_arg( 0 ) ) ); |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
$currency = give_get_currency(); |
368
|
|
|
|
369
|
|
|
switch ( $currency ) { |
370
|
|
|
case 'RIAL' : |
371
|
|
|
case 'JPY' : |
372
|
|
|
case 'TWD' : |
373
|
|
|
case 'HUF' : |
374
|
|
|
|
375
|
|
|
$decimals = 0; |
376
|
|
|
break; |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
return apply_filters( 'give_currency_decimal_count', $decimals, $currency ); |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
add_filter( 'give_sanitize_amount_decimals', 'give_currency_decimal_filter' ); |
383
|
|
|
add_filter( 'give_format_amount_decimals', 'give_currency_decimal_filter' ); |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* Sanitize thousand separator |
387
|
|
|
* |
388
|
|
|
* @since 1.6 |
389
|
|
|
* |
390
|
|
|
* @param string $value |
391
|
|
|
* @param array $field_args |
392
|
|
|
* @param object $field |
393
|
|
|
* |
394
|
|
|
* @return mixed |
395
|
|
|
*/ |
396
|
|
|
function give_sanitize_thousand_separator( $value, $field_args, $field ){ |
|
|
|
|
397
|
|
|
return $value; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
|
401
|
|
|
/** |
402
|
|
|
* Sanitize number of decimals |
403
|
|
|
* |
404
|
|
|
* @since 1.6 |
405
|
|
|
* |
406
|
|
|
* @param string $value |
407
|
|
|
* @param array $field_args |
408
|
|
|
* @param object $field |
409
|
|
|
* |
410
|
|
|
* @return mixed |
411
|
|
|
*/ |
412
|
|
|
function give_sanitize_number_decimals( $value, $field_args, $field ){ |
|
|
|
|
413
|
|
|
return absint($value); |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* Sanitize price file value |
418
|
|
|
* |
419
|
|
|
* @since 1.6 |
420
|
|
|
* |
421
|
|
|
* @param string $value |
422
|
|
|
* @param array $field_args |
423
|
|
|
* @param object $field |
424
|
|
|
* |
425
|
|
|
* @return mixed |
426
|
|
|
*/ |
427
|
|
|
function give_sanitize_price_field_value( $value, $field_args, $field ){ |
|
|
|
|
428
|
|
|
return give_sanitize_amount( $value ); |
429
|
|
|
} |
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.