5 * Random_* Compatibility Library |
5 * Random_* Compatibility Library |
6 * for using the new PHP 7 random_* API in PHP 5 projects |
6 * for using the new PHP 7 random_* API in PHP 5 projects |
7 * |
7 * |
8 * The MIT License (MIT) |
8 * The MIT License (MIT) |
9 * |
9 * |
10 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises |
10 * Copyright (c) 2015 - 2018 Paragon Initiative Enterprises |
11 * |
11 * |
12 * Permission is hereby granted, free of charge, to any person obtaining a copy |
12 * Permission is hereby granted, free of charge, to any person obtaining a copy |
13 * of this software and associated documentation files (the "Software"), to deal |
13 * of this software and associated documentation files (the "Software"), to deal |
14 * in the Software without restriction, including without limitation the rights |
14 * in the Software without restriction, including without limitation the rights |
15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
49 * lose precision, so the <= and => operators might accidentally let a float |
49 * lose precision, so the <= and => operators might accidentally let a float |
50 * through. |
50 * through. |
51 */ |
51 */ |
52 |
52 |
53 try { |
53 try { |
|
54 /** @var int $min */ |
54 $min = RandomCompat_intval($min); |
55 $min = RandomCompat_intval($min); |
55 } catch (TypeError $ex) { |
56 } catch (TypeError $ex) { |
56 throw new TypeError( |
57 throw new TypeError( |
57 'random_int(): $min must be an integer' |
58 'random_int(): $min must be an integer' |
58 ); |
59 ); |
59 } |
60 } |
60 |
61 |
61 try { |
62 try { |
|
63 /** @var int $max */ |
62 $max = RandomCompat_intval($max); |
64 $max = RandomCompat_intval($max); |
63 } catch (TypeError $ex) { |
65 } catch (TypeError $ex) { |
64 throw new TypeError( |
66 throw new TypeError( |
65 'random_int(): $max must be an integer' |
67 'random_int(): $max must be an integer' |
66 ); |
68 ); |
88 * $bytes => the number of random bytes we need |
90 * $bytes => the number of random bytes we need |
89 * $mask => an integer bitmask (for use with the &) operator |
91 * $mask => an integer bitmask (for use with the &) operator |
90 * so we can minimize the number of discards |
92 * so we can minimize the number of discards |
91 */ |
93 */ |
92 $attempts = $bits = $bytes = $mask = $valueShift = 0; |
94 $attempts = $bits = $bytes = $mask = $valueShift = 0; |
|
95 /** @var int $attempts */ |
|
96 /** @var int $bits */ |
|
97 /** @var int $bytes */ |
|
98 /** @var int $mask */ |
|
99 /** @var int $valueShift */ |
93 |
100 |
94 /** |
101 /** |
95 * At this point, $range is a positive number greater than 0. It might |
102 * At this point, $range is a positive number greater than 0. It might |
96 * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to |
103 * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to |
97 * a float and we will lose some precision. |
104 * a float and we will lose some precision. |
|
105 * |
|
106 * @var int|float $range |
98 */ |
107 */ |
99 $range = $max - $min; |
108 $range = $max - $min; |
100 |
109 |
101 /** |
110 /** |
102 * Test for integer overflow: |
111 * Test for integer overflow: |
127 if ($bits % 8 === 0) { |
137 if ($bits % 8 === 0) { |
128 ++$bytes; |
138 ++$bytes; |
129 } |
139 } |
130 ++$bits; |
140 ++$bits; |
131 $range >>= 1; |
141 $range >>= 1; |
|
142 /** @var int $mask */ |
132 $mask = $mask << 1 | 1; |
143 $mask = $mask << 1 | 1; |
133 } |
144 } |
134 $valueShift = $min; |
145 $valueShift = $min; |
135 } |
146 } |
136 |
147 |
|
148 /** @var int $val */ |
137 $val = 0; |
149 $val = 0; |
138 /** |
150 /** |
139 * Now that we have our parameters set up, let's begin generating |
151 * Now that we have our parameters set up, let's begin generating |
140 * random integers until one falls between $min and $max |
152 * random integers until one falls between $min and $max |
141 */ |
153 */ |
|
154 /** @psalm-suppress RedundantCondition */ |
142 do { |
155 do { |
143 /** |
156 /** |
144 * The rejection probability is at most 0.5, so this corresponds |
157 * The rejection probability is at most 0.5, so this corresponds |
145 * to a failure probability of 2^-128 for a working RNG |
158 * to a failure probability of 2^-128 for a working RNG |
146 */ |
159 */ |