3v4l.org

run code in 300+ PHP versions simultaneously
<? /** * Blame Scott Arciszewski of Paragon Initiative Enterprises for this atrocity. * * https://paragonie.com */ class DeterministicRandom { protected $counter; public function __construct(string $seed = '', int $counter = 0) { $this->seed('set', $seed); $this->counter = 0; } private function seed(string $action = 'get', string $data = '') { static $seed = null; if ($action === 'set') { $seed = $data; } elseif ($action === 'get') { return $data; } else { throw new \Error( 'Unknown action' ); } } public function getBytes(int $numBytes): string { return \openssl_encrypt( \str_repeat("\0", $numBytes), 'aes-128-ctr', $this->seed('get'), OPENSSL_RAW_DATA, $this->getNonce($numBytes) ); } public function getInt(int $min, int $max): int { /** * Now that we've verified our weak typing system has given us an integer, * let's validate the logic then we can move forward with generating random * integers along a given range. */ if ($min > $max) { throw new Error( 'Minimum value must be less than or equal to the maximum value' ); } if ($max === $min) { return $min; } /** * Initialize variables to 0 * * We want to store: * $bytes => the number of random bytes we need * $mask => an integer bitmask (for use with the &) operator * so we can minimize the number of discards */ $attempts = $bits = $bytes = $mask = $valueShift = 0; /** * At this point, $range is a positive number greater than 0. It might * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to * a float and we will lose some precision. */ $range = $max - $min; /** * Test for integer overflow: */ if (!is_int($range)) { /** * Still safely calculate wider ranges. * Provided by @CodesInChaos, @oittaa * * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 * * We use ~0 as a mask in this case because it generates all 1s * * @ref https://eval.in/400356 (32-bit) * @ref http://3v4l.org/XX9r5 (64-bit) */ $bytes = PHP_INT_SIZE; $mask = ~0; } else { /** * $bits is effectively ceil(log($range, 2)) without dealing with * type juggling */ while ($range > 0) { if ($bits % 8 === 0) { ++$bytes; } ++$bits; $range >>= 1; $mask = $mask << 1 | 1; } $valueShift = $min; } /** * Now that we have our parameters set up, let's begin generating * random integers until one falls between $min and $max */ do { /** * The rejection probability is at most 0.5, so this corresponds * to a failure probability of 2^-128 for a working RNG */ if ($attempts > 128) { throw new Exception( 'random_int: RNG is broken - too many rejections' ); } /** * Let's grab the necessary number of random bytes */ $randomByteString = $this->getBytes($bytes); if ($randomByteString === false) { throw new Exception( 'Random number generator failure' ); } /** * Let's turn $randomByteString into an integer * * This uses bitwise operators (<< and |) to build an integer * out of the values extracted from ord() * * Example: [9F] | [6D] | [32] | [0C] => * 159 + 27904 + 3276800 + 201326592 => * 204631455 */ $val = 0; for ($i = 0; $i < $bytes; ++$i) { $val |= ord($randomByteString[$i]) << ($i * 8); } /** * Apply mask */ $val &= $mask; $val += $valueShift; ++$attempts; /** * If $val overflows to a floating point number, * ... or is larger than $max, * ... or smaller than $min, * then try again. */ } while (!is_int($val) || $val > $max || $val < $min); return (int) $val; } protected function getNonce(int $increment = 0): string { $nonce = ''; $ctr = $this->counter; while ($ctr > 0) { $nonce = \chr($ctr & 0xFF) . $nonce; $ctr >>= 8; } $this->counter += $increment; return \str_pad($nonce, 16, "\0", STR_PAD_LEFT); } } $seed = str_repeat("\x80", 16); $rnd1 = new DeterministicRandom($seed); $rnd2 = new DeterministicRandom($seed); $out1 = $rnd1->getBytes(16); $out2 = $rnd1->getBytes(16); $out3 = $rnd2->getBytes(32); $int1 = $rnd1->getInt(0, 255); $int2 = $rnd2->getInt(0, 255); var_dump([ bin2hex($out1), bin2hex($out2), bin2hex($out3), $int1, $int2 ]);
Output for 7.1.26 - 7.1.33, 7.2.17 - 7.2.33, 7.3.0 - 7.3.31, 7.4.0 - 7.4.32, 8.0.0 - 8.0.12, 8.0.14 - 8.0.30, 8.1.0 - 8.1.28, 8.2.0 - 8.2.18, 8.3.0 - 8.3.4, 8.3.6
<? /** * Blame Scott Arciszewski of Paragon Initiative Enterprises for this atrocity. * * https://paragonie.com */ class DeterministicRandom { protected $counter; public function __construct(string $seed = '', int $counter = 0) { $this->seed('set', $seed); $this->counter = 0; } private function seed(string $action = 'get', string $data = '') { static $seed = null; if ($action === 'set') { $seed = $data; } elseif ($action === 'get') { return $data; } else { throw new \Error( 'Unknown action' ); } } public function getBytes(int $numBytes): string { return \openssl_encrypt( \str_repeat("\0", $numBytes), 'aes-128-ctr', $this->seed('get'), OPENSSL_RAW_DATA, $this->getNonce($numBytes) ); } public function getInt(int $min, int $max): int { /** * Now that we've verified our weak typing system has given us an integer, * let's validate the logic then we can move forward with generating random * integers along a given range. */ if ($min > $max) { throw new Error( 'Minimum value must be less than or equal to the maximum value' ); } if ($max === $min) { return $min; } /** * Initialize variables to 0 * * We want to store: * $bytes => the number of random bytes we need * $mask => an integer bitmask (for use with the &) operator * so we can minimize the number of discards */ $attempts = $bits = $bytes = $mask = $valueShift = 0; /** * At this point, $range is a positive number greater than 0. It might * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to * a float and we will lose some precision. */ $range = $max - $min; /** * Test for integer overflow: */ if (!is_int($range)) { /** * Still safely calculate wider ranges. * Provided by @CodesInChaos, @oittaa * * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 * * We use ~0 as a mask in this case because it generates all 1s * * @ref https://eval.in/400356 (32-bit) * @ref http://3v4l.org/XX9r5 (64-bit) */ $bytes = PHP_INT_SIZE; $mask = ~0; } else { /** * $bits is effectively ceil(log($range, 2)) without dealing with * type juggling */ while ($range > 0) { if ($bits % 8 === 0) { ++$bytes; } ++$bits; $range >>= 1; $mask = $mask << 1 | 1; } $valueShift = $min; } /** * Now that we have our parameters set up, let's begin generating * random integers until one falls between $min and $max */ do { /** * The rejection probability is at most 0.5, so this corresponds * to a failure probability of 2^-128 for a working RNG */ if ($attempts > 128) { throw new Exception( 'random_int: RNG is broken - too many rejections' ); } /** * Let's grab the necessary number of random bytes */ $randomByteString = $this->getBytes($bytes); if ($randomByteString === false) { throw new Exception( 'Random number generator failure' ); } /** * Let's turn $randomByteString into an integer * * This uses bitwise operators (<< and |) to build an integer * out of the values extracted from ord() * * Example: [9F] | [6D] | [32] | [0C] => * 159 + 27904 + 3276800 + 201326592 => * 204631455 */ $val = 0; for ($i = 0; $i < $bytes; ++$i) { $val |= ord($randomByteString[$i]) << ($i * 8); } /** * Apply mask */ $val &= $mask; $val += $valueShift; ++$attempts; /** * If $val overflows to a floating point number, * ... or is larger than $max, * ... or smaller than $min, * then try again. */ } while (!is_int($val) || $val > $max || $val < $min); return (int) $val; } protected function getNonce(int $increment = 0): string { $nonce = ''; $ctr = $this->counter; while ($ctr > 0) { $nonce = \chr($ctr & 0xFF) . $nonce; $ctr >>= 8; } $this->counter += $increment; return \str_pad($nonce, 16, "\0", STR_PAD_LEFT); } } $seed = str_repeat("\x80", 16); $rnd1 = new DeterministicRandom($seed); $rnd2 = new DeterministicRandom($seed); $out1 = $rnd1->getBytes(16); $out2 = $rnd1->getBytes(16); $out3 = $rnd2->getBytes(32); $int1 = $rnd1->getInt(0, 255); $int2 = $rnd2->getInt(0, 255); var_dump([ bin2hex($out1), bin2hex($out2), bin2hex($out3), $int1, $int2 ]);
Output for 8.3.5
Warning: PHP Startup: Unable to load dynamic library 'sodium.so' (tried: /usr/lib/php/8.3.5/modules/sodium.so (libsodium.so.23: cannot open shared object file: No such file or directory), /usr/lib/php/8.3.5/modules/sodium.so.so (/usr/lib/php/8.3.5/modules/sodium.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0 <? /** * Blame Scott Arciszewski of Paragon Initiative Enterprises for this atrocity. * * https://paragonie.com */ class DeterministicRandom { protected $counter; public function __construct(string $seed = '', int $counter = 0) { $this->seed('set', $seed); $this->counter = 0; } private function seed(string $action = 'get', string $data = '') { static $seed = null; if ($action === 'set') { $seed = $data; } elseif ($action === 'get') { return $data; } else { throw new \Error( 'Unknown action' ); } } public function getBytes(int $numBytes): string { return \openssl_encrypt( \str_repeat("\0", $numBytes), 'aes-128-ctr', $this->seed('get'), OPENSSL_RAW_DATA, $this->getNonce($numBytes) ); } public function getInt(int $min, int $max): int { /** * Now that we've verified our weak typing system has given us an integer, * let's validate the logic then we can move forward with generating random * integers along a given range. */ if ($min > $max) { throw new Error( 'Minimum value must be less than or equal to the maximum value' ); } if ($max === $min) { return $min; } /** * Initialize variables to 0 * * We want to store: * $bytes => the number of random bytes we need * $mask => an integer bitmask (for use with the &) operator * so we can minimize the number of discards */ $attempts = $bits = $bytes = $mask = $valueShift = 0; /** * At this point, $range is a positive number greater than 0. It might * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to * a float and we will lose some precision. */ $range = $max - $min; /** * Test for integer overflow: */ if (!is_int($range)) { /** * Still safely calculate wider ranges. * Provided by @CodesInChaos, @oittaa * * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 * * We use ~0 as a mask in this case because it generates all 1s * * @ref https://eval.in/400356 (32-bit) * @ref http://3v4l.org/XX9r5 (64-bit) */ $bytes = PHP_INT_SIZE; $mask = ~0; } else { /** * $bits is effectively ceil(log($range, 2)) without dealing with * type juggling */ while ($range > 0) { if ($bits % 8 === 0) { ++$bytes; } ++$bits; $range >>= 1; $mask = $mask << 1 | 1; } $valueShift = $min; } /** * Now that we have our parameters set up, let's begin generating * random integers until one falls between $min and $max */ do { /** * The rejection probability is at most 0.5, so this corresponds * to a failure probability of 2^-128 for a working RNG */ if ($attempts > 128) { throw new Exception( 'random_int: RNG is broken - too many rejections' ); } /** * Let's grab the necessary number of random bytes */ $randomByteString = $this->getBytes($bytes); if ($randomByteString === false) { throw new Exception( 'Random number generator failure' ); } /** * Let's turn $randomByteString into an integer * * This uses bitwise operators (<< and |) to build an integer * out of the values extracted from ord() * * Example: [9F] | [6D] | [32] | [0C] => * 159 + 27904 + 3276800 + 201326592 => * 204631455 */ $val = 0; for ($i = 0; $i < $bytes; ++$i) { $val |= ord($randomByteString[$i]) << ($i * 8); } /** * Apply mask */ $val &= $mask; $val += $valueShift; ++$attempts; /** * If $val overflows to a floating point number, * ... or is larger than $max, * ... or smaller than $min, * then try again. */ } while (!is_int($val) || $val > $max || $val < $min); return (int) $val; } protected function getNonce(int $increment = 0): string { $nonce = ''; $ctr = $this->counter; while ($ctr > 0) { $nonce = \chr($ctr & 0xFF) . $nonce; $ctr >>= 8; } $this->counter += $increment; return \str_pad($nonce, 16, "\0", STR_PAD_LEFT); } } $seed = str_repeat("\x80", 16); $rnd1 = new DeterministicRandom($seed); $rnd2 = new DeterministicRandom($seed); $out1 = $rnd1->getBytes(16); $out2 = $rnd1->getBytes(16); $out3 = $rnd2->getBytes(32); $int1 = $rnd1->getInt(0, 255); $int2 = $rnd2->getInt(0, 255); var_dump([ bin2hex($out1), bin2hex($out2), bin2hex($out3), $int1, $int2 ]);
Output for 7.1.20, 7.2.6, 7.3.32 - 7.3.33, 7.4.33, 8.0.13
Fatal error: Uncaught Error: Call to undefined function openssl_encrypt() in /in/FQV94:34 Stack trace: #0 /in/FQV94(184): DeterministicRandom->getBytes(16) #1 {main} thrown in /in/FQV94 on line 34
Process exited with code 255.
Output for 7.0.0 - 7.0.20, 7.1.0 - 7.1.10, 7.2.0
array(5) { [0]=> string(32) "66e94bd4ef8a2c3b884cfa59ca342b2e" [1]=> string(32) "58b2431bc0bede02550f40238969ec78" [2]=> string(64) "66e94bd4ef8a2c3b884cfa59ca342b2e58e2fccefa7e3061367f1d57a4e7455a" [3]=> int(58) [4]=> int(58) }
Output for 5.5.24 - 5.5.35, 5.6.8 - 5.6.28
Fatal error: Default value for parameters with a class type hint can only be NULL in /in/FQV94 on line 12
Process exited with code 255.

preferences:
192.06 ms | 402 KiB | 213 Q