PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.6.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.6.0
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / vendor / wpfluent / framework / src / WPFluent / Support / Number.php

Number.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.6.0, at vendor/wpfluent/framework/src/WPFluent/Support/Number.php

394 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\Framework\Support;
4
5 use RangeException;
6 use FluentCommunity\Framework\Foundation\App;
7 use FluentCommunity\Framework\Support\InvalidArgumentException;
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 return number_format(
24 $value,
25 absint($dec),
26 // @phpstan-ignore-next-line
27 $locale->number_format['decimal_point'],
28 // @phpstan-ignore-next-line
29 $locale->number_format['thousands_sep']
30 );
31 }
32
33 /**
34 * Format a number as int
35 *
36 * @param int|float $val
37 * @return int
38 */
39 public static function toInt($val)
40 {
41 return intval($val);
42 }
43
44 /**
45 * Cast to float, optionally rounding to $dec decimals.
46 *
47 * Default rounds to 2 decimals (preserved for backwards compatibility).
48 * Pass $dec = null to skip rounding entirely and return the raw cast.
49 *
50 * @param int|float|string $val
51 * @param int|null $dec Decimal places to round to, or null to skip rounding
52 * @return float
53 */
54 public static function toFloat($val, $dec = 2)
55 {
56 $val = (float) $val;
57
58 return $dec === null ? $val : round($val, $dec);
59 }
60
61 /**
62 * Convert a value to bool.
63 *
64 * Standard PHP cast semantics: 0, 0.0, '', '0' → false; everything else → true.
65 *
66 * @param mixed $val
67 * @return bool
68 */
69 public static function toBool($val)
70 {
71 return (bool) $val;
72 }
73
74 /**
75 * Format a number to currency depending on the locale
76 *
77 * @param int|float $value
78 * @param array $options
79 * @return string Formatted number with currency symbol
80 */
81 public static function toCurrency($value, $options = [])
82 {
83 $defaults = [
84 'currency_symbol' => '$',
85 'number_of_decimals' => 2,
86 'space_with_currency' => 0,
87 'currency_position' => 'left',
88 ];
89
90 $args = wp_parse_args($options, $defaults);
91
92 // Format the absolute number; the negative sign is prepended
93 // separately below so it lands outside the currency symbol
94 // (-$1,234.56 rather than $-1,234.56).
95 $isNegative = $value < 0;
96
97 $formattedNumber = static::format(
98 abs($value), $args['number_of_decimals']
99 );
100
101 // Prepare the currency symbol and spacing
102 $symbol = $args['currency_symbol'];
103
104 // Apply the framework's plugin-prefixed `currency_symbol` filter
105 // so each plugin can wire its own ecommerce integration without
106 // colliding across plugins on the same site (each gets its own
107 // hook namespace via app.hook_prefix).
108 if ($app = App::getInstance()) {
109 $symbol = $app->applyCustomFilters(
110 '_currency_symbol', $symbol, $value, $args
111 );
112 }
113
114 $space = $args['space_with_currency'] ? ' ' : '';
115
116 // Build the body based on symbol position, then prepend the
117 // negative sign in front of the whole thing so it reads
118 // -$1,234.56 (left) or -1,234.56$ (right).
119 if ($args['currency_position'] === 'left') {
120 $body = $symbol . $space . $formattedNumber;
121 } else {
122 $body = $formattedNumber . $space . $symbol;
123 }
124
125 return $isNegative ? '-' . $body : $body;
126 }
127
128 /**
129 * Notation to numbers.
130 *
131 * This function transforms the php.ini notation
132 * for numbers (like '2M') to an integer.
133 *
134 * @param string $num
135 * @return int
136 */
137 public static function notationToNum($num)
138 {
139 $num = trim($num);
140
141 if ($num === '') {
142 throw new InvalidArgumentException(
143 'Input cannot be empty.'
144 );
145 }
146
147 // Normalize: uppercase, drop interior whitespace, strip optional
148 // trailing 'B' or 'IB' so 'KB', 'MB', 'GB', 'KiB', 'MiB', 'GiB',
149 // and '2 M' all parse identically to the bare 'K'/'M'/'G' form.
150 $normalized = preg_replace('/\s+/', '', strtoupper($num));
151 $normalized = preg_replace('/I?B$/', '', $normalized);
152
153 if ($normalized === '') {
154 throw new InvalidArgumentException(
155 'Invalid numeric value in notation.'
156 );
157 }
158
159 $unit = substr($normalized, -1);
160
161 // Determine if the last char is a recognized unit
162 $units = ['P', 'T', 'G', 'M', 'K'];
163
164 if (in_array($unit, $units, true)) {
165 // Numeric part without the unit
166 $numberPart = substr($normalized, 0, -1);
167
168 if (!is_numeric($numberPart)) {
169 throw new InvalidArgumentException(
170 'Invalid numeric value in notation.'
171 );
172 }
173
174 $value = (float) $numberPart;
175
176 // Multiply based on unit with fall-through logic
177 switch ($unit) {
178 case 'P':
179 $value *= 1024;
180 // no break
181 case 'T':
182 $value *= 1024;
183 // no break
184 case 'G':
185 $value *= 1024;
186 // no break
187 case 'M':
188 $value *= 1024;
189 // no break
190 case 'K':
191 $value *= 1024;
192 break;
193 }
194 } else {
195 // No unit, just parse the number directly
196 if (!is_numeric($normalized)) {
197 throw new InvalidArgumentException(
198 'Invalid numeric value without unit.'
199 );
200 }
201
202 $value = (float) $normalized;
203 }
204
205 // Guard against silent integer overflow (esp. on 32-bit builds).
206 if ($value > PHP_INT_MAX || $value < PHP_INT_MIN) {
207 throw new RangeException(
208 'Value exceeds PHP_INT_MAX.'
209 );
210 }
211
212 // Return as integer (bytes)
213 return (int) round($value);
214 }
215
216 /**
217 * Calculates the percentage/$percent from the $value/$total
218 *
219 * @param int|float $percent
220 * @param int|float $total
221 * @return int|float
222 */
223 public static function getPercentage($percent, $total)
224 {
225 return ($percent / 100) * $total;
226 }
227
228 /**
229 * Calculate what percentage $value is of $total.
230 *
231 * Returns 0.0 when $total is 0 (avoids divide-by-zero) — useful for
232 * dashboards and reports where "0 out of 0" should display as 0%.
233 *
234 * Examples:
235 * percentageOf(50, 200) → 25.0
236 * percentageOf(150, 100) → 150.0
237 * percentageOf(0, 0) → 0.0
238 *
239 * @param int|float|string $value
240 * @param int|float|string $total
241 * @return float
242 */
243 public static function percentageOf($value, $total)
244 {
245 $total = (float) $total;
246
247 if ($total === 0.0) {
248 return 0.0;
249 }
250
251 return ((float) $value / $total) * 100;
252 }
253
254 /**
255 * Converts a number of bytes to human readable format
256 * using the maximum unit available to convert the bytes.
257 *
258 * @param int|float $bytes
259 * @param integer $decimals
260 * @return string Formatted size units of bytes, i.e: 1mb/1gb e.t.c.
261 */
262 public static function formatBytes($bytes, $decimals = 0)
263 {
264 return size_format($bytes, $decimals);
265 }
266
267 /**
268 * Makes an ordinal number from the integer.
269 *
270 * Floats are truncated toward zero (2.7 → 2nd, -2.7 → -2nd).
271 * Negatives keep their sign (-21 → -21st, -11 → -11th).
272 * Non-numeric input is returned unchanged.
273 *
274 * @param int|float|string $number
275 * @return string The ordinal number, i.e: 1st, 5th e.t.c.
276 */
277 public static function toOrdinal($number)
278 {
279 if (!is_numeric($number)) {
280 return $number;
281 }
282
283 $int = (int) $number;
284 $abs = abs($int);
285 $sign = $int < 0 ? '-' : '';
286
287 if (($abs % 100) >= 11 && ($abs % 100) <= 13) {
288 $suffix = 'th';
289 } else {
290 switch ($abs % 10) {
291 case 1:
292 $suffix = 'st';
293 break;
294 case 2:
295 $suffix = 'nd';
296 break;
297 case 3:
298 $suffix = 'rd';
299 break;
300 default:
301 $suffix = 'th';
302 break;
303 }
304 }
305
306 return $sign . $abs . $suffix;
307 }
308
309 /**
310 * Convert the number to its human readable equivalent.
311 *
312 * @param int $number
313 * @param int $precision
314 * @param int|null $maxPrecision
315 * @return string
316 */
317 public static function forHumans(
318 $number, $precision = 0, $maxPrecision = null, $abbr = false
319 )
320 {
321 return static::summarize($number, $precision, $maxPrecision, $abbr ? [
322 3 => 'K',
323 6 => 'M',
324 9 => 'B',
325 12 => 'T',
326 15 => 'Q',
327 18 => 'Qi',
328 ] : [
329 3 => ' thousand',
330 6 => ' million',
331 9 => ' billion',
332 12 => ' trillion',
333 15 => ' quadrillion',
334 18 => ' quintillion',
335 ]);
336 }
337
338 /**
339 * Convert the number to its human readable equivalent.
340 *
341 * @param int $number
342 * @param int $precision
343 * @param int|null $maxPrecision
344 * @param array $units
345 * @return string
346 */
347 protected static function summarize(
348 $number, $precision = 0, $maxPrecision = null, $units = []
349 )
350 {
351 if (empty($units)) {
352 $units = [
353 3 => 'K',
354 6 => 'M',
355 9 => 'B',
356 12 => 'T',
357 15 => 'Q',
358 18 => 'Qi',
359 ];
360 }
361
362 // Recursion threshold = the smallest exponent above the highest
363 // mapped unit. Numbers at or beyond this fall through the table
364 // and get composite naming (e.g. '1KQi' for 1e21 = 1 sextillion).
365 $topExponent = max(array_keys($units));
366 $overflow = pow(10, $topExponent + 3);
367
368 switch (true) {
369 case floatval($number) === 0.0:
370 return $precision > 0 ? static::format(0, $precision) : '0';
371
372 case $number < 0:
373 return sprintf('-%s', static::summarize(
374 abs($number), $precision, $maxPrecision, $units
375 ));
376
377 case $number >= $overflow:
378 return sprintf('%s'.end($units), static::summarize(
379 $number / pow(10, $topExponent), $precision, $maxPrecision, $units
380 ));
381 }
382
383 $numberExponent = floor(log10($number));
384 $displayExponent = $numberExponent - ($numberExponent % 3);
385 $number /= pow(10, $displayExponent);
386
387 return trim(
388 sprintf('%s%s', static::format(
389 $number, $precision
390 ), $units[$displayExponent] ?? '')
391 );
392 }
393 }
394