1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace MabeEnum; |
4
|
|
|
|
5
|
|
|
use Iterator; |
6
|
|
|
use Countable; |
7
|
|
|
use InvalidArgumentException; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* EnumSet implementation in base of an integer bit set |
11
|
|
|
* |
12
|
|
|
* @link http://github.com/marc-mabe/php-enum for the canonical source repository |
13
|
|
|
* @copyright Copyright (c) 2015 Marc Bennewitz |
14
|
|
|
* @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License |
15
|
|
|
*/ |
16
|
|
|
class EnumSet implements Iterator, Countable |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* The classname of the Enumeration |
20
|
|
|
* @var string |
21
|
|
|
*/ |
22
|
|
|
private $enumeration; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* BitSet of all attached enumerations in little endian |
26
|
|
|
* @var string |
27
|
|
|
*/ |
28
|
|
|
private $bitset; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Ordinal number of current iterator position |
32
|
|
|
* @var int |
33
|
|
|
*/ |
34
|
|
|
private $ordinal = 0; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Highest possible ordinal number |
38
|
|
|
* @var int |
39
|
|
|
*/ |
40
|
|
|
private $ordinalMax; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Constructor |
44
|
|
|
* |
45
|
|
|
* @param string $enumeration The classname of the enumeration |
46
|
|
|
* @throws InvalidArgumentException |
47
|
|
|
*/ |
48
|
46 |
|
public function __construct($enumeration) |
49
|
|
|
{ |
50
|
46 |
View Code Duplication |
if (!is_subclass_of($enumeration, __NAMESPACE__ . '\Enum')) { |
51
|
1 |
|
throw new InvalidArgumentException(sprintf( |
52
|
1 |
|
"This EnumSet can handle subclasses of '%s' only", |
53
|
|
|
__NAMESPACE__ . '\Enum' |
54
|
1 |
|
)); |
55
|
|
|
} |
56
|
|
|
|
57
|
45 |
|
$this->enumeration = $enumeration; |
58
|
45 |
|
$this->ordinalMax = count($enumeration::getConstants()); |
59
|
|
|
|
60
|
|
|
// init the bitset with zeros |
61
|
45 |
|
$this->bitset = str_repeat("\0", ceil($this->ordinalMax / 8)); |
62
|
45 |
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Get the classname of enumeration this set is for |
66
|
|
|
* @return string |
67
|
|
|
* @deprecated Please use getEnumeration() instead |
68
|
|
|
*/ |
69
|
|
|
public function getEnumClass() |
70
|
|
|
{ |
71
|
|
|
return $this->getEnumeration(); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Get the classname of the enumeration |
76
|
|
|
* @return string |
77
|
|
|
*/ |
78
|
12 |
|
public function getEnumeration() |
79
|
|
|
{ |
80
|
12 |
|
return $this->enumeration; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Attach a new enumerator or overwrite an existing one |
85
|
|
|
* @param Enum|null|boolean|int|float|string $enumerator |
86
|
|
|
* @return void |
87
|
|
|
* @throws InvalidArgumentException On an invalid given enumerator |
88
|
|
|
*/ |
89
|
35 |
|
public function attach($enumerator) |
90
|
|
|
{ |
91
|
35 |
|
$enumeration = $this->enumeration; |
92
|
35 |
|
$this->setBit($enumeration::get($enumerator)->getOrdinal()); |
93
|
35 |
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Detach the given enumerator |
97
|
|
|
* @param Enum|null|boolean|int|float|string $enumerator |
98
|
|
|
* @return void |
99
|
|
|
* @throws InvalidArgumentException On an invalid given enumerator |
100
|
|
|
*/ |
101
|
7 |
|
public function detach($enumerator) |
102
|
|
|
{ |
103
|
7 |
|
$enumeration = $this->enumeration; |
104
|
7 |
|
$this->unsetBit($enumeration::get($enumerator)->getOrdinal()); |
105
|
7 |
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Test if the given enumerator was attached |
109
|
|
|
* @param Enum|null|boolean|int|float|string $enumerator |
110
|
|
|
* @return boolean |
111
|
|
|
*/ |
112
|
11 |
|
public function contains($enumerator) |
113
|
|
|
{ |
114
|
11 |
|
$enumeration = $this->enumeration; |
115
|
11 |
|
return $this->getBit($enumeration::get($enumerator)->getOrdinal()); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/* Iterator */ |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Get the current enumerator |
122
|
|
|
* @return Enum|null Returns the current enumerator or NULL on an invalid iterator position |
123
|
|
|
*/ |
124
|
9 |
|
public function current() |
125
|
|
|
{ |
126
|
9 |
|
if ($this->valid()) { |
127
|
9 |
|
$enumeration = $this->enumeration; |
128
|
9 |
|
return $enumeration::getByOrdinal($this->ordinal); |
129
|
|
|
} |
130
|
|
|
|
131
|
2 |
|
return null; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Get the ordinal number of the current iterator position |
136
|
|
|
* @return int |
137
|
|
|
*/ |
138
|
6 |
|
public function key() |
139
|
|
|
{ |
140
|
6 |
|
return $this->ordinal; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Go to the next valid iterator position. |
145
|
|
|
* If no valid iterator position is found the iterator position will be the last possible + 1. |
146
|
|
|
* @return void |
147
|
|
|
*/ |
148
|
16 |
|
public function next() |
149
|
|
|
{ |
150
|
|
|
do { |
151
|
16 |
|
if (++$this->ordinal >= $this->ordinalMax) { |
152
|
4 |
|
$this->ordinal = $this->ordinalMax; |
153
|
4 |
|
return; |
154
|
|
|
} |
155
|
16 |
|
} while (!$this->getBit($this->ordinal)); |
156
|
16 |
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Go to the first valid iterator position. |
160
|
|
|
* If no valid iterator position in found the iterator position will be 0. |
161
|
|
|
* @return void |
162
|
|
|
*/ |
163
|
10 |
|
public function rewind() |
164
|
|
|
{ |
165
|
10 |
|
if (trim($this->bitset, "\0") !== '') { |
166
|
10 |
|
$this->ordinal = -1; |
167
|
10 |
|
$this->next(); |
168
|
10 |
|
} else { |
169
|
1 |
|
$this->ordinal = 0; |
170
|
|
|
} |
171
|
10 |
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Test if the iterator in a valid state |
175
|
|
|
* @return boolean |
176
|
|
|
*/ |
177
|
10 |
|
public function valid() |
178
|
|
|
{ |
179
|
10 |
|
return $this->ordinal !== $this->ordinalMax && $this->getBit($this->ordinal); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/* Countable */ |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Count the number of elements |
186
|
|
|
* @return int |
187
|
|
|
*/ |
188
|
11 |
|
public function count() |
189
|
|
|
{ |
190
|
11 |
|
$cnt = 0; |
191
|
11 |
|
$ord = 0; |
192
|
|
|
|
193
|
11 |
|
while ($ord !== $this->ordinalMax) { |
194
|
10 |
|
if ($this->getBit($ord++)) { |
195
|
10 |
|
++$cnt; |
196
|
10 |
|
} |
197
|
10 |
|
} |
198
|
|
|
|
199
|
11 |
|
return $cnt; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Check if this EnumSet is the same as other |
204
|
|
|
* @param EnumSet $other |
205
|
|
|
* @return bool |
206
|
|
|
*/ |
207
|
2 |
|
public function isEqual(EnumSet $other) |
208
|
|
|
{ |
209
|
2 |
|
return $this->getEnumeration() === $other->getEnumeration() |
210
|
2 |
|
&& $this->getBinaryBitsetLe() === $other->getBinaryBitsetLe(); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Check if this EnumSet is a subset of other |
215
|
|
|
* @param EnumSet $other |
216
|
|
|
* @return bool |
217
|
|
|
*/ |
218
|
4 |
View Code Duplication |
public function isSubset(EnumSet $other) |
|
|
|
|
219
|
|
|
{ |
220
|
4 |
|
if ($this->getEnumeration() !== $other->getEnumeration()) { |
221
|
1 |
|
return false; |
222
|
|
|
} |
223
|
|
|
|
224
|
3 |
|
$thisBitset = $this->getBinaryBitsetLe(); |
225
|
3 |
|
return ($thisBitset & $other->getBinaryBitsetLe()) === $thisBitset; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Check if this EnumSet is a superset of other |
230
|
|
|
* @param EnumSet $other |
231
|
|
|
* @return bool |
232
|
|
|
*/ |
233
|
4 |
View Code Duplication |
public function isSuperset(EnumSet $other) |
|
|
|
|
234
|
|
|
{ |
235
|
4 |
|
if ($this->getEnumeration() !== $other->getEnumeration()) { |
236
|
1 |
|
return false; |
237
|
|
|
} |
238
|
|
|
|
239
|
3 |
|
$thisBitset = $this->getBinaryBitsetLe(); |
240
|
3 |
|
return ($thisBitset | $other->getBinaryBitsetLe()) === $thisBitset; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Produce a new set with enumerators from both this and other (this | other) |
245
|
|
|
* @param EnumSet $other,... Other EnumSet(s) of the same enumeration to produce the union |
|
|
|
|
246
|
|
|
* @return EnumSet |
247
|
|
|
*/ |
248
|
1 |
View Code Duplication |
public function union(EnumSet $other) |
|
|
|
|
249
|
|
|
{ |
250
|
1 |
|
$bitset = $this->bitset; |
251
|
1 |
|
foreach (func_get_args() as $other) { |
252
|
1 |
|
if ($this->enumeration !== $other->enumeration) { |
253
|
|
|
throw new InvalidArgumentException(sprintf( |
254
|
|
|
"Others should be an %s of the same enumeration as this '%s'", |
255
|
|
|
__CLASS__, |
256
|
|
|
$this->enumeration |
257
|
|
|
)); |
258
|
|
|
} |
259
|
|
|
|
260
|
1 |
|
$bitset |= $other->bitset; |
261
|
1 |
|
} |
262
|
|
|
|
263
|
1 |
|
$clone = clone $this; |
264
|
1 |
|
$clone->bitset = $bitset; |
265
|
1 |
|
return $clone; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* Produce a new set with enumerators common to both this and other (this & other) |
270
|
|
|
* @param EnumSet $other,... Other EnumSet(s) of the same enumeration to produce the union |
|
|
|
|
271
|
|
|
* @return EnumSet |
272
|
|
|
*/ |
273
|
1 |
View Code Duplication |
public function intersect(EnumSet $other) |
|
|
|
|
274
|
|
|
{ |
275
|
1 |
|
$bitset = $this->bitset; |
276
|
1 |
|
foreach (func_get_args() as $other) { |
277
|
1 |
|
if ($this->enumeration !== $other->enumeration) { |
278
|
|
|
throw new InvalidArgumentException(sprintf( |
279
|
|
|
"Others should be an %s of the same enumeration as this '%s'", |
280
|
|
|
__CLASS__, |
281
|
|
|
$this->enumeration |
282
|
|
|
)); |
283
|
|
|
} |
284
|
|
|
|
285
|
1 |
|
$bitset &= $other->bitset; |
286
|
1 |
|
} |
287
|
|
|
|
288
|
1 |
|
$clone = clone $this; |
289
|
1 |
|
$clone->bitset = $bitset; |
290
|
1 |
|
return $clone; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Produce a new set with enumerators in this but not in other (this - (other | other)) |
295
|
|
|
* @param EnumSet $other,... Other EnumSet(s) of the same enumeration to produce the union |
|
|
|
|
296
|
|
|
* @return EnumSet |
297
|
|
|
*/ |
298
|
1 |
View Code Duplication |
public function diff(EnumSet $other) |
|
|
|
|
299
|
|
|
{ |
300
|
1 |
|
$bitset = ''; |
301
|
1 |
|
foreach (func_get_args() as $other) { |
302
|
1 |
|
if ($this->enumeration !== $other->enumeration) { |
303
|
|
|
throw new InvalidArgumentException(sprintf( |
304
|
|
|
"Others should be an %s of the same enumeration as this '%s'", |
305
|
|
|
__CLASS__, |
306
|
|
|
$this->enumeration |
307
|
|
|
)); |
308
|
|
|
} |
309
|
|
|
|
310
|
1 |
|
$bitset |= $other->bitset; |
311
|
1 |
|
} |
312
|
|
|
|
313
|
1 |
|
$clone = clone $this; |
314
|
1 |
|
$clone->bitset = $this->bitset & ~$bitset; |
315
|
1 |
|
return $clone; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Produce a new set with enumerators in either this and other but not in both (this ^ (other | other)) |
320
|
|
|
* @param EnumSet $other,... Other EnumSet(s) of the same enumeration to produce the union |
|
|
|
|
321
|
|
|
* @return EnumSet |
322
|
|
|
*/ |
323
|
1 |
View Code Duplication |
public function symDiff(EnumSet $other) |
|
|
|
|
324
|
|
|
{ |
325
|
1 |
|
$bitset = ''; |
326
|
1 |
|
foreach (func_get_args() as $other) { |
327
|
1 |
|
if ($this->enumeration !== $other->enumeration) { |
328
|
|
|
throw new InvalidArgumentException(sprintf( |
329
|
|
|
"Others should be an %s of the same enumeration as this '%s'", |
330
|
|
|
__CLASS__, |
331
|
|
|
$this->enumeration |
332
|
|
|
)); |
333
|
|
|
} |
334
|
|
|
|
335
|
1 |
|
$bitset |= $other->bitset; |
336
|
1 |
|
} |
337
|
|
|
|
338
|
1 |
|
$clone = clone $this; |
339
|
1 |
|
$clone->bitset = $this->bitset ^ $bitset; |
340
|
1 |
|
return $clone; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Get ordinal numbers of the defined enumerators as array |
345
|
|
|
* @return int[] |
346
|
|
|
*/ |
347
|
12 |
|
public function getOrdinals() |
348
|
|
|
{ |
349
|
12 |
|
$ordinals = array(); |
350
|
12 |
|
$byteLen = strlen($this->bitset); |
351
|
|
|
|
352
|
12 |
|
for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) { |
353
|
12 |
|
if ($this->bitset[$bytePos] === "\0") { |
354
|
12 |
|
continue; // fast skip null byte |
355
|
|
|
} |
356
|
|
|
|
357
|
12 |
|
for ($bitPos = 0; $bitPos < 8; ++$bitPos) { |
358
|
12 |
|
if ((ord($this->bitset[$bytePos]) & (1 << $bitPos)) !== 0) { |
359
|
12 |
|
$ordinals[] = $bytePos * 8 + $bitPos; |
360
|
12 |
|
} |
361
|
12 |
|
} |
362
|
12 |
|
} |
|
|
|
|
363
|
|
|
|
364
|
12 |
|
return $ordinals; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
/** |
368
|
|
|
* Get values of the defined enumerators as array |
369
|
|
|
* @return null[]|bool[]|int[]|float[]|string[] |
370
|
|
|
*/ |
371
|
6 |
|
public function getValues() |
372
|
|
|
{ |
373
|
6 |
|
$values = array(); |
374
|
6 |
|
foreach ($this->getEnumerators() as $enumerator) { |
375
|
6 |
|
$values[] = $enumerator->getValue(); |
376
|
6 |
|
} |
377
|
6 |
|
return $values; |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* Get names of the defined enumerators as array |
382
|
|
|
* @return string[] |
383
|
|
|
*/ |
384
|
2 |
|
public function getNames() |
385
|
|
|
{ |
386
|
2 |
|
$names = array(); |
387
|
2 |
|
foreach ($this->getEnumerators() as $enumerator) { |
388
|
2 |
|
$names[] = $enumerator->getName(); |
389
|
2 |
|
} |
390
|
2 |
|
return $names; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
/** |
394
|
|
|
* Get the defined enumerators as array |
395
|
|
|
* @return Enum[] |
396
|
|
|
*/ |
397
|
10 |
|
public function getEnumerators() |
398
|
|
|
{ |
399
|
10 |
|
$enumeration = $this->enumeration; |
400
|
10 |
|
$enumerators = array(); |
401
|
10 |
|
foreach ($this->getOrdinals() as $ord) { |
402
|
10 |
|
$enumerators[] = $enumeration::getByOrdinal($ord); |
403
|
10 |
|
} |
404
|
10 |
|
return $enumerators; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Get binary bitset in little-endian order |
409
|
|
|
* |
410
|
|
|
* @return string |
411
|
|
|
*/ |
412
|
10 |
|
public function getBinaryBitsetLe() |
413
|
|
|
{ |
414
|
10 |
|
return $this->bitset; |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
/** |
418
|
|
|
* Set binary bitset in little-endian order |
419
|
|
|
* |
420
|
|
|
* NOTE: It resets the current position of the iterator |
421
|
|
|
* |
422
|
|
|
* @param string $bitset |
423
|
|
|
* @return void |
424
|
|
|
* @throws InvalidArgumentException On a non string is given as Parameter |
425
|
|
|
*/ |
426
|
6 |
|
public function setBinaryBitsetLe($bitset) |
427
|
|
|
{ |
428
|
6 |
|
if (!is_string($bitset)) { |
429
|
1 |
|
throw new InvalidArgumentException('Bitset must be a string'); |
430
|
|
|
} |
431
|
|
|
|
432
|
5 |
|
$size = ceil($this->ordinalMax / 8); |
433
|
5 |
|
$sizeIn = strlen($bitset); |
434
|
|
|
|
435
|
5 |
|
if ($sizeIn < $size) { |
436
|
|
|
// add "\0" if the given bitset is not long enough |
437
|
1 |
|
$bitset .= str_repeat("\0", $size - $sizeIn); |
438
|
5 |
|
} elseif ($sizeIn > $size) { |
439
|
1 |
|
$bitset = substr($bitset, 0, $size); |
440
|
1 |
|
} |
441
|
|
|
|
442
|
5 |
|
$this->bitset = $bitset; |
443
|
|
|
|
444
|
5 |
|
$this->rewind(); |
445
|
5 |
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* Get binary bitset in big-endian order |
449
|
|
|
* |
450
|
|
|
* @return string |
451
|
|
|
*/ |
452
|
2 |
|
public function getBinaryBitsetBe() |
453
|
|
|
{ |
454
|
2 |
|
return strrev($this->bitset); |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
/** |
458
|
|
|
* Set binary bitset in big-endian order |
459
|
|
|
* |
460
|
|
|
* NOTE: It resets the current position of the iterator |
461
|
|
|
* |
462
|
|
|
* @param string $bitset |
463
|
|
|
* @return void |
464
|
|
|
* @throws InvalidArgumentException On a non string is given as Parameter |
465
|
|
|
*/ |
466
|
3 |
|
public function setBinaryBitsetBe($bitset) |
467
|
|
|
{ |
468
|
3 |
|
if (!is_string($bitset)) { |
469
|
1 |
|
throw new InvalidArgumentException('Bitset must be a string'); |
470
|
|
|
} |
471
|
|
|
|
472
|
2 |
|
$this->setBinaryBitsetLe(strrev($bitset)); |
473
|
2 |
|
} |
474
|
|
|
|
475
|
|
|
/** |
476
|
|
|
* Get the binary bitset |
477
|
|
|
* |
478
|
|
|
* @return string Returns the binary bitset in big-endian order |
479
|
|
|
* @deprecated Please use getBinaryBitsetBe() instead |
480
|
|
|
*/ |
481
|
|
|
public function getBitset() |
482
|
|
|
{ |
483
|
|
|
return $this->getBinaryBitsetBe(); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
/** |
487
|
|
|
* Set the bitset. |
488
|
|
|
* NOTE: It resets the current position of the iterator |
489
|
|
|
* |
490
|
|
|
* @param string $bitset The binary bitset in big-endian order |
491
|
|
|
* @return void |
492
|
|
|
* @throws InvalidArgumentException On a non string is given as Parameter |
493
|
|
|
* @deprecated Please use setBinaryBitsetBe() instead |
494
|
|
|
*/ |
495
|
|
|
public function setBitset($bitset) |
496
|
|
|
{ |
497
|
|
|
$this->setBinaryBitsetBE($bitset); |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
/** |
501
|
|
|
* Get a bit at the given ordinal number |
502
|
|
|
* |
503
|
|
|
* @param $ordinal int Ordinal number of bit to get |
504
|
|
|
* @return boolean |
505
|
|
|
*/ |
506
|
22 |
|
private function getBit($ordinal) |
507
|
|
|
{ |
508
|
22 |
|
return (ord($this->bitset[(int) ($ordinal / 8)]) & 1 << ($ordinal % 8)) !== 0; |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* Set a bit at the given ordinal number |
513
|
|
|
* |
514
|
|
|
* @param $ordinal int Ordnal number of bit to set |
515
|
|
|
* @return void |
516
|
|
|
*/ |
517
|
35 |
|
private function setBit($ordinal) |
518
|
|
|
{ |
519
|
35 |
|
$byte = (int) ($ordinal / 8); |
520
|
35 |
|
$this->bitset[$byte] = $this->bitset[$byte] | chr(1 << ($ordinal % 8)); |
521
|
35 |
|
} |
522
|
|
|
|
523
|
|
|
/** |
524
|
|
|
* Unset a bit at the given ordinal number |
525
|
|
|
* |
526
|
|
|
* @param $ordinal int Ordinal number of bit to unset |
527
|
|
|
* @return void |
528
|
|
|
*/ |
529
|
7 |
|
private function unsetBit($ordinal) |
530
|
|
|
{ |
531
|
7 |
|
$byte = (int) ($ordinal / 8); |
532
|
7 |
|
$this->bitset[$byte] = $this->bitset[$byte] & chr(~(1 << ($ordinal % 8))); |
533
|
7 |
|
} |
534
|
|
|
} |
535
|
|
|
|
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.