PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.01
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
← All changes | vendor/wpfluent/framework/src/WPFluent/Randomizer/Randomizer.php +75 -27 2.6.012.10.01 View file →
@@ -9,8 +9,20 @@
9 9 * An implementation of the Randomizer class.
10 10 * @see https://www.php.net/manual/en/class.random-randomizer.php
11 11 */
12 12 if (class_exists('Random\Randomizer')) {
13 + /**
14 + * Wrapper around the native PHP 8.2+ Random\Randomizer.
15 + *
16 + * @method string getBytes(int $length)
17 + * @method int getInt(int $min, int $max)
18 + * @method float getFloat(float $min, float $max)
19 + * @method int nextInt()
20 + * @method float nextFloat()
21 + * @method array pickArrayKeys(array $array, int $num)
22 + * @method array shuffleArray(array $array)
23 + * @method string shuffleBytes(string $string)
24 + */
13 25 final class Randomizer
14 26 {
15 27 use GetStringTrait;
16 28
@@ -80,23 +92,48 @@
80 92 }
81 93
82 94 public function getFloat(float $min, float $max)
83 95 {
84 - if ($min >= $max) {
85 - throw new InvalidArgumentException(
86 - 'The minimum value must be less than the maximum value.'
96 + // Match native Random\Randomizer::getFloat — allow $min == $max
97 + // (returns $min); only reject $min > $max.
98 + if ($min > $max) {
99 + $this->throwInvalidRange(
100 + 'Argument #1 ($min) must be less than or equal to argument #2 ($max).'
87 101 );
88 102 }
89 103
90 - $randomFraction = $this->nextFloat();
104 + // When $min == $max, the formula naturally returns $min
105 + // (anything * 0 == 0), so no special case needed.
106 + return $min + ($this->nextFloat() * ($max - $min));
107 + }
91 108
92 - return $min + ($randomFraction * ($max - $min));
109 + public function getInt(int $min, int $max)
110 + {
111 + // Match native Random\Randomizer::getInt — throw on $min > $max
112 + // and delegate to random_int (which already handles the range
113 + // correctly, rejection-sampled, no manual offset arithmetic).
114 + if ($min > $max) {
115 + $this->throwInvalidRange(
116 + 'Argument #1 ($min) must be less than or equal to argument #2 ($max).'
117 + );
118 + }
119 +
120 + return random_int($min, $max);
93 121 }
94 122
95 - public function getInt(int $min, int $max)
123 + /**
124 + * Throw the same exception type the native Random\Randomizer would
125 + * throw on bad range input. \ValueError landed in PHP 8.0; on older
126 + * runtimes fall back to InvalidArgumentException so we always raise
127 + * something meaningful.
128 + */
129 + private function throwInvalidRange(string $message)
96 130 {
97 - $range = $max - $min + 1;
98 - return $min + random_int(0, $range - 1);
131 + if (class_exists('ValueError', false)) {
132 + throw new \ValueError($message);
133 + }
134 +
135 + throw new InvalidArgumentException($message);
99 136 }
100 137
101 138 public function nextFloat()
102 139 {
@@ -109,25 +146,37 @@
109 146 }
110 147
111 148 public function pickArrayKeys(array $array, int $num)
112 149 {
113 - if ($num > count($array)) {
150 + $count = count($array);
151 +
152 + if ($num < 1 || $num > $count) {
114 153 throw new InvalidArgumentException(
115 - 'Cannot pick more keys than the array size.'
154 + 'Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array).'
116 155 );
117 156 }
118 157
119 - $pickedKeys = [];
120 -
158 + // Match native Random\Randomizer::pickArrayKeys — picked keys
159 + // are returned in their ORIGINAL position order in the array,
160 + // not in pick order. Strategy: build an index list, partial
161 + // Fisher-Yates to select $num indices, then sort those indices
162 + // and map back to keys. Runs in O(n) time.
121 163 $keys = array_keys($array);
164 + $indices = range(0, $count - 1);
122 165
123 - while (count($pickedKeys) < $num) {
124 - $index = random_int(0, count($keys) - 1);
125 - $pickedKeys[] = $keys[$index];
126 - array_splice($keys, $index, 1);
166 + // Partial Fisher-Yates: only shuffle the first $num positions.
167 + for ($i = 0; $i < $num; $i++) {
168 + $j = random_int($i, $count - 1);
169 + [$indices[$i], $indices[$j]] = [$indices[$j], $indices[$i]];
127 170 }
128 171
129 - return $pickedKeys;
172 + // Take the picked indices, sort to restore original order.
173 + $picked = array_slice($indices, 0, $num);
174 + sort($picked);
175 +
176 + return array_map(static function ($i) use ($keys) {
177 + return $keys[$i];
178 + }, $picked);
130 179 }
131 180
132 181 public function shuffleArray(array $array)
133 182 {
@@ -136,18 +185,17 @@
136 185 if ($count < 2) {
137 186 return $array;
138 187 }
139 188
140 - $originalArray = $array;
141 -
142 - // Shuffle and check if the result is different
143 - do {
144 - // Perform the Fisher-Yates shuffle
145 - for ($i = $count - 1; $i > 0; $i--) {
146 - $j = random_int(0, $i);
147 - [$array[$i], $array[$j]] = [$array[$j], $array[$i]];
148 - }
149 - } while ($array === $originalArray);
189 + // Match native Random\Randomizer::shuffleArray — a single
190 + // unbiased Fisher-Yates pass. Do NOT loop until the result
191 + // differs from the input; that biases the distribution and
192 + // can infinite-loop on tiny arrays. A fair shuffle MUST be
193 + // allowed to occasionally produce the original order.
194 + for ($i = $count - 1; $i > 0; $i--) {
195 + $j = random_int(0, $i);
196 + [$array[$i], $array[$j]] = [$array[$j], $array[$i]];
197 + }
150 198
151 199 return $array;
152 200 }
153 201