This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Savvot\Random; |
||
4 | |||
5 | /** |
||
6 | * Abstract base class with basic methods |
||
7 | * |
||
8 | * @package Savvot\Random |
||
9 | * @author SavvoT <[email protected]> |
||
10 | */ |
||
11 | abstract class AbstractRand |
||
12 | { |
||
13 | /** |
||
14 | * Maximum possible value of randomInt() generator in derived class. |
||
15 | * Must be overridden to correct value if different from default uint32 |
||
16 | */ |
||
17 | const INT_MAX = 0xFFFFFFFF; |
||
18 | |||
19 | /** |
||
20 | * Helper constants with common character lists for randomString() method |
||
21 | */ |
||
22 | const CL_ALNUM = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; |
||
23 | const CL_NUM = '0123456789'; |
||
24 | const CL_ALUC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
||
25 | const CL_ALLC = 'abcdefghijklmnopqrstuvwxyz'; |
||
26 | const CL_HEXUC = '0123456789ABCDEF'; |
||
27 | const CL_HEXLC = '0123456789abcdef'; |
||
28 | |||
29 | /** |
||
30 | * @var string User specified seed used to initialize a PRNG |
||
31 | */ |
||
32 | protected $seed; |
||
33 | /** |
||
34 | * @var string 128bit binary md5 hash from seed. |
||
35 | * Should be used as data source for PRNG initialization |
||
36 | */ |
||
37 | protected $hashedSeed; |
||
38 | /** |
||
39 | * @var float Helper var = 1/INT_MAX |
||
40 | */ |
||
41 | protected $intMaxDiv; |
||
42 | |||
43 | /** |
||
44 | * @var mixed Internal generator's state. |
||
45 | * Derived class must use it for storing current state of prng. |
||
46 | * Can be saved and sets back to "replay" random sequences |
||
47 | */ |
||
48 | protected $state; |
||
49 | |||
50 | /** |
||
51 | * @var array State stack, used by pushState() and popState() methods |
||
52 | */ |
||
53 | protected $stateStack = []; |
||
54 | |||
55 | /** |
||
56 | * @var GaussianSampler Normal distributed random numbers generator |
||
57 | */ |
||
58 | protected $gaussianSampler; |
||
59 | |||
60 | /** |
||
61 | * Initializes random generator |
||
62 | * Must be implemented by derived class |
||
63 | */ |
||
64 | abstract protected function init(); |
||
65 | |||
66 | /** |
||
67 | * Returns random unsigned integer. Int size depends on generator's algorithm. |
||
68 | * Must be implemented by derived class |
||
69 | * |
||
70 | * @return int Random number |
||
71 | */ |
||
72 | abstract public function randomInt(); |
||
73 | |||
74 | /** |
||
75 | * Class constructor. Initializes generator from specified $seed string |
||
76 | * |
||
77 | * @param string $seed Seed to initialize generator's state. Defaults to null (auto) |
||
78 | */ |
||
79 | public function __construct($seed = null) |
||
80 | { |
||
81 | $this->intMaxDiv = 1.0 / static::INT_MAX; |
||
82 | $this->setSeed($seed); |
||
83 | } |
||
84 | |||
85 | /** |
||
86 | * Sets new seed and initializes generator |
||
87 | * |
||
88 | * @param string|int $seed New seed for PRNG. If null is given, creates random seed from mt_rand |
||
89 | */ |
||
90 | public function setSeed($seed = null) |
||
91 | { |
||
92 | $this->seed = $seed !== null ? $seed : mt_rand(); |
||
0 ignored issues
–
show
|
|||
93 | $this->hashedSeed = md5($this->seed, true); |
||
94 | $this->init(); |
||
95 | } |
||
96 | |||
97 | /** |
||
98 | * Returns seed used to initialize PRNG |
||
99 | * |
||
100 | * @return string Seed |
||
101 | */ |
||
102 | public function getSeed() |
||
103 | { |
||
104 | return $this->seed; |
||
105 | } |
||
106 | |||
107 | /** |
||
108 | * Sets PRNG state, previously saved by getState() function |
||
109 | * |
||
110 | * @param array $state State array with seed and internal generator's state |
||
111 | * @throws RandException |
||
112 | */ |
||
113 | public function setState(array $state) |
||
114 | { |
||
115 | if (!isset($state['class'], $state['seed'], $state['state'])) { |
||
116 | throw new RandException('Invalid state'); |
||
117 | } |
||
118 | if ($state['class'] !== static::class) { |
||
119 | throw new RandException('This state is from different generator'); |
||
120 | } |
||
121 | |||
122 | $this->setSeed($state['seed']); |
||
123 | $this->state = $state['state']; |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * Returns array with class name, seed and current internal state |
||
128 | * This state can be used to save generator's state in custom position and "replay" rnd sequences |
||
129 | * |
||
130 | * @return array Information needed to restore generator to known state |
||
131 | */ |
||
132 | public function getState() |
||
133 | { |
||
134 | return [ |
||
135 | 'class' => static::class, |
||
136 | 'seed' => $this->seed, |
||
137 | 'state' => $this->state |
||
138 | ]; |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * Pushes current internal generator's state onto stack. |
||
143 | */ |
||
144 | public function pushState() |
||
145 | { |
||
146 | $this->stateStack[] = $this->getState(); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Restores last pushed internal generator's state from stack |
||
151 | * |
||
152 | * @throws RandException if stack is empty |
||
153 | */ |
||
154 | public function popState() |
||
155 | { |
||
156 | $state = array_pop($this->stateStack); |
||
157 | if ($state === null) { |
||
158 | throw new RandException('State stack is empty'); |
||
159 | } |
||
160 | $this->setState($state); |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Resets generator to initial state |
||
165 | */ |
||
166 | public function reset() |
||
167 | { |
||
168 | $this->init(); |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * @param float $mean |
||
173 | * @param float $sigma |
||
174 | * @return float Normally distributed random number |
||
175 | */ |
||
176 | public function gaussianRandom($mean = 0.0, $sigma = 1.0) |
||
177 | { |
||
178 | // Sampler initialization is heavy so should not be used in __construct |
||
179 | if ($this->gaussianSampler === null) { |
||
180 | $this->gaussianSampler = new GaussianSampler($this); |
||
181 | } |
||
182 | |||
183 | return $mean + ($this->gaussianSampler->nextSample() * $sigma); |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * Returns uniformly distributed random number within given range. |
||
188 | * In case of incorrect range RandException will be thrown |
||
189 | * |
||
190 | * @param int $min Range min. Defaults to 0. |
||
191 | * @param int $max Range max. Defaults to INT_MAX |
||
192 | * @return int Random number in specified range inclusive |
||
193 | * @throws RandException |
||
194 | */ |
||
195 | public function random($min = 0, $max = null) |
||
196 | { |
||
197 | if ($max === null) { |
||
198 | $max = static::INT_MAX; |
||
199 | } elseif ($max < $min) { |
||
200 | throw new RandException('Max is smaller than min'); |
||
201 | } elseif ($max > static::INT_MAX) { |
||
202 | throw new RandException('Max is bigger than maximum generator value'); |
||
203 | } |
||
204 | return $min + (int)(($max - $min + 1) * $this->randomInt() * $this->intMaxDiv); |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * Returns unsigned float |
||
209 | * |
||
210 | * @return float Random float value in range 0 <= num <= 1 |
||
211 | */ |
||
212 | public function randomFloat() |
||
213 | { |
||
214 | return $this->randomInt() * $this->intMaxDiv; |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * Returns true or false |
||
219 | * |
||
220 | * @return bool Random boolean value |
||
221 | */ |
||
222 | public function randomBool() |
||
223 | { |
||
224 | return $this->randomInt() > (static::INT_MAX >> 1); |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * Returns random binary data with given length. |
||
229 | * May be optimised by derived class depending on generator algorithm used |
||
230 | * |
||
231 | * @param int $length Length of data to generate |
||
232 | * @return string Random binary data |
||
233 | * @throws RandException |
||
234 | */ |
||
235 | public function randomData($length) |
||
236 | { |
||
237 | static $asciiTable; |
||
238 | if ($asciiTable === null) { |
||
239 | $asciiTable = array_map(function ($v){ return chr($v); }, range(0, 255)); |
||
240 | } |
||
241 | |||
242 | if ($length < 1) { |
||
243 | throw new RandException('Invalid length'); |
||
244 | } |
||
245 | |||
246 | $data = ''; |
||
247 | for ($i = 0; $i < $length; $i++) { |
||
248 | $data .= $asciiTable[$this->random(0, 255)]; |
||
249 | } |
||
250 | return $data; |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Generate random string with given length from specified character list. |
||
255 | * Supports multi-byte characters when $mb flag is set |
||
256 | * |
||
257 | * @param int $length Length of string to generate |
||
258 | * @param string $charList List of characters to create string from |
||
259 | * @param bool $mb Whether to interpret $charList as multibyte string. Defaults to false |
||
260 | * @return string Random string |
||
261 | */ |
||
262 | public function randomString($length, $charList = self::CL_ALNUM, $mb = false) |
||
263 | { |
||
264 | $str = ''; |
||
265 | if ($mb) { |
||
266 | $charList = preg_split('//u', $charList, -1, PREG_SPLIT_NO_EMPTY); |
||
267 | $len = count($charList) - 1; |
||
268 | } else { |
||
269 | $len = strlen($charList) - 1; |
||
270 | } |
||
271 | |||
272 | for ($i = 0; $i < $length; $i++) { |
||
273 | $str .= $charList[$this->random(0, $len)]; |
||
274 | } |
||
275 | return $str; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Returns random key from given array |
||
280 | * |
||
281 | * @param array $array Array to get random key from |
||
282 | * @return mixed Random array's key |
||
283 | * @throws RandException |
||
284 | */ |
||
285 | public function arrayRand(array $array) |
||
286 | { |
||
287 | if (empty($array)) { |
||
288 | throw new RandException('Empty array specified'); |
||
289 | } |
||
290 | |||
291 | $keys = array_keys($array); |
||
292 | $i = $this->random(0, count($keys) - 1); |
||
293 | return $keys[$i]; |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * Returns random element from given array |
||
298 | * |
||
299 | * @param array $array Array to get random element from |
||
300 | * @return mixed Random array's element |
||
301 | */ |
||
302 | public function arrayRandValue(array $array) |
||
303 | { |
||
304 | return $array[$this->arrayRand($array)]; |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * Shuffles given array by reference like php shuffle() function |
||
309 | * This function assigns new keys to the elements in array. |
||
310 | * |
||
311 | * @param array $array Array for shuffling |
||
312 | */ |
||
313 | public function arrayShuffle(array &$array) |
||
314 | { |
||
315 | $len = count($array) - 1; |
||
316 | if ($len <= 0) { |
||
317 | return; |
||
318 | } |
||
319 | |||
320 | $array = array_values($array); |
||
321 | $this->shuffle($array); |
||
322 | } |
||
323 | |||
324 | /** |
||
325 | * Shuffles given array by reference like php shuffle() function |
||
326 | * Preserves key => value pairs |
||
327 | * |
||
328 | * @param array $array Reference to array for shuffling |
||
329 | */ |
||
330 | public function arrayShuffleAssoc(array &$array) |
||
331 | { |
||
332 | $len = count($array) - 1; |
||
333 | if ($len <= 0) { |
||
334 | return; |
||
335 | } |
||
336 | |||
337 | $keys = array_keys($array); |
||
338 | $this->shuffle($keys); |
||
339 | |||
340 | foreach ($keys as $key) { |
||
341 | $tmp = $array[$key]; |
||
342 | unset($array[$key]); |
||
343 | $array[$key] = $tmp; |
||
344 | } |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Returns random key from input array by its weight |
||
349 | * Array must be specified in [key => weight, ...] form |
||
350 | * |
||
351 | * @param array $array Input array with with key => weight elements |
||
352 | * @return mixed Random key |
||
353 | * @throws RandException |
||
354 | */ |
||
355 | public function arrayWeightRand(array $array) |
||
356 | { |
||
357 | $count = count($array); |
||
358 | if ($count <= 1) { |
||
359 | return key($array); |
||
360 | } |
||
361 | $sum = array_sum($array); |
||
362 | if ($sum < 1) { |
||
363 | throw new RandException('Negative or all-zero weights not allowed'); |
||
364 | } |
||
365 | $targetWeight = $this->random(1, $sum); |
||
366 | foreach ($array as $key => $weight) { |
||
367 | if ($weight < 0) { |
||
368 | throw new RandException('Negative weights not allowed'); |
||
369 | } |
||
370 | $targetWeight -= $weight; |
||
371 | if ($targetWeight <= 0) { |
||
372 | return $key; |
||
373 | } |
||
374 | } |
||
375 | } |
||
376 | |||
377 | /** |
||
378 | * Shuffles input array by element's weights. |
||
379 | * Array must be specified in [key => weight, ...] form |
||
380 | * |
||
381 | * @param array $array Array for shuffling |
||
382 | */ |
||
383 | public function arrayWeightShuffle(array &$array) |
||
384 | { |
||
385 | $tmp = []; |
||
386 | $c = count($array); |
||
387 | |||
388 | for ($i = 0; $i < $c; $i++) { |
||
389 | $key = $this->arrayWeightRand($array); |
||
390 | $tmp[$key] = $array[$key]; |
||
391 | unset($array[$key]); |
||
392 | } |
||
393 | $array = $tmp; |
||
394 | } |
||
395 | |||
396 | /** |
||
397 | * Simple array shuffle helper function |
||
398 | * |
||
399 | * @param array $array |
||
400 | */ |
||
401 | private function shuffle(array &$array) |
||
402 | { |
||
403 | for ($i = count($array) - 1; $i >= 0; $i--) { |
||
404 | $j = $this->random(0, $i); |
||
405 | $tmp = $array[$i]; |
||
406 | $array[$i] = $array[$j]; |
||
407 | $array[$j] = $tmp; |
||
408 | } |
||
409 | } |
||
410 | } |
||
411 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.