PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.21
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.21
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / vendor / wpfluent / framework / src / WPFluent / Support / Number.php

Number.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.3.21, at vendor/wpfluent/framework/src/WPFluent/Support/Number.php

560 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\Framework\Support;
4
5 use FluentCart\Framework\Support\InvalidArgumentException;
6 use RangeException;
7 use TypeError;
8
9 class Number
10 {
11 /**
12 * Format a number depending on the locale
13 *
14 * @param int|float $value
15 * @param integer $dec
16 * @return string Formatted number
17 * @see https://developer.wordpress.org/reference/functions/number_format_i18n
18 */
19 public static function format($value, $dec = 0)
20 {
21 $locale = Locale::init();
22
23 // @phpstan-ignore-next-line
24 if (isset($locale)) {
25 $formatted = number_format(
26 $value,
27 absint($dec),
28 // @phpstan-ignore-next-line
29 $locale->number_format['decimal_point'],
30 // @phpstan-ignore-next-line
31 $locale->number_format['thousands_sep']
32 );
33 } else {
34 $formatted = number_format($value, absint($dec));
35 }
36
37 return $formatted;
38 }
39
40 /**
41 * Format a number as int
42 *
43 * @param int|float $val
44 * @return int
45 */
46 public static function toInt($val)
47 {
48 return intval($val);
49 }
50
51 /**
52 * Format a number as float
53 *
54 * @param int|float $val
55 * @param integer $dec
56 * @return float
57 */
58 public static function toFloat($val, $dec = 2)
59 {
60 return round((float) $val, $dec);
61 }
62
63 /**
64 * Convert a number to bool
65 *
66 * @param int $val
67 * @return bool
68 */
69 public static function toBool($val)
70 {
71 if (is_bool($val)) {
72 return $val;
73 }
74 return (bool) $val;
75 }
76
77 /**
78 * Format a number to currency depending on the locale
79 *
80 * @param int|float $value
81 * @param array $options
82 * @return string Formatted number with currency symbol
83 */
84 public static function toCurrency($value, $options = [])
85 {
86 $locale = Locale::init();
87
88 $defaults = [
89 'currency_symbol' => '$',
90 'number_of_decimals' => 2,
91 'space_with_currency' => 0,
92 'currency_position' => 'left',
93 ];
94
95 $args = wp_parse_args($options, $defaults);
96
97 // Format the number with the
98 // specified number of decimals
99 $formattedNumber = static::format(
100 $value, $args['number_of_decimals']
101 );
102
103 // Prepare the currency symbol and spacing
104 $symbol = $args['currency_symbol'];
105 $space = $args['space_with_currency'] ? ' ' : '';
106
107 // Return the formatted currency string based
108 // on the position of the currency symbol
109 if ($args['currency_position'] === 'left') {
110 return $symbol . $space . $formattedNumber;
111 } else {
112 return $formattedNumber . $space . $symbol;
113 }
114 }
115
116 /**
117 * Notation to numbers.
118 *
119 * This function transforms the php.ini notation
120 * for numbers (like '2M') to an integer.
121 *
122 * @param string $num
123 * @return int
124 */
125 public static function notationToNum($num)
126 {
127 $num = trim($num);
128
129 if ($num === '') {
130 throw new InvalidArgumentException(
131 'Input cannot be empty.'
132 );
133 }
134
135 // Extract the last character to check for unit
136 $unit = strtoupper(substr($num, -1));
137
138 // Determine if the last char is a recognized unit
139 $units = ['P', 'T', 'G', 'M', 'K'];
140
141 if (in_array($unit, $units, true)) {
142 // Numeric part without the unit
143 $numberPart = substr($num, 0, -1);
144
145 if (!is_numeric($numberPart)) {
146 throw new InvalidArgumentException(
147 'Invalid numeric value in notation.'
148 );
149 }
150
151 $value = (float) $numberPart;
152
153 // Multiply based on unit with fall-through logic
154 switch ($unit) {
155 case 'P':
156 $value *= 1024;
157 // no break
158 case 'T':
159 $value *= 1024;
160 // no break
161 case 'G':
162 $value *= 1024;
163 // no break
164 case 'M':
165 $value *= 1024;
166 // no break
167 case 'K':
168 $value *= 1024;
169 break;
170 }
171 } else {
172 // No unit, just parse the number directly
173 if (!is_numeric($num)) {
174 throw new InvalidArgumentException(
175 'Invalid numeric value without unit.'
176 );
177 }
178
179 $value = (float) $num;
180 }
181
182 // Return as integer (bytes)
183 return (int) round($value);
184 }
185
186 /**
187 * Calculates the percentage/$percent from the $value/$total
188 *
189 * @param int|float $percent
190 * @param int|float $total
191 * @return int|float
192 */
193 public static function getPercentage($percent, $total)
194 {
195 return ($percent / 100) * $total;
196 }
197
198 /**
199 * Converts a number of bytes to human readable format
200 * using the maximum unit available to convert the bytes.
201 *
202 * @param int|float $bytes
203 * @param integer $decimals
204 * @return string Formatted size units of bytes, i.e: 1mb/1gb e.t.c.
205 */
206 public static function formatBytes($bytes, $decimals = 0)
207 {
208 return size_format($bytes, $decimals);
209 }
210
211 /**
212 * Makes an ordinal number from the integer
213 *
214 * @param int $number
215 * @return string The ordinal number, i.e: 1st, 5th e.t.c.
216 */
217 public static function toOrdinal($number)
218 {
219 if (!is_numeric($number)) {
220 return $number;
221 }
222
223 if (($number % 100) >= 11 && ($number % 100) <= 13) {
224 $suffix = 'th';
225 } else {
226 switch ($number % 10) {
227 case 1:
228 $suffix = 'st';
229 break;
230 case 2:
231 $suffix = 'nd';
232 break;
233 case 3:
234 $suffix = 'rd';
235 break;
236 default:
237 $suffix = 'th';
238 break;
239 }
240 }
241
242 return $number . $suffix;
243 }
244
245 /**
246 * Convert the number to its human readable equivalent.
247 *
248 * @param int $number
249 * @param int $precision
250 * @param int|null $maxPrecision
251 * @return string
252 */
253 public static function forHumans(
254 $number, $precision = 0, $maxPrecision = null, $abbr = false
255 )
256 {
257 return static::summarize($number, $precision, $maxPrecision, $abbr ? [
258 3 => 'K',
259 6 => 'M',
260 9 => 'B',
261 12 => 'T',
262 15 => 'Q',
263 ] : [
264 3 => ' thousand',
265 6 => ' million',
266 9 => ' billion',
267 12 => ' trillion',
268 15 => ' quadrillion',
269 ]);
270 }
271
272 /**
273 * Convert the number to its human readable equivalent.
274 *
275 * @param int $number
276 * @param int $precision
277 * @param int|null $maxPrecision
278 * @param array $units
279 * @return string
280 */
281 protected static function summarize(
282 $number, $precision = 0, $maxPrecision = null, $units = []
283 )
284 {
285 if (empty($units)) {
286 $units = [
287 3 => 'K',
288 6 => 'M',
289 9 => 'B',
290 12 => 'T',
291 15 => 'Q',
292 ];
293 }
294
295 switch (true) {
296 case floatval($number) === 0.0:
297 return $precision > 0 ? static::format(0, $precision) : '0';
298
299 case $number < 0:
300 return sprintf('-%s', static::summarize(
301 abs($number), $precision, $maxPrecision, $units
302 ));
303
304 case $number >= 1e15:
305 return sprintf('%s'.end($units), static::summarize(
306 $number / 1e15, $precision, $maxPrecision, $units
307 ));
308 }
309
310 $numberExponent = floor(log10($number));
311 $displayExponent = $numberExponent - ($numberExponent % 3);
312 $number /= pow(10, $displayExponent);
313
314 return trim(
315 sprintf('%s%s', static::format(
316 $number, $precision
317 ), $units[$displayExponent] ?? '')
318 );
319 }
320
321 /**
322 * Convert a numeric value to words.
323 *
324 * Example:
325 * 199900010500.91 → "one hundred and ninety-nine billion nine hundred million
326 * ten thousand five hundred and 91 cents"
327 *
328 * @param int|float|string $number The number to convert.
329 * @param array $options Optional configuration:
330 * - 'thousand_separator' => string (default ',')
331 * - 'decimal_separator' => string (default '.')
332 * - 'round' => bool (default false) Whether to round floats.
333 * - 'inCents' => bool (default true) Include the fraction as cents.
334 *
335 * @return string The human-readable word representation of the number.
336 *
337 * @throws \RangeException If the number is out of acceptable bounds.
338 */
339 public static function inWords($number, $options = [])
340 {
341 return (new NumberToWords)->inWords($number, $options);
342 }
343
344 /**
345 * Make words from integers
346 *
347 * @param int $number
348 * @param array $numberWords
349 * @param array $tensWords
350 * @return string
351 */
352 protected static function spellInteger($number, $numberWords, $tensWords)
353 {
354 $result = '';
355
356 $result .= static::convertToWords($number, $numberWords, $tensWords);
357
358 return trim($result);
359 }
360
361 /**
362 * Make words from fraction part
363 *
364 * @param int $number
365 * @param array $numberWords
366 * @return string
367 */
368 protected static function spellDecimal($number, $numberWords, $decimalSeparator)
369 {
370 $result = '';
371
372 $decimalPosition = strpos($number, $decimalSeparator);
373
374 if ($decimalPosition !== false) {
375
376 $decimalAsString = substr($number, $decimalPosition + 1);
377
378 for ($i = 0; $i < strlen($decimalAsString); $i++) {
379
380 $digit = intval($decimalAsString[$i]);
381
382 $result .= $numberWords[$digit] . ' ';
383 }
384 } else {
385 $result .= 'zero';
386 }
387
388 return trim($result);
389 }
390
391 /**
392 * Main function to make the unit words
393 *
394 * @param int|float $number
395 * @param array $numberWords
396 * @param array $tensWords
397 * @return string
398 */
399 protected static function convertToWords($number, $numberWords, $tensWords)
400 {
401 if ($number < 20) {
402 return $numberWords[$number];
403
404 } elseif ($number < 100) {
405
406 $result = $tensWords[intval($number / 10)];
407
408 if (intval($number) % 10 !== 0) {
409 $result .= ' ' . static::convertToWords(
410 intval($number) % 10, $numberWords, $tensWords
411 );
412 }
413
414 return $result;
415
416 } elseif ($number < 1000) {
417
418 $result = $numberWords[intval($number / 100)] . ' hundred';
419
420 if (intval($number) % 100 !== 0) {
421 $result .= ' and ' . static::convertToWords(
422 intval($number) % 100, $numberWords, $tensWords
423 );
424 }
425
426 return $result;
427
428 } elseif ($number < 1000000) {
429
430 $result = static::convertToWords(
431 intval($number / 1000), $numberWords, $tensWords
432 );
433
434 $result .= ' thousand';
435
436 if (intval($number) % 1000 !== 0) {
437 $result .= ' ' . static::convertToWords(
438 intval($number) % 1000, $numberWords, $tensWords
439 );
440 }
441
442 return $result;
443
444 } elseif ($number < 1000000000) {
445
446 $result = static::convertToWords(
447 intval($number / 1000000), $numberWords, $tensWords
448 );
449
450 $result .= ' million';
451
452 if (intval($number) % 1000000 !== 0) {
453 $result .= ' ' . static::convertToWords(
454 intval($number) % 1000000, $numberWords, $tensWords
455 );
456 }
457
458 return $result;
459
460 } elseif ($number < 1000000000000) {
461
462 $result = static::convertToWords(
463 intval($number / 1000000000), $numberWords, $tensWords
464 );
465
466 $result .= ' billion';
467
468 if (intval($number) % 1000000000 !== 0) {
469 $result .= ' ' . static::convertToWords(
470 intval($number) % 1000000000, $numberWords, $tensWords
471 );
472 }
473
474 return $result;
475
476 } elseif ($number < 1000000000000000) {
477
478 $result = static::convertToWords(
479 intval($number / 1000000000000), $numberWords, $tensWords
480 );
481
482 $result .= ' trillion';
483
484 if (intval($number) % 1000000000000 !== 0) {
485 $result .= ' ' . static::convertToWords(
486 intval($number) % 1000000000000, $numberWords, $tensWords
487 );
488 }
489
490 return $result;
491
492 } elseif (intval($number) < 1000000000000000000) {
493
494 $result = static::convertToWords(
495 intval($number / 1000000000000000), $numberWords, $tensWords
496 );
497
498 $result .= ' quadrillion';
499
500 if (intval($number) % 1000000000000000 !== 0) {
501 $result .= ' ' . static::convertToWords(
502 intval($number) % 1000000000000000, $numberWords, $tensWords
503 );
504 }
505
506 return $result;
507
508 } elseif (intval($number) < 1000000000000000000000) {
509
510 $result = static::convertToWords(
511 intval($number / 1000000000000000000), $numberWords, $tensWords
512 );
513
514 $result .= ' quintillion';
515
516 if (intval($number) % 1000000000000000000 !== 0) {
517 $result .= ' ' . static::convertToWords(
518 intval($number) % 1000000000000000000, $numberWords, $tensWords
519 );
520 }
521
522 return $result;
523 } else {
524 throw new RangeException('Out of range', 500);
525 }
526 }
527
528 /**
529 * Make words for cents
530 *
531 * @param int|float $cents
532 * @param array $numberWords
533 * @param array $tensWords
534 * @return string
535 */
536 protected static function toCents($cents, $numberWords, $tensWords)
537 {
538 if (array_key_exists($cents, $numberWords)) {
539 return $numberWords[$cents];
540 } elseif (array_key_exists($cents, $tensWords)) {
541 return $tensWords[$cents];
542 }
543
544 $result = '';
545 $tens = substr(floor($cents / 10) * 10, 0, 1);
546 $unitsPart = $cents % 10;
547
548 if (array_key_exists($tens, $tensWords)) {
549 $result .= $tensWords[$tens];
550 if ($unitsPart > 0) {
551 $result .= ' ' . $numberWords[$unitsPart];
552 }
553 } elseif (array_key_exists($unitsPart, $numberWords)) {
554 $result .= $numberWords[$unitsPart];
555 }
556
557 return $result;
558 }
559 }
560