Num.php 5.4 KB

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