Num.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <?php
  2. /**
  3. * Number helper class. Provides additional formatting methods that for working
  4. * with numbers.
  5. *
  6. * @package KO7
  7. * @category Helpers
  8. *
  9. * @copyright (c) 2007-2016 Kohana Team
  10. * @copyright (c) since 2016 Koseven Team
  11. * @license https://koseven.dev/LICENSE
  12. */
  13. class KO7_Num {
  14. const ROUND_HALF_UP = 1;
  15. const ROUND_HALF_DOWN = 2;
  16. const ROUND_HALF_EVEN = 3;
  17. const ROUND_HALF_ODD = 4;
  18. /**
  19. * @var array Valid byte units => power of 2 that defines the unit's size
  20. */
  21. public static $byte_units = [
  22. 'B' => 0,
  23. 'K' => 10,
  24. 'Ki' => 10,
  25. 'KB' => 10,
  26. 'KiB' => 10,
  27. 'M' => 20,
  28. 'Mi' => 20,
  29. 'MB' => 20,
  30. 'MiB' => 20,
  31. 'G' => 30,
  32. 'Gi' => 30,
  33. 'GB' => 30,
  34. 'GiB' => 30,
  35. 'T' => 40,
  36. 'Ti' => 40,
  37. 'TB' => 40,
  38. 'TiB' => 40,
  39. 'P' => 50,
  40. 'Pi' => 50,
  41. 'PB' => 50,
  42. 'PiB' => 50,
  43. 'E' => 60,
  44. 'Ei' => 60,
  45. 'EB' => 60,
  46. 'EiB' => 60,
  47. 'Z' => 70,
  48. 'Zi' => 70,
  49. 'ZB' => 70,
  50. 'ZiB' => 70,
  51. 'Y' => 80,
  52. 'Yi' => 80,
  53. 'YB' => 80,
  54. 'YiB' => 80,
  55. ];
  56. /**
  57. * Returns the English ordinal suffix (th, st, nd, etc) of a number.
  58. *
  59. * echo 2, Num::ordinal(2); // "2nd"
  60. * echo 10, Num::ordinal(10); // "10th"
  61. * echo 33, Num::ordinal(33); // "33rd"
  62. *
  63. * @param integer $number
  64. * @return string
  65. */
  66. public static function ordinal($number)
  67. {
  68. if ($number % 100 > 10 AND $number % 100 < 14)
  69. {
  70. return 'th';
  71. }
  72. switch ($number % 10)
  73. {
  74. case 1:
  75. return 'st';
  76. case 2:
  77. return 'nd';
  78. case 3:
  79. return 'rd';
  80. default:
  81. return 'th';
  82. }
  83. }
  84. /**
  85. * Locale-aware number and monetary formatting.
  86. *
  87. * // In English, "1,200.05"
  88. * // In Spanish, "1200,05"
  89. * // In Portuguese, "1 200,05"
  90. * echo Num::format(1200.05, 2);
  91. *
  92. * // In English, "1,200.05"
  93. * // In Spanish, "1.200,05"
  94. * // In Portuguese, "1.200.05"
  95. * echo Num::format(1200.05, 2, TRUE);
  96. *
  97. * @param float $number number to format
  98. * @param integer $places decimal places
  99. * @param boolean $monetary monetary formatting?
  100. * @return string
  101. * @since 3.0.2
  102. */
  103. public static function format($number, $places, $monetary = FALSE)
  104. {
  105. $info = localeconv();
  106. if ($monetary)
  107. {
  108. $decimal = $info['mon_decimal_point'];
  109. $thousands = $info['mon_thousands_sep'];
  110. }
  111. else
  112. {
  113. $decimal = $info['decimal_point'];
  114. $thousands = $info['thousands_sep'];
  115. }
  116. return number_format($number, $places, $decimal, $thousands);
  117. }
  118. /**
  119. * Round a number to a specified precision, using a specified tie breaking technique
  120. *
  121. * @param float $value Number to round
  122. * @param integer $precision Desired precision
  123. * @param integer $mode Tie breaking mode, accepts the PHP_ROUND_HALF_* constants
  124. * @param boolean $native Set to false to force use of the userland implementation
  125. * @return float Rounded number
  126. */
  127. public static function round($value, $precision = 0, $mode = self::ROUND_HALF_UP, $native = TRUE)
  128. {
  129. if ($native)
  130. {
  131. return round($value, $precision, $mode);
  132. }
  133. if ($mode === self::ROUND_HALF_UP)
  134. {
  135. return round($value, $precision);
  136. }
  137. else
  138. {
  139. $factor = ($precision === 0) ? 1 : pow(10, $precision);
  140. switch ($mode)
  141. {
  142. case self::ROUND_HALF_DOWN:
  143. case self::ROUND_HALF_EVEN:
  144. case self::ROUND_HALF_ODD:
  145. // Check if we have a rounding tie, otherwise we can just call round()
  146. if (($value * $factor) - floor($value * $factor) === 0.5)
  147. {
  148. if ($mode === self::ROUND_HALF_DOWN)
  149. {
  150. // Round down operation, so we round down unless the value
  151. // is -ve because up is down and down is up down there. ;)
  152. $up = ($value < 0);
  153. }
  154. else
  155. {
  156. // Round up if the integer is odd and the round mode is set to even
  157. // or the integer is even and the round mode is set to odd.
  158. // Any other instance round down.
  159. $up = ( ! ( ! (floor($value * $factor) & 1)) === ($mode === self::ROUND_HALF_EVEN));
  160. }
  161. if ($up)
  162. {
  163. $value = ceil($value * $factor);
  164. }
  165. else
  166. {
  167. $value = floor($value * $factor);
  168. }
  169. return $value / $factor;
  170. }
  171. else
  172. {
  173. return round($value, $precision);
  174. }
  175. break;
  176. }
  177. }
  178. }
  179. /**
  180. * Converts a file size number to a byte value. File sizes are defined in
  181. * the format: SB, where S is the size (1, 8.5, 300, etc.) and B is the
  182. * byte unit (K, MiB, GB, etc.). All valid byte units are defined in
  183. * Num::$byte_units
  184. *
  185. * echo Num::bytes('200K'); // 204800
  186. * echo Num::bytes('5MiB'); // 5242880
  187. * echo Num::bytes('1000'); // 1000
  188. * echo Num::bytes('2.5GB'); // 2684354560
  189. *
  190. * @param string $bytes file size in SB format
  191. * @return float
  192. */
  193. public static function bytes($size)
  194. {
  195. // Prepare the size
  196. $size = trim( (string) $size);
  197. // Construct an OR list of byte units for the regex
  198. $accepted = implode('|', array_keys(Num::$byte_units));
  199. // Construct the regex pattern for verifying the size format
  200. $pattern = '/^([0-9]+(?:\.[0-9]+)?)('.$accepted.')?$/Di';
  201. // Verify the size format and store the matching parts
  202. if ( ! preg_match($pattern, $size, $matches))
  203. throw new KO7_Exception('The byte unit size, ":size", is improperly formatted.', [
  204. ':size' => $size,
  205. ]);
  206. // Find the float value of the size
  207. $size = (float) $matches[1];
  208. // Find the actual unit, assume B if no unit specified
  209. $unit = Arr::get($matches, 2, 'B');
  210. // Convert the size into bytes
  211. $bytes = $size * pow(2, Num::$byte_units[$unit]);
  212. return $bytes;
  213. }
  214. }