PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Math/Rand.php

https://bitbucket.org/gencer/zf2
PHP | 219 lines | 128 code | 22 blank | 69 comment | 27 complexity | 4b39238addb2ff530e185b84c9b07cf0 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Math;
  10. use RandomLib;
  11. /**
  12. * Pseudorandom number generator (PRNG)
  13. */
  14. abstract class Rand
  15. {
  16. /**
  17. * Alternative random byte generator using RandomLib
  18. *
  19. * @var RandomLib\Generator
  20. */
  21. protected static $generator = null;
  22. /**
  23. * Generate random bytes using OpenSSL or Mcrypt and mt_rand() as fallback
  24. *
  25. * @param int $length
  26. * @param bool $strong true if you need a strong random generator (cryptography)
  27. * @return string
  28. * @throws Exception\RuntimeException
  29. */
  30. public static function getBytes($length, $strong = false)
  31. {
  32. $length = (int) $length;
  33. if ($length <= 0) {
  34. return false;
  35. }
  36. if (function_exists('openssl_random_pseudo_bytes')
  37. && ((PHP_VERSION_ID >= 50304)
  38. || strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
  39. ) {
  40. $bytes = openssl_random_pseudo_bytes($length, $usable);
  41. if (true === $usable) {
  42. return $bytes;
  43. }
  44. }
  45. if (function_exists('mcrypt_create_iv')
  46. && ((PHP_VERSION_ID >= 50307)
  47. || strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
  48. ) {
  49. $bytes = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
  50. if ($bytes !== false && strlen($bytes) === $length) {
  51. return $bytes;
  52. }
  53. }
  54. $checkAlternatives = (file_exists('/dev/urandom') && is_readable('/dev/urandom'))
  55. || class_exists('\\COM', false);
  56. if (true === $strong && false === $checkAlternatives) {
  57. throw new Exception\RuntimeException (
  58. 'This PHP environment doesn\'t support secure random number generation. ' .
  59. 'Please consider installing the OpenSSL and/or Mcrypt extensions'
  60. );
  61. }
  62. $generator = self::getAlternativeGenerator();
  63. return $generator->generate($length);
  64. }
  65. /**
  66. * Retrieve a fallback/alternative RNG generator
  67. *
  68. * @return RandomLib\Generator
  69. */
  70. public static function getAlternativeGenerator()
  71. {
  72. if (null !== static::$generator) {
  73. return static::$generator;
  74. }
  75. if (!class_exists('RandomLib\\Factory')) {
  76. throw new Exception\RuntimeException(
  77. 'The RandomLib fallback pseudorandom number generator (PRNG) '
  78. . ' must be installed in the absence of the OpenSSL and '
  79. . 'Mcrypt extensions'
  80. );
  81. }
  82. $factory = new RandomLib\Factory;
  83. $factory->registerSource(
  84. 'HashTiming',
  85. 'Zend\Math\Source\HashTiming'
  86. );
  87. static::$generator = $factory->getMediumStrengthGenerator();
  88. return static::$generator;
  89. }
  90. /**
  91. * Generate random boolean
  92. *
  93. * @param bool $strong true if you need a strong random generator (cryptography)
  94. * @return bool
  95. */
  96. public static function getBoolean($strong = false)
  97. {
  98. $byte = static::getBytes(1, $strong);
  99. return (bool) (ord($byte) % 2);
  100. }
  101. /**
  102. * Generate a random integer between $min and $max
  103. *
  104. * @param int $min
  105. * @param int $max
  106. * @param bool $strong true if you need a strong random generator (cryptography)
  107. * @return int
  108. * @throws Exception\DomainException
  109. */
  110. public static function getInteger($min, $max, $strong = false)
  111. {
  112. if ($min > $max) {
  113. throw new Exception\DomainException(
  114. 'The min parameter must be lower than max parameter'
  115. );
  116. }
  117. $range = $max - $min;
  118. if ($range == 0) {
  119. return $max;
  120. } elseif ($range > PHP_INT_MAX || is_float($range)) {
  121. throw new Exception\DomainException(
  122. 'The supplied range is too great to generate'
  123. );
  124. }
  125. // calculate number of bits required to store range on this machine
  126. $r = $range;
  127. $bits = 0;
  128. while ($r) {
  129. $bits++;
  130. $r >>= 1;
  131. }
  132. $bits = (int) max($bits, 1);
  133. $bytes = (int) max(ceil($bits / 8), 1);
  134. $filter = (int) ((1 << $bits) - 1);
  135. do {
  136. $rnd = hexdec(bin2hex(static::getBytes($bytes, $strong)));
  137. $rnd &= $filter;
  138. } while ($rnd > $range);
  139. return ($min + $rnd);
  140. }
  141. /**
  142. * Generate random float (0..1)
  143. * This function generates floats with platform-dependent precision
  144. *
  145. * PHP uses double precision floating-point format (64-bit) which has
  146. * 52-bits of significand precision. We gather 7 bytes of random data,
  147. * and we fix the exponent to the bias (1023). In this way we generate
  148. * a float of 1.mantissa.
  149. *
  150. * @param bool $strong true if you need a strong random generator (cryptography)
  151. * @return float
  152. */
  153. public static function getFloat($strong = false)
  154. {
  155. $bytes = static::getBytes(7, $strong);
  156. $bytes[6] = $bytes[6] | chr(0xF0);
  157. $bytes .= chr(63); // exponent bias (1023)
  158. list(, $float) = unpack('d', $bytes);
  159. return ($float - 1);
  160. }
  161. /**
  162. * Generate a random string of specified length.
  163. *
  164. * Uses supplied character list for generating the new string.
  165. * If no character list provided - uses Base 64 character set.
  166. *
  167. * @param int $length
  168. * @param string|null $charlist
  169. * @param bool $strong true if you need a strong random generator (cryptography)
  170. * @return string
  171. * @throws Exception\DomainException
  172. */
  173. public static function getString($length, $charlist = null, $strong = false)
  174. {
  175. if ($length < 1) {
  176. throw new Exception\DomainException('Length should be >= 1');
  177. }
  178. // charlist is empty or not provided
  179. if (empty($charlist)) {
  180. $numBytes = ceil($length * 0.75);
  181. $bytes = static::getBytes($numBytes, $strong);
  182. return substr(rtrim(base64_encode($bytes), '='), 0, $length);
  183. }
  184. $listLen = strlen($charlist);
  185. if ($listLen == 1) {
  186. return str_repeat($charlist, $length);
  187. }
  188. $bytes = static::getBytes($length, $strong);
  189. $pos = 0;
  190. $result = '';
  191. for ($i = 0; $i < $length; $i++) {
  192. $pos = ($pos + ord($bytes[$i])) % $listLen;
  193. $result .= $charlist[$pos];
  194. }
  195. return $result;
  196. }
  197. }