123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- <?php
- /**
- * Number helper class. Provides additional formatting methods that for working
- * with numbers.
- *
- * @package KO7
- * @category Helpers
- *
- * @copyright (c) 2007-2016 Kohana Team
- * @copyright (c) since 2016 Koseven Team
- * @license https://koseven.dev/LICENSE
- */
- class KO7_Num {
- const ROUND_HALF_UP = 1;
- const ROUND_HALF_DOWN = 2;
- const ROUND_HALF_EVEN = 3;
- const ROUND_HALF_ODD = 4;
- /**
- * @var array Valid byte units => power of 2 that defines the unit's size
- */
- public static $byte_units = [
- 'B' => 0,
- 'K' => 10,
- 'Ki' => 10,
- 'KB' => 10,
- 'KiB' => 10,
- 'M' => 20,
- 'Mi' => 20,
- 'MB' => 20,
- 'MiB' => 20,
- 'G' => 30,
- 'Gi' => 30,
- 'GB' => 30,
- 'GiB' => 30,
- 'T' => 40,
- 'Ti' => 40,
- 'TB' => 40,
- 'TiB' => 40,
- 'P' => 50,
- 'Pi' => 50,
- 'PB' => 50,
- 'PiB' => 50,
- 'E' => 60,
- 'Ei' => 60,
- 'EB' => 60,
- 'EiB' => 60,
- 'Z' => 70,
- 'Zi' => 70,
- 'ZB' => 70,
- 'ZiB' => 70,
- 'Y' => 80,
- 'Yi' => 80,
- 'YB' => 80,
- 'YiB' => 80,
- ];
- /**
- * Returns the English ordinal suffix (th, st, nd, etc) of a number.
- *
- * echo 2, Num::ordinal(2); // "2nd"
- * echo 10, Num::ordinal(10); // "10th"
- * echo 33, Num::ordinal(33); // "33rd"
- *
- * @param integer $number
- * @return string
- */
- public static function ordinal($number)
- {
- if ($number % 100 > 10 AND $number % 100 < 14)
- {
- return 'th';
- }
- switch ($number % 10)
- {
- case 1:
- return 'st';
- case 2:
- return 'nd';
- case 3:
- return 'rd';
- default:
- return 'th';
- }
- }
- /**
- * Locale-aware number and monetary formatting.
- *
- * // In English, "1,200.05"
- * // In Spanish, "1200,05"
- * // In Portuguese, "1 200,05"
- * echo Num::format(1200.05, 2);
- *
- * // In English, "1,200.05"
- * // In Spanish, "1.200,05"
- * // In Portuguese, "1.200.05"
- * echo Num::format(1200.05, 2, TRUE);
- *
- * @param float $number number to format
- * @param integer $places decimal places
- * @param boolean $monetary monetary formatting?
- * @return string
- * @since 3.0.2
- */
- public static function format($number, $places, $monetary = FALSE)
- {
- $info = localeconv();
- if ($monetary)
- {
- $decimal = $info['mon_decimal_point'];
- $thousands = $info['mon_thousands_sep'];
- }
- else
- {
- $decimal = $info['decimal_point'];
- $thousands = $info['thousands_sep'];
- }
- return number_format($number, $places, $decimal, $thousands);
- }
- /**
- * Round a number to a specified precision, using a specified tie breaking technique
- *
- * @param float $value Number to round
- * @param integer $precision Desired precision
- * @param integer $mode Tie breaking mode, accepts the PHP_ROUND_HALF_* constants
- * @param boolean $native Set to false to force use of the userland implementation
- * @return float Rounded number
- */
- public static function round($value, $precision = 0, $mode = self::ROUND_HALF_UP, $native = TRUE)
- {
- if ($native)
- {
- return round($value, $precision, $mode);
- }
- if ($mode === self::ROUND_HALF_UP)
- {
- return round($value, $precision);
- }
- else
- {
- $factor = ($precision === 0) ? 1 : pow(10, $precision);
- switch ($mode)
- {
- case self::ROUND_HALF_DOWN:
- case self::ROUND_HALF_EVEN:
- case self::ROUND_HALF_ODD:
- // Check if we have a rounding tie, otherwise we can just call round()
- if (($value * $factor) - floor($value * $factor) === 0.5)
- {
- if ($mode === self::ROUND_HALF_DOWN)
- {
- // Round down operation, so we round down unless the value
- // is -ve because up is down and down is up down there. ;)
- $up = ($value < 0);
- }
- else
- {
- // Round up if the integer is odd and the round mode is set to even
- // or the integer is even and the round mode is set to odd.
- // Any other instance round down.
- $up = ( ! ( ! (floor($value * $factor) & 1)) === ($mode === self::ROUND_HALF_EVEN));
- }
- if ($up)
- {
- $value = ceil($value * $factor);
- }
- else
- {
- $value = floor($value * $factor);
- }
- return $value / $factor;
- }
- else
- {
- return round($value, $precision);
- }
- break;
- }
- }
- }
- /**
- * Converts a file size number to a byte value. File sizes are defined in
- * the format: SB, where S is the size (1, 8.5, 300, etc.) and B is the
- * byte unit (K, MiB, GB, etc.). All valid byte units are defined in
- * Num::$byte_units
- *
- * echo Num::bytes('200K'); // 204800
- * echo Num::bytes('5MiB'); // 5242880
- * echo Num::bytes('1000'); // 1000
- * echo Num::bytes('2.5GB'); // 2684354560
- *
- * @param string $bytes file size in SB format
- * @return float
- */
- public static function bytes($size)
- {
- // Prepare the size
- $size = trim( (string) $size);
- // Construct an OR list of byte units for the regex
- $accepted = implode('|', array_keys(Num::$byte_units));
- // Construct the regex pattern for verifying the size format
- $pattern = '/^([0-9]+(?:\.[0-9]+)?)('.$accepted.')?$/Di';
- // Verify the size format and store the matching parts
- if ( ! preg_match($pattern, $size, $matches))
- throw new KO7_Exception('The byte unit size, ":size", is improperly formatted.', [
- ':size' => $size,
- ]);
- // Find the float value of the size
- $size = (float) $matches[1];
- // Find the actual unit, assume B if no unit specified
- $unit = Arr::get($matches, 2, 'B');
- // Convert the size into bytes
- $bytes = $size * pow(2, Num::$byte_units[$unit]);
- return $bytes;
- }
- }
|