Total Complexity | 101 |
Total Lines | 818 |
Duplicated Lines | 8.31 % |
Coverage | 100% |
Changes | 15 | ||
Bugs | 6 | Features | 2 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like EnumSet often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use EnumSet, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class EnumSet implements Iterator, Countable |
||
18 | { |
||
19 | /** |
||
20 | * The classname of the Enumeration |
||
21 | * @var string |
||
22 | */ |
||
23 | private $enumeration; |
||
24 | |||
25 | /** |
||
26 | * Ordinal number of current iterator position |
||
27 | * @var int |
||
28 | */ |
||
29 | private $ordinal = 0; |
||
30 | |||
31 | /** |
||
32 | * Highest possible ordinal number |
||
33 | * @var int |
||
34 | */ |
||
35 | private $ordinalMax; |
||
36 | |||
37 | /** |
||
38 | * Integer or binary (little endian) bitset |
||
39 | * @var int|string |
||
40 | */ |
||
41 | private $bitset = 0; |
||
42 | |||
43 | /**#@+ |
||
44 | * Defines private method names to be called depended of how the bitset type was set too. |
||
45 | * ... Integer or binary bitset. |
||
46 | * ... *Int or *Bin method |
||
47 | * |
||
48 | * @var string |
||
49 | */ |
||
50 | private $fnDoRewind = 'doRewindInt'; |
||
51 | private $fnDoCount = 'doCountInt'; |
||
52 | private $fnDoGetOrdinals = 'doGetOrdinalsInt'; |
||
53 | private $fnDoGetBit = 'doGetBitInt'; |
||
54 | private $fnDoSetBit = 'doSetBitInt'; |
||
55 | private $fnDoUnsetBit = 'doUnsetBitInt'; |
||
56 | private $fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeInt'; |
||
57 | private $fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeInt'; |
||
58 | /**#@-*/ |
||
59 | |||
60 | /** |
||
61 | * Constructor |
||
62 | * |
||
63 | * @param string $enumeration The classname of the enumeration |
||
64 | * @throws InvalidArgumentException |
||
65 | */ |
||
66 | 195 | public function __construct($enumeration) |
|
67 | { |
||
68 | 195 | if (!\is_subclass_of($enumeration, Enum::class)) { |
|
69 | 3 | throw new InvalidArgumentException(\sprintf( |
|
70 | 3 | "%s can handle subclasses of '%s' only", |
|
71 | 3 | static::class, |
|
72 | 2 | Enum::class |
|
73 | 1 | )); |
|
74 | } |
||
75 | |||
76 | 192 | $this->enumeration = $enumeration; |
|
77 | 192 | $this->ordinalMax = count($enumeration::getConstants()); |
|
78 | |||
79 | // By default the bitset is initialized as integer bitset |
||
80 | // in case the enumeraton has more enumerators then integer bits |
||
81 | // we will switch this into a binary bitset |
||
82 | 192 | if ($this->ordinalMax > \PHP_INT_SIZE * 8) { |
|
83 | // init binary bitset with zeros |
||
84 | 48 | $this->bitset = \str_repeat("\0", (int)\ceil($this->ordinalMax / 8)); |
|
85 | |||
86 | // switch internal binary bitset functions |
||
87 | 48 | $this->fnDoRewind = 'doRewindBin'; |
|
88 | 48 | $this->fnDoCount = 'doCountBin'; |
|
89 | 48 | $this->fnDoGetOrdinals = 'doGetOrdinalsBin'; |
|
90 | 48 | $this->fnDoGetBit = 'doGetBitBin'; |
|
91 | 48 | $this->fnDoSetBit = 'doSetBitBin'; |
|
92 | 48 | $this->fnDoUnsetBit = 'doUnsetBitBin'; |
|
93 | 48 | $this->fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeBin'; |
|
94 | 48 | $this->fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeBin'; |
|
95 | 16 | } |
|
96 | 192 | } |
|
97 | |||
98 | /** |
||
99 | * Get the classname of the enumeration |
||
100 | * @return string |
||
101 | */ |
||
102 | 3 | public function getEnumeration() |
|
103 | { |
||
104 | 3 | return $this->enumeration; |
|
105 | } |
||
106 | |||
107 | /** |
||
108 | * Attach a new enumerator or overwrite an existing one |
||
109 | * @param Enum|null|boolean|int|float|string $enumerator |
||
110 | * @return void |
||
111 | * @throws InvalidArgumentException On an invalid given enumerator |
||
112 | */ |
||
113 | 123 | public function attach($enumerator) |
|
114 | { |
||
115 | 123 | $enumeration = $this->enumeration; |
|
116 | 123 | $this->{$this->fnDoSetBit}($enumeration::get($enumerator)->getOrdinal()); |
|
117 | 123 | } |
|
118 | |||
119 | /** |
||
120 | * Detach the given enumerator |
||
121 | * @param Enum|null|boolean|int|float|string $enumerator |
||
122 | * @return void |
||
123 | * @throws InvalidArgumentException On an invalid given enumerator |
||
124 | */ |
||
125 | 21 | public function detach($enumerator) |
|
126 | { |
||
127 | 21 | $enumeration = $this->enumeration; |
|
128 | 21 | $this->{$this->fnDoUnsetBit}($enumeration::get($enumerator)->getOrdinal()); |
|
129 | 21 | } |
|
130 | |||
131 | /** |
||
132 | * Test if the given enumerator was attached |
||
133 | * @param Enum|null|boolean|int|float|string $enumerator |
||
134 | * @return boolean |
||
135 | */ |
||
136 | 24 | public function contains($enumerator) |
|
137 | { |
||
138 | 24 | $enumeration = $this->enumeration; |
|
139 | 24 | return $this->{$this->fnDoGetBit}($enumeration::get($enumerator)->getOrdinal()); |
|
140 | } |
||
141 | |||
142 | /* Iterator */ |
||
143 | |||
144 | /** |
||
145 | * Get the current enumerator |
||
146 | * @return Enum|null Returns the current enumerator or NULL on an invalid iterator position |
||
147 | */ |
||
148 | 36 | public function current() |
|
149 | { |
||
150 | 36 | if ($this->valid()) { |
|
151 | 36 | $enumeration = $this->enumeration; |
|
152 | 36 | return $enumeration::byOrdinal($this->ordinal); |
|
153 | } |
||
154 | |||
155 | 6 | return null; |
|
156 | } |
||
157 | |||
158 | /** |
||
159 | * Get the ordinal number of the current iterator position |
||
160 | * @return int |
||
161 | */ |
||
162 | 21 | public function key() |
|
163 | { |
||
164 | 21 | return $this->ordinal; |
|
165 | } |
||
166 | |||
167 | /** |
||
168 | * Go to the next valid iterator position. |
||
169 | * If no valid iterator position is found the iterator position will be the last possible + 1. |
||
170 | * @return void |
||
171 | */ |
||
172 | 57 | public function next() |
|
173 | { |
||
174 | do { |
||
175 | 57 | if (++$this->ordinal >= $this->ordinalMax) { |
|
176 | 18 | $this->ordinal = $this->ordinalMax; |
|
177 | 18 | return; |
|
178 | } |
||
179 | 57 | } while (!$this->{$this->fnDoGetBit}($this->ordinal)); |
|
180 | 57 | } |
|
181 | |||
182 | /** |
||
183 | * Go to the first valid iterator position. |
||
184 | * If no valid iterator position was found the iterator position will be 0. |
||
185 | * @return void |
||
186 | * @see doRewindBin |
||
187 | * @see doRewindInt |
||
188 | */ |
||
189 | 36 | public function rewind() |
|
190 | { |
||
191 | 36 | $this->{$this->fnDoRewind}(); |
|
192 | 36 | } |
|
193 | |||
194 | /** |
||
195 | * Go to the first valid iterator position. |
||
196 | * If no valid iterator position was found the iterator position will be 0. |
||
197 | * |
||
198 | * This is the binary bitset implementation. |
||
199 | * |
||
200 | * @return void |
||
201 | * @see rewind |
||
202 | * @see doRewindInt |
||
203 | */ |
||
204 | 15 | private function doRewindBin() |
|
205 | { |
||
206 | 15 | if (\ltrim($this->bitset, "\0") !== '') { |
|
207 | 15 | $this->ordinal = -1; |
|
208 | 15 | $this->next(); |
|
209 | 5 | } else { |
|
210 | 3 | $this->ordinal = 0; |
|
211 | } |
||
212 | 15 | } |
|
213 | |||
214 | /** |
||
215 | * Go to the first valid iterator position. |
||
216 | * If no valid iterator position was found the iterator position will be 0. |
||
217 | * |
||
218 | * This is the binary bitset implementation. |
||
219 | * |
||
220 | * @return void |
||
221 | * @see rewind |
||
222 | * @see doRewindBin |
||
223 | */ |
||
224 | 21 | private function doRewindInt() |
|
225 | { |
||
226 | 21 | if ($this->bitset) { |
|
227 | 21 | $this->ordinal = -1; |
|
228 | 21 | $this->next(); |
|
229 | 7 | } else { |
|
230 | 3 | $this->ordinal = 0; |
|
231 | } |
||
232 | 21 | } |
|
233 | |||
234 | /** |
||
235 | * Test if the iterator is in a valid state |
||
236 | * @return boolean |
||
237 | */ |
||
238 | 39 | public function valid() |
|
239 | { |
||
240 | 39 | return $this->ordinal !== $this->ordinalMax && $this->{$this->fnDoGetBit}($this->ordinal); |
|
241 | } |
||
242 | |||
243 | /* Countable */ |
||
244 | |||
245 | /** |
||
246 | * Count the number of elements |
||
247 | * |
||
248 | * @return int |
||
249 | * @see doCountBin |
||
250 | * @see doCountInt |
||
251 | */ |
||
252 | 39 | public function count() |
|
253 | { |
||
254 | 39 | return $this->{$this->fnDoCount}(); |
|
255 | } |
||
256 | |||
257 | /** |
||
258 | * Count the number of elements. |
||
259 | * |
||
260 | * This is the binary bitset implementation. |
||
261 | * |
||
262 | * @return int |
||
263 | * @see count |
||
264 | * @see doCountInt |
||
265 | */ |
||
266 | 15 | private function doCountBin() |
|
267 | { |
||
268 | 15 | $count = 0; |
|
269 | 15 | $bitset = $this->bitset; |
|
270 | 15 | $byteLen = \strlen($bitset); |
|
271 | 15 | for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) { |
|
272 | 15 | if ($bitset[$bytePos] === "\0") { |
|
273 | // fast skip null byte |
||
274 | 15 | continue; |
|
275 | } |
||
276 | |||
277 | 15 | $ord = \ord($bitset[$bytePos]); |
|
278 | 15 | for ($bitPos = 0; $bitPos < 8; ++$bitPos) { |
|
279 | 15 | if ($ord & (1 << $bitPos)) { |
|
280 | 15 | ++$count; |
|
281 | 5 | } |
|
282 | 5 | } |
|
283 | 5 | } |
|
284 | 15 | return $count; |
|
285 | } |
||
286 | |||
287 | /** |
||
288 | * Count the number of elements. |
||
289 | * |
||
290 | * This is the integer bitset implementation. |
||
291 | * |
||
292 | * @return int |
||
293 | * @see count |
||
294 | * @see doCountBin |
||
295 | */ |
||
296 | 24 | private function doCountInt() |
|
297 | { |
||
298 | 24 | $count = 0; |
|
299 | 24 | $bitset = $this->bitset; |
|
300 | |||
301 | // PHP does not support right shift unsigned |
||
302 | 24 | if ($bitset < 0) { |
|
303 | 6 | $count = 1; |
|
304 | 6 | $bitset = $bitset & \PHP_INT_MAX; |
|
305 | 2 | } |
|
306 | |||
307 | // iterate byte by byte and count set bits |
||
308 | 24 | for ($i = 0; $i < \PHP_INT_SIZE; ++$i) { |
|
309 | 24 | $bitPos = $i * 8; |
|
310 | 24 | $bitChk = 0xff << $bitPos; |
|
311 | 24 | $byte = $bitset & $bitChk; |
|
312 | 24 | if ($byte) { |
|
313 | 21 | $byte = $byte >> $bitPos; |
|
314 | 21 | if ($byte & 0b00000001) ++$count; |
|
315 | 21 | if ($byte & 0b00000010) ++$count; |
|
316 | 21 | if ($byte & 0b00000100) ++$count; |
|
317 | 21 | if ($byte & 0b00001000) ++$count; |
|
318 | 21 | if ($byte & 0b00010000) ++$count; |
|
319 | 21 | if ($byte & 0b00100000) ++$count; |
|
320 | 21 | if ($byte & 0b01000000) ++$count; |
|
321 | 21 | if ($byte & 0b10000000) ++$count; |
|
322 | 7 | } |
|
323 | |||
324 | 24 | if ($bitset <= $bitChk) { |
|
325 | 21 | break; |
|
326 | } |
||
327 | 5 | } |
|
328 | |||
329 | 24 | return $count; |
|
330 | } |
||
331 | |||
332 | /** |
||
333 | * Check if this EnumSet is the same as other |
||
334 | * @param EnumSet $other |
||
335 | * @return bool |
||
336 | */ |
||
337 | 9 | public function isEqual(EnumSet $other) |
|
338 | { |
||
339 | 9 | return $this->enumeration === $other->enumeration |
|
340 | 9 | && $this->bitset === $other->bitset; |
|
341 | } |
||
342 | |||
343 | /** |
||
344 | * Check if this EnumSet is a subset of other |
||
345 | * @param EnumSet $other |
||
346 | * @return bool |
||
347 | */ |
||
348 | 12 | View Code Duplication | public function isSubset(EnumSet $other) |
|
|||
349 | { |
||
350 | 12 | if ($this->enumeration !== $other->enumeration) { |
|
351 | 3 | return false; |
|
352 | } |
||
353 | |||
354 | 9 | return ($this->bitset & $other->bitset) === $this->bitset; |
|
355 | } |
||
356 | |||
357 | /** |
||
358 | * Check if this EnumSet is a superset of other |
||
359 | * @param EnumSet $other |
||
360 | * @return bool |
||
361 | */ |
||
362 | 12 | View Code Duplication | public function isSuperset(EnumSet $other) |
363 | { |
||
364 | 12 | if ($this->enumeration !== $other->enumeration) { |
|
365 | 3 | return false; |
|
366 | } |
||
367 | |||
368 | 9 | return ($this->bitset | $other->bitset) === $this->bitset; |
|
369 | } |
||
370 | |||
371 | /** |
||
372 | * Produce a new set with enumerators from both this and other (this | other) |
||
373 | * |
||
374 | * @param EnumSet $other EnumSet of the same enumeration to produce the union |
||
375 | * @return EnumSet |
||
376 | */ |
||
377 | 6 | View Code Duplication | public function union(EnumSet $other) |
378 | { |
||
379 | 6 | if ($this->enumeration !== $other->enumeration) { |
|
380 | 3 | throw new InvalidArgumentException(\sprintf( |
|
381 | 3 | 'Other should be of the same enumeration as this %s', |
|
382 | 3 | $this->enumeration |
|
383 | 1 | )); |
|
384 | } |
||
385 | |||
386 | 3 | $clone = clone $this; |
|
387 | 3 | $clone->bitset = $this->bitset | $other->bitset; |
|
388 | 3 | return $clone; |
|
389 | } |
||
390 | |||
391 | /** |
||
392 | * Produce a new set with enumerators common to both this and other (this & other) |
||
393 | * |
||
394 | * @param EnumSet $other EnumSet of the same enumeration to produce the intersect |
||
395 | * @return EnumSet |
||
396 | */ |
||
397 | 6 | View Code Duplication | public function intersect(EnumSet $other) |
398 | { |
||
399 | 6 | if ($this->enumeration !== $other->enumeration) { |
|
400 | 3 | throw new InvalidArgumentException(\sprintf( |
|
401 | 3 | 'Other should be of the same enumeration as this %s', |
|
402 | 3 | $this->enumeration |
|
403 | 1 | )); |
|
404 | } |
||
405 | |||
406 | 3 | $clone = clone $this; |
|
407 | 3 | $clone->bitset = $this->bitset & $other->bitset; |
|
408 | 3 | return $clone; |
|
409 | } |
||
410 | |||
411 | /** |
||
412 | * Produce a new set with enumerators in this but not in other (this - other) |
||
413 | * |
||
414 | * @param EnumSet $other EnumSet of the same enumeration to produce the diff |
||
415 | * @return EnumSet |
||
416 | */ |
||
417 | 6 | View Code Duplication | public function diff(EnumSet $other) |
418 | { |
||
419 | 6 | if ($this->enumeration !== $other->enumeration) { |
|
420 | 3 | throw new InvalidArgumentException(\sprintf( |
|
421 | 3 | 'Other should be of the same enumeration as this %s', |
|
422 | 3 | $this->enumeration |
|
423 | 1 | )); |
|
424 | } |
||
425 | |||
426 | 3 | $clone = clone $this; |
|
427 | 3 | $clone->bitset = $this->bitset & ~$other->bitset; |
|
428 | 3 | return $clone; |
|
429 | } |
||
430 | |||
431 | /** |
||
432 | * Produce a new set with enumerators in either this and other but not in both (this ^ other) |
||
433 | * |
||
434 | * @param EnumSet $other EnumSet of the same enumeration to produce the symmetric difference |
||
435 | * @return EnumSet |
||
436 | */ |
||
437 | 6 | View Code Duplication | public function symDiff(EnumSet $other) |
449 | } |
||
450 | |||
451 | /** |
||
452 | * Get ordinal numbers of the defined enumerators as array |
||
453 | * @return int[] |
||
454 | */ |
||
455 | 42 | public function getOrdinals() |
|
456 | { |
||
457 | 42 | return $this->{$this->fnDoGetOrdinals}(); |
|
458 | } |
||
459 | |||
460 | /** |
||
461 | * Get ordinal numbers of the defined enumerators as array. |
||
462 | * |
||
463 | * This is the binary bitset implementation. |
||
464 | * |
||
465 | * @return int[] |
||
466 | * @see getOrdinals |
||
467 | * @see goGetOrdinalsInt |
||
468 | */ |
||
469 | 6 | private function doGetOrdinalsBin() |
|
470 | { |
||
471 | 6 | $ordinals = []; |
|
472 | 6 | $bitset = $this->bitset; |
|
473 | 6 | $byteLen = \strlen($bitset); |
|
474 | 6 | for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) { |
|
475 | 6 | if ($bitset[$bytePos] === "\0") { |
|
476 | // fast skip null byte |
||
477 | 6 | continue; |
|
478 | } |
||
479 | |||
480 | 6 | $ord = \ord($bitset[$bytePos]); |
|
481 | 6 | for ($bitPos = 0; $bitPos < 8; ++$bitPos) { |
|
482 | 6 | if ($ord & (1 << $bitPos)) { |
|
483 | 6 | $ordinals[] = $bytePos * 8 + $bitPos; |
|
484 | 2 | } |
|
485 | 2 | } |
|
486 | 2 | } |
|
487 | 6 | return $ordinals; |
|
488 | } |
||
489 | |||
490 | /** |
||
491 | * Get ordinal numbers of the defined enumerators as array. |
||
492 | * |
||
493 | * This is the integer bitset implementation. |
||
494 | * |
||
495 | * @return int[] |
||
496 | * @see getOrdinals |
||
497 | * @see doGetOrdinalsBin |
||
498 | */ |
||
499 | 36 | private function doGetOrdinalsInt() |
|
500 | { |
||
501 | 36 | $ordinals = []; |
|
502 | 36 | $ordinalMax = $this->ordinalMax; |
|
503 | 36 | $bitset = $this->bitset; |
|
504 | 36 | for ($ord = 0; $ord < $ordinalMax; ++$ord) { |
|
505 | 36 | if ($bitset & (1 << $ord)) { |
|
506 | 36 | $ordinals[] = $ord; |
|
507 | 12 | } |
|
508 | 12 | } |
|
509 | 36 | return $ordinals; |
|
510 | } |
||
511 | |||
512 | /** |
||
513 | * Get values of the defined enumerators as array |
||
514 | * @return null[]|bool[]|int[]|float[]|string[] |
||
515 | */ |
||
516 | 18 | public function getValues() |
|
517 | { |
||
518 | 18 | $enumeration = $this->enumeration; |
|
519 | 18 | $values = []; |
|
520 | 18 | foreach ($this->getOrdinals() as $ord) { |
|
521 | 18 | $values[] = $enumeration::byOrdinal($ord)->getValue(); |
|
522 | 6 | } |
|
523 | 18 | return $values; |
|
524 | } |
||
525 | |||
526 | /** |
||
527 | * Get names of the defined enumerators as array |
||
528 | * @return string[] |
||
529 | */ |
||
530 | 6 | public function getNames() |
|
531 | { |
||
532 | 6 | $enumeration = $this->enumeration; |
|
533 | 6 | $names = []; |
|
534 | 6 | foreach ($this->getOrdinals() as $ord) { |
|
535 | 6 | $names[] = $enumeration::byOrdinal($ord)->getName(); |
|
536 | 2 | } |
|
537 | 6 | return $names; |
|
538 | } |
||
539 | |||
540 | /** |
||
541 | * Get the defined enumerators as array |
||
542 | * @return Enum[] |
||
543 | */ |
||
544 | 6 | public function getEnumerators() |
|
545 | { |
||
546 | 6 | $enumeration = $this->enumeration; |
|
547 | 6 | $enumerators = []; |
|
548 | 6 | foreach ($this->getOrdinals() as $ord) { |
|
549 | 6 | $enumerators[] = $enumeration::byOrdinal($ord); |
|
550 | 2 | } |
|
551 | 6 | return $enumerators; |
|
552 | } |
||
553 | |||
554 | /** |
||
555 | * Get binary bitset in little-endian order |
||
556 | * |
||
557 | * @return string |
||
558 | */ |
||
559 | 18 | public function getBinaryBitsetLe() |
|
560 | { |
||
561 | 18 | return $this->{$this->fnDoGetBinaryBitsetLe}(); |
|
562 | } |
||
563 | |||
564 | /** |
||
565 | * Get binary bitset in little-endian order. |
||
566 | * |
||
567 | * This is the binary bitset implementation. |
||
568 | * |
||
569 | * @return string |
||
570 | */ |
||
571 | 12 | private function doGetBinaryBitsetLeBin() |
|
572 | { |
||
573 | 12 | return $this->bitset; |
|
574 | } |
||
575 | |||
576 | /** |
||
577 | * Get binary bitset in little-endian order. |
||
578 | * |
||
579 | * This is the integer bitset implementation. |
||
580 | * |
||
581 | * @return string |
||
582 | */ |
||
583 | 6 | private function doGetBinaryBitsetLeInt() |
|
584 | { |
||
585 | 6 | $bin = \pack(\PHP_INT_SIZE === 8 ? 'P' : 'V', $this->bitset); |
|
586 | 6 | return \substr($bin, 0, (int)\ceil($this->ordinalMax / 8)); |
|
587 | } |
||
588 | |||
589 | /** |
||
590 | * Set binary bitset in little-endian order |
||
591 | * |
||
592 | * NOTE: It resets the current position of the iterator |
||
593 | * |
||
594 | * @param string $bitset |
||
595 | * @return void |
||
596 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
597 | */ |
||
598 | 39 | public function setBinaryBitsetLe($bitset) |
|
599 | { |
||
600 | 39 | if (!\is_string($bitset)) { |
|
601 | 3 | throw new InvalidArgumentException('Bitset must be a string'); |
|
602 | } |
||
603 | |||
604 | 36 | $this->{$this->fnDoSetBinaryBitsetLe}($bitset); |
|
605 | |||
606 | // reset the iterator position |
||
607 | 18 | $this->rewind(); |
|
608 | 18 | } |
|
609 | |||
610 | /** |
||
611 | * Set binary bitset in little-endian order |
||
612 | * |
||
613 | * NOTE: It resets the current position of the iterator |
||
614 | * |
||
615 | * @param string $bitset |
||
616 | * @return void |
||
617 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
618 | */ |
||
619 | 21 | private function doSetBinaryBitsetLeBin($bitset) |
|
620 | { |
||
621 | 21 | $size = \strlen($this->bitset); |
|
622 | 21 | $sizeIn = \strlen($bitset); |
|
623 | |||
624 | 21 | if ($sizeIn < $size) { |
|
625 | // add "\0" if the given bitset is not long enough |
||
626 | 3 | $bitset .= \str_repeat("\0", $size - $sizeIn); |
|
627 | 19 | } elseif ($sizeIn > $size) { |
|
628 | 6 | if (\ltrim(\substr($bitset, $size), "\0") !== '') { |
|
629 | 3 | throw new InvalidArgumentException('Out-Of-Range bits detected'); |
|
630 | } |
||
631 | 3 | $bitset = \substr($bitset, 0, $size); |
|
632 | 1 | } |
|
633 | |||
634 | // truncate out-of-range bits of last byte |
||
635 | 18 | $lastByteMaxOrd = $this->ordinalMax % 8; |
|
636 | 18 | if ($lastByteMaxOrd !== 0) { |
|
637 | 18 | $lastByte = $bitset[$size - 1]; |
|
638 | 18 | $lastByteExpected = \chr((1 << $lastByteMaxOrd) - 1) & $lastByte; |
|
639 | 18 | if ($lastByte !== $lastByteExpected) { |
|
640 | 6 | throw new InvalidArgumentException('Out-Of-Range bits detected'); |
|
641 | } |
||
642 | |||
643 | 12 | $this->bitset = \substr($bitset, 0, -1) . $lastByteExpected; |
|
644 | 4 | } |
|
645 | |||
646 | 12 | $this->bitset = $bitset; |
|
647 | 12 | } |
|
648 | |||
649 | /** |
||
650 | * Set binary bitset in little-endian order |
||
651 | * |
||
652 | * NOTE: It resets the current position of the iterator |
||
653 | * |
||
654 | * @param string $bitset |
||
655 | * @return void |
||
656 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
657 | */ |
||
658 | 15 | private function doSetBinaryBitsetLeInt($bitset) |
|
677 | 6 | } |
|
678 | |||
679 | /** |
||
680 | * Get binary bitset in big-endian order |
||
681 | * |
||
682 | * @return string |
||
683 | */ |
||
684 | 3 | public function getBinaryBitsetBe() |
|
685 | { |
||
686 | 3 | return \strrev($this->bitset); |
|
687 | } |
||
688 | |||
689 | /** |
||
690 | * Set binary bitset in big-endian order |
||
691 | * |
||
692 | * NOTE: It resets the current position of the iterator |
||
693 | * |
||
694 | * @param string $bitset |
||
695 | * @return void |
||
696 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
697 | */ |
||
698 | 6 | public function setBinaryBitsetBe($bitset) |
|
699 | { |
||
700 | 6 | if (!\is_string($bitset)) { |
|
701 | 3 | throw new InvalidArgumentException('Bitset must be a string'); |
|
702 | } |
||
703 | 3 | $this->setBinaryBitsetLe(\strrev($bitset)); |
|
704 | 3 | } |
|
705 | |||
706 | /** |
||
707 | * Get a bit at the given ordinal number |
||
708 | * |
||
709 | * @param int $ordinal Ordinal number of bit to get |
||
710 | * @return boolean |
||
711 | */ |
||
712 | 9 | public function getBit($ordinal) |
|
719 | } |
||
720 | |||
721 | /** |
||
722 | * Get a bit at the given ordinal number. |
||
723 | * |
||
724 | * This is the binary bitset implementation. |
||
725 | * |
||
726 | * @param int $ordinal Ordinal number of bit to get |
||
727 | * @return boolean |
||
728 | * @see getBit |
||
729 | * @see doGetBitInt |
||
730 | */ |
||
731 | 27 | private function doGetBitBin($ordinal) |
|
732 | { |
||
733 | 27 | return (\ord($this->bitset[(int) ($ordinal / 8)]) & 1 << ($ordinal % 8)) !== 0; |
|
734 | } |
||
735 | |||
736 | /** |
||
737 | * Get a bit at the given ordinal number. |
||
738 | * |
||
739 | * This is the integer bitset implementation. |
||
740 | * |
||
741 | * @param int $ordinal Ordinal number of bit to get |
||
742 | * @return boolean |
||
743 | * @see getBit |
||
744 | * @see doGetBitBin |
||
745 | */ |
||
746 | 48 | private function doGetBitInt($ordinal) |
|
747 | { |
||
748 | 48 | return (bool)($this->bitset & (1 << $ordinal)); |
|
749 | } |
||
750 | |||
751 | /** |
||
752 | * Set a bit at the given ordinal number |
||
753 | * |
||
754 | * @param int $ordinal Ordnal number of bit to set |
||
755 | * @param bool $bit The bit to set |
||
756 | * @return void |
||
757 | * @see doSetBitBin |
||
758 | * @see doSetBitInt |
||
759 | * @see doUnsetBin |
||
760 | * @see doUnsetInt |
||
761 | */ |
||
762 | 6 | public function setBit($ordinal, $bit) |
|
763 | { |
||
764 | 6 | if ($ordinal < 0 || $ordinal > $this->ordinalMax) { |
|
765 | 3 | throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->ordinalMax}"); |
|
766 | } |
||
767 | |||
768 | 3 | if ($bit) { |
|
769 | 3 | $this->{$this->fnDoSetBit}($ordinal); |
|
770 | 1 | } else { |
|
771 | 3 | $this->{$this->fnDoUnsetBit}($ordinal); |
|
772 | } |
||
773 | 3 | } |
|
774 | |||
775 | /** |
||
776 | * Set a bit at the given ordinal number. |
||
777 | * |
||
778 | * This is the binary bitset implementation. |
||
779 | * |
||
780 | * @param int $ordinal Ordnal number of bit to set |
||
781 | * @return void |
||
782 | * @see setBit |
||
783 | * @see doSetBitInt |
||
784 | */ |
||
785 | 21 | private function doSetBitBin($ordinal) |
|
786 | { |
||
787 | 21 | $byte = (int) ($ordinal / 8); |
|
788 | 21 | $this->bitset[$byte] = $this->bitset[$byte] | \chr(1 << ($ordinal % 8)); |
|
789 | 21 | } |
|
790 | |||
791 | /** |
||
792 | * Set a bit at the given ordinal number. |
||
793 | * |
||
794 | * This is the binary bitset implementation. |
||
795 | * |
||
796 | * @param int $ordinal Ordnal number of bit to set |
||
797 | * @return void |
||
798 | * @see setBit |
||
799 | * @see doSetBitBin |
||
800 | */ |
||
801 | 105 | private function doSetBitInt($ordinal) |
|
802 | { |
||
803 | 105 | $this->bitset = $this->bitset | (1 << $ordinal); |
|
804 | 105 | } |
|
805 | |||
806 | /** |
||
807 | * Unset a bit at the given ordinal number. |
||
808 | * |
||
809 | * This is the binary bitset implementation. |
||
810 | * |
||
811 | * @param int $ordinal Ordinal number of bit to unset |
||
812 | * @return void |
||
813 | * @see setBit |
||
814 | * @see doUnsetBitInt |
||
815 | */ |
||
816 | 9 | private function doUnsetBitBin($ordinal) |
|
817 | { |
||
818 | 9 | $byte = (int) ($ordinal / 8); |
|
819 | 9 | $this->bitset[$byte] = $this->bitset[$byte] & \chr(~(1 << ($ordinal % 8))); |
|
820 | 9 | } |
|
821 | |||
822 | /** |
||
823 | * Unset a bit at the given ordinal number. |
||
824 | * |
||
825 | * This is the integer bitset implementation. |
||
826 | * |
||
827 | * @param int $ordinal Ordinal number of bit to unset |
||
828 | * @return void |
||
829 | * @see setBit |
||
830 | * @see doUnsetBitBin |
||
831 | */ |
||
832 | 15 | private function doUnsetBitInt($ordinal) |
|
835 | 15 | } |
|
836 | } |
||
837 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.