| Conditions | 13 |
| Paths | 10 |
| Total Lines | 163 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | <?php |
||
| 41 | function random_int($min, $max) |
||
| 42 | { |
||
| 43 | /** |
||
| 44 | * Type and input logic checks |
||
| 45 | * |
||
| 46 | * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX) |
||
| 47 | * (non-inclusive), it will sanely cast it to an int. If you it's equal to |
||
| 48 | * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats |
||
| 49 | * lose precision, so the <= and => operators might accidentally let a float |
||
| 50 | * through. |
||
| 51 | */ |
||
| 52 | |||
| 53 | try { |
||
| 54 | /** @var int $min */ |
||
| 55 | $min = RandomCompat_intval($min); |
||
| 56 | } catch (TypeError $ex) { |
||
|
|
|||
| 57 | throw new TypeError( |
||
| 58 | 'random_int(): $min must be an integer' |
||
| 59 | ); |
||
| 60 | } |
||
| 61 | |||
| 62 | try { |
||
| 63 | /** @var int $max */ |
||
| 64 | $max = RandomCompat_intval($max); |
||
| 65 | } catch (TypeError $ex) { |
||
| 66 | throw new TypeError( |
||
| 67 | 'random_int(): $max must be an integer' |
||
| 68 | ); |
||
| 69 | } |
||
| 70 | |||
| 71 | /** |
||
| 72 | * Now that we've verified our weak typing system has given us an integer, |
||
| 73 | * let's validate the logic then we can move forward with generating random |
||
| 74 | * integers along a given range. |
||
| 75 | */ |
||
| 76 | if ($min > $max) { |
||
| 77 | throw new Error( |
||
| 78 | 'Minimum value must be less than or equal to the maximum value' |
||
| 79 | ); |
||
| 80 | } |
||
| 81 | |||
| 82 | if ($max === $min) { |
||
| 83 | return (int) $min; |
||
| 84 | } |
||
| 85 | |||
| 86 | /** |
||
| 87 | * Initialize variables to 0 |
||
| 88 | * |
||
| 89 | * We want to store: |
||
| 90 | * $bytes => the number of random bytes we need |
||
| 91 | * $mask => an integer bitmask (for use with the &) operator |
||
| 92 | * so we can minimize the number of discards |
||
| 93 | */ |
||
| 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 */ |
||
| 100 | |||
| 101 | /** |
||
| 102 | * At this point, $range is a positive number greater than 0. It might |
||
| 103 | * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to |
||
| 104 | * a float and we will lose some precision. |
||
| 105 | * |
||
| 106 | * @var int|float $range |
||
| 107 | */ |
||
| 108 | $range = $max - $min; |
||
| 109 | |||
| 110 | /** |
||
| 111 | * Test for integer overflow: |
||
| 112 | */ |
||
| 113 | if (!is_int($range)) { |
||
| 114 | |||
| 115 | /** |
||
| 116 | * Still safely calculate wider ranges. |
||
| 117 | * Provided by @CodesInChaos, @oittaa |
||
| 118 | * |
||
| 119 | * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 |
||
| 120 | * |
||
| 121 | * We use ~0 as a mask in this case because it generates all 1s |
||
| 122 | * |
||
| 123 | * @ref https://eval.in/400356 (32-bit) |
||
| 124 | * @ref http://3v4l.org/XX9r5 (64-bit) |
||
| 125 | */ |
||
| 126 | $bytes = PHP_INT_SIZE; |
||
| 127 | /** @var int $mask */ |
||
| 128 | $mask = ~0; |
||
| 129 | |||
| 130 | } else { |
||
| 131 | |||
| 132 | /** |
||
| 133 | * $bits is effectively ceil(log($range, 2)) without dealing with |
||
| 134 | * type juggling |
||
| 135 | */ |
||
| 136 | while ($range > 0) { |
||
| 137 | if ($bits % 8 === 0) { |
||
| 138 | ++$bytes; |
||
| 139 | } |
||
| 140 | ++$bits; |
||
| 141 | $range >>= 1; |
||
| 142 | /** @var int $mask */ |
||
| 143 | $mask = $mask << 1 | 1; |
||
| 144 | } |
||
| 145 | $valueShift = $min; |
||
| 146 | } |
||
| 147 | |||
| 148 | /** @var int $val */ |
||
| 149 | $val = 0; |
||
| 150 | /** |
||
| 151 | * Now that we have our parameters set up, let's begin generating |
||
| 152 | * random integers until one falls between $min and $max |
||
| 153 | */ |
||
| 154 | /** @psalm-suppress RedundantCondition */ |
||
| 155 | do { |
||
| 156 | /** |
||
| 157 | * The rejection probability is at most 0.5, so this corresponds |
||
| 158 | * to a failure probability of 2^-128 for a working RNG |
||
| 159 | */ |
||
| 160 | if ($attempts > 128) { |
||
| 161 | throw new Exception( |
||
| 162 | 'random_int: RNG is broken - too many rejections' |
||
| 163 | ); |
||
| 164 | } |
||
| 165 | |||
| 166 | /** |
||
| 167 | * Let's grab the necessary number of random bytes |
||
| 168 | */ |
||
| 169 | $randomByteString = random_bytes($bytes); |
||
| 170 | |||
| 171 | /** |
||
| 172 | * Let's turn $randomByteString into an integer |
||
| 173 | * |
||
| 174 | * This uses bitwise operators (<< and |) to build an integer |
||
| 175 | * out of the values extracted from ord() |
||
| 176 | * |
||
| 177 | * Example: [9F] | [6D] | [32] | [0C] => |
||
| 178 | * 159 + 27904 + 3276800 + 201326592 => |
||
| 179 | * 204631455 |
||
| 180 | */ |
||
| 181 | $val &= 0; |
||
| 182 | for ($i = 0; $i < $bytes; ++$i) { |
||
| 183 | $val |= ord($randomByteString[$i]) << ($i * 8); |
||
| 184 | } |
||
| 185 | /** @var int $val */ |
||
| 186 | |||
| 187 | /** |
||
| 188 | * Apply mask |
||
| 189 | */ |
||
| 190 | $val &= $mask; |
||
| 191 | $val += $valueShift; |
||
| 192 | |||
| 193 | ++$attempts; |
||
| 194 | /** |
||
| 195 | * If $val overflows to a floating point number, |
||
| 196 | * ... or is larger than $max, |
||
| 197 | * ... or smaller than $min, |
||
| 198 | * then try again. |
||
| 199 | */ |
||
| 200 | } while (!is_int($val) || $val > $max || $val < $min); |
||
| 201 | |||
| 202 | return (int) $val; |
||
| 203 | } |
||
| 204 | } |
||
| 205 |
Scrutinizer analyzes your
composer.json/composer.lockfile if available to determine the classes, and functions that are defined by your dependencies.It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.