Skip to content

Instantly share code, notes, and snippets.

@jrnickell
Last active August 29, 2015 14:17
Show Gist options
  • Save jrnickell/bd5c3d5b5e6f71bca4b9 to your computer and use it in GitHub Desktop.
Save jrnickell/bd5c3d5b5e6f71bca4b9 to your computer and use it in GitHub Desktop.
Random compat
if (!function_exists('random_bytes')) {
function random_bytes($length)
{
$buffer = '';
$valid = false;
if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
$buffer = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
if ($buffer) {
$valid = true;
}
}
if (!$valid && function_exists('openssl_random_pseudo_bytes')) {
$bytes = openssl_random_pseudo_bytes($length, $strong);
if ($bytes && true === $strong) {
$buffer = $bytes;
$valid = true;
}
}
if (!$valid && @is_readable('/dev/urandom')) {
$handle = fopen('/dev/urandom', 'r');
$len = strlen($buffer);
while ($len < $length) {
$buffer .= fread($handle, $length - $len);
$len = strlen($buffer);
}
fclose($handle);
if ($len >= $length) {
$valid = true;
}
}
if (!$valid || strlen($buffer) < $length) {
$len = strlen($buffer);
for ($i = 0; $i < $length; $i++) {
if ($i < $len) {
$buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
} else {
$buffer .= chr(mt_rand(0, 255));
}
}
}
return $buffer;
}
}
if (!function_exists('random_int')) {
function random_int($min, $max)
{
$min = (int) $min;
$max = (int) $max;
$range = $max - $min;
if ($range <= 0) {
return $min;
}
$bits = (int) ceil(log($range, 2));
$bytes = (int) max(ceil($bits / 8), 1);
if ($bits === 63) {
$mask = 0x7fffffffffffffff;
} else {
$mask = (int) (pow(2, $bits) - 1);
}
$num = 0;
do {
$num = hexdec(bin2hex(random_bytes($bytes))) & $mask;
} while ($num > $range);
return $num + $min;
}
}
@scottchiefbaker
Copy link

Can you explain how your random_int() function works? I'm not following the logic.

@jrnickell
Copy link
Author

@scottchiefbaker The methods that the code is based on have some comments:
https://github.com/ircmaxell/RandomLib/blob/master/lib/RandomLib/Generator.php#L166
https://github.com/ircmaxell/random_compat/blob/master/lib/random.php#L44

Generally speaking, the input values are cast as integers and checked. The range is determined (the number of possible integers). The total amount of bits/bytes is found, in order to hold a value up to the maximum range value. Then a bitmask is created for the "&" operation inside the do loop.

Within the do loop, enough random bytes are generated (from the random_bytes method in this case), converted to hex, then converted to decimal. Then the mask is applied. This is the same as the methods referenced above, and it removes unused bits.

The loop exits as long as the number is within the valid range. Finally adding the generated integer (within range) to the "min" value produces the result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment