1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace MabeEnum; |
4
|
|
|
|
5
|
|
|
use Countable; |
6
|
|
|
use Iterator; |
7
|
|
|
use InvalidArgumentException; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* This EnumSet is based on a bitset of a binary string. |
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
|
|
|
* Ordinal number of current iterator position |
26
|
|
|
* @var int |
27
|
|
|
*/ |
28
|
|
|
private $ordinal = 0; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Highest possible ordinal number |
32
|
|
|
* @var int |
33
|
|
|
*/ |
34
|
|
|
private $ordinalMax; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Integer or binary (little endian) bitset |
38
|
|
|
* @var int|string |
39
|
|
|
*/ |
40
|
|
|
private $bitset = 0; |
41
|
|
|
|
42
|
|
|
/**#@+ |
43
|
|
|
* Defined the private method names to be called depended of |
44
|
|
|
* 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
|
100 |
|
public function __construct($enumeration) |
67
|
|
|
{ |
68
|
100 |
|
if (!is_subclass_of($enumeration, Enum::class)) { |
69
|
2 |
|
throw new InvalidArgumentException(sprintf( |
70
|
2 |
|
"%s can handle subclasses of '%s' only", |
71
|
2 |
|
static::class, |
72
|
1 |
|
Enum::class |
73
|
1 |
|
)); |
74
|
|
|
} |
75
|
|
|
|
76
|
98 |
|
$this->enumeration = $enumeration; |
77
|
98 |
|
$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
|
98 |
|
if ($this->ordinalMax > PHP_INT_SIZE * 8) { |
83
|
|
|
// init binary bitset with zeros |
84
|
18 |
|
$this->bitset = str_repeat("\0", ceil($this->ordinalMax / 8)); |
85
|
|
|
|
86
|
|
|
// switch internal binary bitset functions |
87
|
18 |
|
$this->fnDoRewind = 'doRewindBin'; |
88
|
18 |
|
$this->fnDoCount = 'doCountBin'; |
89
|
18 |
|
$this->fnDoGetOrdinals = 'doGetOrdinalsBin'; |
90
|
18 |
|
$this->fnDoGetBit = 'doGetBitBin'; |
91
|
18 |
|
$this->fnDoSetBit = 'doSetBitBin'; |
92
|
18 |
|
$this->fnDoUnsetBit = 'doUnsetBitBin'; |
93
|
18 |
|
$this->fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeBin'; |
94
|
18 |
|
$this->fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeBin'; |
95
|
9 |
|
} |
96
|
98 |
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Get the classname of the enumeration |
100
|
|
|
* @return string |
101
|
|
|
*/ |
102
|
2 |
|
public function getEnumeration() |
103
|
|
|
{ |
104
|
2 |
|
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
|
68 |
|
public function attach($enumerator) |
114
|
|
|
{ |
115
|
68 |
|
$enumeration = $this->enumeration; |
116
|
68 |
|
$this->{$this->fnDoSetBit}($enumeration::get($enumerator)->getOrdinal()); |
117
|
68 |
|
} |
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
|
12 |
|
public function detach($enumerator) |
126
|
|
|
{ |
127
|
12 |
|
$enumeration = $this->enumeration; |
128
|
12 |
|
$this->{$this->fnDoUnsetBit}($enumeration::get($enumerator)->getOrdinal()); |
129
|
12 |
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Test if the given enumerator was attached |
133
|
|
|
* @param Enum|null|boolean|int|float|string $enumerator |
134
|
|
|
* @return boolean |
135
|
|
|
*/ |
136
|
18 |
|
public function contains($enumerator) |
137
|
|
|
{ |
138
|
18 |
|
$enumeration = $this->enumeration; |
139
|
18 |
|
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
|
18 |
|
public function current() |
149
|
|
|
{ |
150
|
18 |
|
if ($this->valid()) { |
151
|
18 |
|
$enumeration = $this->enumeration; |
152
|
18 |
|
return $enumeration::byOrdinal($this->ordinal); |
153
|
|
|
} |
154
|
|
|
|
155
|
4 |
|
return null; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Get the ordinal number of the current iterator position |
160
|
|
|
* @return int |
161
|
|
|
*/ |
162
|
12 |
|
public function key() |
163
|
|
|
{ |
164
|
12 |
|
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
|
30 |
|
public function next() |
173
|
|
|
{ |
174
|
|
|
do { |
175
|
30 |
|
if (++$this->ordinal >= $this->ordinalMax) { |
176
|
8 |
|
$this->ordinal = $this->ordinalMax; |
177
|
8 |
|
return; |
178
|
|
|
} |
179
|
30 |
|
} while (!$this->{$this->fnDoGetBit}($this->ordinal)); |
180
|
30 |
|
} |
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
|
18 |
|
public function rewind() |
190
|
|
|
{ |
191
|
18 |
|
$this->{$this->fnDoRewind}(); |
192
|
18 |
|
} |
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
|
6 |
|
private function doRewindBin() |
205
|
|
|
{ |
206
|
6 |
|
if (trim($this->bitset, "\0") !== '') { |
207
|
6 |
|
$this->ordinal = -1; |
208
|
6 |
|
$this->next(); |
209
|
3 |
|
} else { |
210
|
|
|
$this->ordinal = 0; |
211
|
|
|
} |
212
|
6 |
|
} |
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
|
12 |
|
private function doRewindInt() |
225
|
|
|
{ |
226
|
12 |
|
if ($this->bitset) { |
227
|
12 |
|
$this->ordinal = -1; |
228
|
12 |
|
$this->next(); |
229
|
6 |
|
} else { |
230
|
2 |
|
$this->ordinal = 0; |
231
|
|
|
} |
232
|
12 |
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* Test if the iterator in a valid state |
236
|
|
|
* @return boolean |
237
|
|
|
*/ |
238
|
20 |
|
public function valid() |
239
|
|
|
{ |
240
|
20 |
|
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
|
18 |
|
public function count() |
253
|
|
|
{ |
254
|
18 |
|
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
|
8 |
|
private function doCountBin() |
267
|
|
|
{ |
268
|
8 |
|
$count = 0; |
269
|
8 |
|
$byteLen = strlen($this->bitset); |
270
|
8 |
|
for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) { |
271
|
8 |
|
if ($this->bitset[$bytePos] === "\0") { |
272
|
|
|
// fast skip null byte |
273
|
8 |
|
continue; |
274
|
|
|
} |
275
|
|
|
|
276
|
8 |
|
$ord = ord($this->bitset[$bytePos]); |
277
|
8 |
|
for ($bitPos = 0; $bitPos < 8; ++$bitPos) { |
278
|
8 |
|
if ($ord & (1 << $bitPos)) { |
279
|
8 |
|
++$count; |
280
|
4 |
|
} |
281
|
4 |
|
} |
282
|
4 |
|
} |
283
|
8 |
|
return $count; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Count the number of elements. |
288
|
|
|
* |
289
|
|
|
* This is the integer bitset implementation. |
290
|
|
|
* |
291
|
|
|
* @return int |
292
|
|
|
* @see count |
293
|
|
|
* @see doCountBin |
294
|
|
|
*/ |
295
|
10 |
|
private function doCountInt() |
296
|
|
|
{ |
297
|
10 |
|
$count = 0; |
298
|
10 |
|
$ord = 0; |
299
|
10 |
|
while ($ord !== $this->ordinalMax) { |
300
|
8 |
|
if ($this->bitset & (1 << $ord++)) { |
301
|
8 |
|
++$count; |
302
|
4 |
|
} |
303
|
4 |
|
} |
304
|
10 |
|
return $count; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Check if this EnumSet is the same as other |
309
|
|
|
* @param EnumSet $other |
310
|
|
|
* @return bool |
311
|
|
|
*/ |
312
|
6 |
|
public function isEqual(EnumSet $other) |
313
|
|
|
{ |
314
|
6 |
|
return $this->enumeration === $other->enumeration |
315
|
6 |
|
&& $this->bitset === $other->bitset; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Check if this EnumSet is a subset of other |
320
|
|
|
* @param EnumSet $other |
321
|
|
|
* @return bool |
322
|
|
|
*/ |
323
|
8 |
View Code Duplication |
public function isSubset(EnumSet $other) |
|
|
|
|
324
|
|
|
{ |
325
|
8 |
|
if ($this->enumeration !== $other->enumeration) { |
326
|
2 |
|
return false; |
327
|
|
|
} |
328
|
|
|
|
329
|
6 |
|
return ($this->bitset & $other->bitset) === $this->bitset; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* Check if this EnumSet is a superset of other |
334
|
|
|
* @param EnumSet $other |
335
|
|
|
* @return bool |
336
|
|
|
*/ |
337
|
8 |
View Code Duplication |
public function isSuperset(EnumSet $other) |
|
|
|
|
338
|
|
|
{ |
339
|
8 |
|
if ($this->enumeration !== $other->enumeration) { |
340
|
2 |
|
return false; |
341
|
|
|
} |
342
|
|
|
|
343
|
6 |
|
return ($this->bitset | $other->bitset) === $this->bitset; |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Produce a new set with enumerators from both this and other (this | other) |
348
|
|
|
* @param EnumSet ...$others Other EnumSet(s) of the same enumeration to produce the union |
349
|
|
|
* @return EnumSet |
350
|
|
|
*/ |
|
|
|
|
351
|
4 |
View Code Duplication |
public function union(EnumSet ...$others) |
|
|
|
|
352
|
|
|
{ |
353
|
4 |
|
$bitset = $this->bitset; |
354
|
4 |
|
foreach ($others as $other) { |
355
|
4 |
|
if ($this->enumeration !== $other->enumeration) { |
356
|
2 |
|
throw new InvalidArgumentException(sprintf( |
357
|
2 |
|
'Others should be of the same enumeration as this %s', |
358
|
2 |
|
$this->enumeration |
359
|
1 |
|
)); |
360
|
|
|
} |
361
|
|
|
|
362
|
2 |
|
$bitset |= $other->bitset; |
363
|
1 |
|
} |
364
|
|
|
|
365
|
2 |
|
$clone = clone $this; |
366
|
2 |
|
$clone->bitset = $bitset; |
367
|
2 |
|
return $clone; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* Produce a new set with enumerators common to both this and other (this & other) |
372
|
|
|
* @param EnumSet ...$others Other EnumSet(s) of the same enumeration to produce the union |
373
|
|
|
* @return EnumSet |
374
|
|
|
*/ |
|
|
|
|
375
|
4 |
View Code Duplication |
public function intersect(EnumSet ...$others) |
|
|
|
|
376
|
|
|
{ |
377
|
4 |
|
$bitset = $this->bitset; |
378
|
4 |
|
foreach ($others as $other) { |
379
|
4 |
|
if ($this->enumeration !== $other->enumeration) { |
380
|
2 |
|
throw new InvalidArgumentException(sprintf( |
381
|
2 |
|
'Others should be of the same enumeration as this %s', |
382
|
2 |
|
$this->enumeration |
383
|
1 |
|
)); |
384
|
|
|
} |
385
|
|
|
|
386
|
2 |
|
$bitset &= $other->bitset; |
387
|
1 |
|
} |
388
|
|
|
|
389
|
2 |
|
$clone = clone $this; |
390
|
2 |
|
$clone->bitset = $bitset; |
391
|
2 |
|
return $clone; |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
/** |
395
|
|
|
* Produce a new set with enumerators in this but not in other (this - other) |
396
|
|
|
* @param EnumSet ...$others Other EnumSet(s) of the same enumeration to produce the union |
397
|
|
|
* @return EnumSet |
398
|
|
|
*/ |
|
|
|
|
399
|
4 |
View Code Duplication |
public function diff(EnumSet ...$others) |
|
|
|
|
400
|
|
|
{ |
401
|
4 |
|
$clone = clone $this; |
402
|
|
|
|
403
|
4 |
|
if (isset($others[0])) { |
404
|
4 |
|
$bitset = $others[0]->bitset; |
405
|
4 |
|
foreach ($others as $other) { |
406
|
4 |
|
if ($this->enumeration !== $other->enumeration) { |
407
|
2 |
|
throw new InvalidArgumentException(sprintf( |
408
|
2 |
|
'Others should of the same enumeration as this %s', |
409
|
2 |
|
$this->enumeration |
410
|
1 |
|
)); |
411
|
|
|
} |
412
|
|
|
|
413
|
2 |
|
$bitset |= $other->bitset; |
414
|
1 |
|
} |
415
|
|
|
|
416
|
2 |
|
$clone->bitset = $this->bitset & ~$bitset; |
417
|
1 |
|
} |
418
|
|
|
|
419
|
2 |
|
return $clone; |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* Produce a new set with enumerators in either this and other but not in both (this ^ (other | other)) |
424
|
|
|
* @param EnumSet ...$others Other EnumSet(s) of the same enumeration to produce the union |
425
|
|
|
* @return EnumSet |
426
|
|
|
*/ |
|
|
|
|
427
|
4 |
View Code Duplication |
public function symDiff(EnumSet ...$others) |
|
|
|
|
428
|
|
|
{ |
429
|
4 |
|
$clone = clone $this; |
430
|
|
|
|
431
|
4 |
|
if (isset($others[0])) { |
432
|
4 |
|
$bitset = $others[0]->bitset; |
433
|
4 |
|
foreach ($others as $other) { |
434
|
4 |
|
if ($this->enumeration !== $other->enumeration) { |
435
|
2 |
|
throw new InvalidArgumentException(sprintf( |
436
|
2 |
|
'Others should be of the same enumeration as this %s', |
437
|
2 |
|
$this->enumeration |
438
|
1 |
|
)); |
439
|
|
|
} |
440
|
|
|
|
441
|
2 |
|
$bitset |= $other->bitset; |
442
|
1 |
|
} |
443
|
|
|
|
444
|
2 |
|
$clone->bitset = $this->bitset ^ $bitset; |
445
|
1 |
|
} |
446
|
|
|
|
447
|
2 |
|
return $clone; |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Get ordinal numbers of the defined enumerators as array |
452
|
|
|
* @return int[] |
453
|
|
|
*/ |
454
|
24 |
|
public function getOrdinals() |
455
|
|
|
{ |
456
|
24 |
|
return $this->{$this->fnDoGetOrdinals}(); |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
/** |
460
|
|
|
* Get ordinal numbers of the defined enumerators as array. |
461
|
|
|
* |
462
|
|
|
* This is the binary bitset implementation. |
463
|
|
|
* |
464
|
|
|
* @return int[] |
465
|
|
|
* @see getOrdinals |
466
|
|
|
* @see goGetOrdinalsInt |
467
|
|
|
*/ |
468
|
|
|
private function doGetOrdinalsBin() |
469
|
|
|
{ |
470
|
|
|
$ordinals = []; |
471
|
|
|
$byteLen = strlen($this->bitset); |
472
|
|
|
for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) { |
473
|
|
|
if ($this->bitset[$bytePos] === "\0") { |
474
|
|
|
// fast skip null byte |
475
|
|
|
continue; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
$ord = ord($this->bitset[$bytePos]); |
479
|
|
|
for ($bitPos = 0; $bitPos < 8; ++$bitPos) { |
480
|
|
|
if ($ord & (1 << $bitPos)) { |
481
|
|
|
$ordinals[] = $bytePos * 8 + $bitPos; |
482
|
|
|
} |
483
|
|
|
} |
484
|
|
|
} |
485
|
|
|
return $ordinals; |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
/** |
489
|
|
|
* Get ordinal numbers of the defined enumerators as array. |
490
|
|
|
* |
491
|
|
|
* This is the integer bitset implementation. |
492
|
|
|
* |
493
|
|
|
* @return int[] |
494
|
|
|
* @see getOrdinals |
495
|
|
|
* @see doGetOrdinalsBin |
496
|
|
|
*/ |
497
|
24 |
|
private function doGetOrdinalsInt() |
498
|
|
|
{ |
499
|
24 |
|
$ordinals = []; |
500
|
24 |
|
for ($ord = 0; $ord < $this->ordinalMax; ++$ord) { |
501
|
24 |
|
if ($this->bitset & (1 << $ord)) { |
502
|
24 |
|
$ordinals[] = $ord; |
503
|
12 |
|
} |
504
|
12 |
|
} |
505
|
24 |
|
return $ordinals; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Get values of the defined enumerators as array |
510
|
|
|
* @return null[]|bool[]|int[]|float[]|string[] |
511
|
|
|
*/ |
512
|
12 |
|
public function getValues() |
513
|
|
|
{ |
514
|
12 |
|
$enumeration = $this->enumeration; |
515
|
12 |
|
$values = []; |
516
|
12 |
|
foreach ($this->getOrdinals() as $ord) { |
517
|
12 |
|
$values[] = $enumeration::byOrdinal($ord)->getValue(); |
518
|
6 |
|
} |
519
|
12 |
|
return $values; |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
/** |
523
|
|
|
* Get names of the defined enumerators as array |
524
|
|
|
* @return string[] |
525
|
|
|
*/ |
526
|
4 |
|
public function getNames() |
527
|
|
|
{ |
528
|
4 |
|
$enumeration = $this->enumeration; |
529
|
4 |
|
$names = []; |
530
|
4 |
|
foreach ($this->getOrdinals() as $ord) { |
531
|
4 |
|
$names[] = $enumeration::byOrdinal($ord)->getName(); |
532
|
2 |
|
} |
533
|
4 |
|
return $names; |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
/** |
537
|
|
|
* Get the defined enumerators as array |
538
|
|
|
* @return Enum[] |
539
|
|
|
*/ |
540
|
4 |
|
public function getEnumerators() |
541
|
|
|
{ |
542
|
4 |
|
$enumeration = $this->enumeration; |
543
|
4 |
|
$enumerators = []; |
544
|
4 |
|
foreach ($this->getOrdinals() as $ord) { |
545
|
4 |
|
$enumerators[] = $enumeration::byOrdinal($ord); |
546
|
2 |
|
} |
547
|
4 |
|
return $enumerators; |
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
/** |
551
|
|
|
* Get binary bitset in little-endian order |
552
|
|
|
* |
553
|
|
|
* @return string |
554
|
|
|
*/ |
555
|
8 |
|
public function getBinaryBitsetLe() |
556
|
|
|
{ |
557
|
8 |
|
return $this->{$this->fnDoGetBinaryBitsetLe}(); |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Get binary bitset in little-endian order. |
562
|
|
|
* |
563
|
|
|
* This is the binary bitset implementation. |
564
|
|
|
* |
565
|
|
|
* @return string |
|
|
|
|
566
|
|
|
*/ |
567
|
6 |
|
private function doGetBinaryBitsetLeBin() |
568
|
|
|
{ |
569
|
6 |
|
return $this->bitset; |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
/** |
573
|
|
|
* Get binary bitset in little-endian order. |
574
|
|
|
* |
575
|
|
|
* This is the integer bitset implementation. |
576
|
|
|
* |
577
|
|
|
* @return string |
578
|
|
|
*/ |
579
|
2 |
|
private function doGetBinaryBitsetLeInt() |
580
|
|
|
{ |
581
|
2 |
|
$bin = pack(PHP_INT_SIZE === 8 ? 'P' : 'V', $this->bitset); |
582
|
2 |
|
return substr($bin, 0, ceil($this->ordinalMax / 8)); |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
/** |
586
|
|
|
* Set binary bitset in little-endian order |
587
|
|
|
* |
588
|
|
|
* NOTE: It resets the current position of the iterator |
589
|
|
|
* |
590
|
|
|
* @param string $bitset |
591
|
|
|
* @return void |
592
|
|
|
* @throws InvalidArgumentException On a non string is given as Parameter |
593
|
|
|
*/ |
594
|
16 |
|
public function setBinaryBitsetLe($bitset) |
595
|
|
|
{ |
596
|
16 |
|
if (!is_string($bitset)) { |
597
|
2 |
|
throw new InvalidArgumentException('Bitset must be a string'); |
598
|
|
|
} |
599
|
|
|
|
600
|
14 |
|
$this->{$this->fnDoSetBinaryBitsetLe}($bitset); |
601
|
|
|
|
602
|
|
|
// reset the iterator position |
603
|
8 |
|
$this->rewind(); |
604
|
8 |
|
} |
605
|
|
|
|
606
|
|
|
/** |
607
|
|
|
* Set binary bitset in little-endian order |
608
|
|
|
* |
609
|
|
|
* NOTE: It resets the current position of the iterator |
610
|
|
|
* |
611
|
|
|
* @param string $bitset |
612
|
|
|
* @return void |
613
|
|
|
* @throws InvalidArgumentException On a non string is given as Parameter |
614
|
|
|
*/ |
615
|
8 |
|
private function doSetBinaryBitsetLeBin($bitset) |
616
|
|
|
{ |
617
|
8 |
|
$size = strlen($this->bitset); |
618
|
8 |
|
$sizeIn = strlen($bitset); |
619
|
|
|
|
620
|
8 |
|
if ($sizeIn < $size) { |
621
|
|
|
// add "\0" if the given bitset is not long enough |
622
|
2 |
|
$bitset .= str_repeat("\0", $size - $sizeIn); |
623
|
7 |
|
} elseif ($sizeIn > $size) { |
624
|
|
|
if (trim(substr($bitset, $size), "\0") !== '') { |
625
|
|
|
throw new InvalidArgumentException('Out-Of-Range bits detected'); |
626
|
|
|
} |
627
|
|
|
$bitset = substr($bitset, 0, $size); |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
// truncate out-of-range bits of last byte |
631
|
8 |
|
$lastByteMaxOrd = $this->ordinalMax % 8; |
632
|
8 |
|
if ($lastByteMaxOrd !== 0) { |
633
|
8 |
|
$lastByte = $bitset[$size - 1]; |
634
|
8 |
|
$lastByteExpected = chr((1 << $lastByteMaxOrd) - 1) & $lastByte; |
635
|
8 |
|
if ($lastByte !== $lastByteExpected) { |
636
|
2 |
|
throw new InvalidArgumentException('Out-Of-Range bits detected'); |
637
|
|
|
} |
638
|
|
|
|
639
|
6 |
|
$this->bitset = substr($bitset, 0, -1) . $lastByteExpected; |
640
|
3 |
|
} |
641
|
|
|
|
642
|
6 |
|
$this->bitset = $bitset; |
643
|
6 |
|
} |
644
|
|
|
|
645
|
|
|
/** |
646
|
|
|
* Set binary bitset in little-endian order |
647
|
|
|
* |
648
|
|
|
* NOTE: It resets the current position of the iterator |
649
|
|
|
* |
650
|
|
|
* @param string $bitset |
651
|
|
|
* @return void |
652
|
|
|
* @throws InvalidArgumentException On a non string is given as Parameter |
653
|
|
|
*/ |
654
|
6 |
|
private function doSetBinaryBitsetLeInt($bitset) |
655
|
|
|
{ |
656
|
6 |
|
$len = strlen($bitset); |
657
|
6 |
|
$int = 0; |
658
|
6 |
|
for ($i = 0; $i < $len; ++$i) { |
659
|
6 |
|
$ord = ord($bitset[$i]); |
660
|
|
|
|
661
|
6 |
|
if ($ord && $i > PHP_INT_SIZE) { |
662
|
|
|
throw new InvalidArgumentException('Out-Of-Range bits detected'); |
663
|
|
|
} |
664
|
|
|
|
665
|
6 |
|
$int |= $ord << (8 * $i); |
666
|
3 |
|
} |
667
|
|
|
|
668
|
6 |
|
if ($int & (~0 << $this->ordinalMax)) { |
669
|
4 |
|
throw new InvalidArgumentException('Out-Of-Range bits detected'); |
670
|
|
|
} |
671
|
|
|
|
672
|
2 |
|
$this->bitset = $int; |
673
|
2 |
|
} |
674
|
|
|
|
675
|
|
|
/** |
676
|
|
|
* Get binary bitset in big-endian order |
677
|
|
|
* |
678
|
|
|
* @return string |
679
|
|
|
*/ |
680
|
2 |
|
public function getBinaryBitsetBe() |
681
|
|
|
{ |
682
|
2 |
|
return strrev($this->bitset); |
683
|
|
|
} |
684
|
|
|
|
685
|
|
|
/** |
686
|
|
|
* Set binary bitset in big-endian order |
687
|
|
|
* |
688
|
|
|
* NOTE: It resets the current position of the iterator |
689
|
|
|
* |
690
|
|
|
* @param string $bitset |
691
|
|
|
* @return void |
692
|
|
|
* @throws InvalidArgumentException On a non string is given as Parameter |
693
|
|
|
*/ |
694
|
4 |
|
public function setBinaryBitsetBe($bitset) |
695
|
|
|
{ |
696
|
4 |
|
if (!is_string($bitset)) { |
697
|
2 |
|
throw new InvalidArgumentException('Bitset must be a string'); |
698
|
|
|
} |
699
|
2 |
|
$this->setBinaryBitsetLe(strrev($bitset)); |
700
|
2 |
|
} |
701
|
|
|
|
702
|
|
|
/** |
703
|
|
|
* Get a bit at the given ordinal number |
704
|
|
|
* |
705
|
|
|
* @param int $ordinal Ordinal number of bit to get |
706
|
|
|
* @return boolean |
707
|
|
|
*/ |
708
|
|
|
public function getBit($ordinal) |
709
|
|
|
{ |
710
|
|
|
if ($ordinal < 0 || $ordinal > $this->ordinalMax) { |
711
|
|
|
throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->ordinalMax}"); |
|
|
|
|
712
|
|
|
} |
713
|
|
|
|
714
|
|
|
return $this->{$this->fnDoGetBit}($ordinal); |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
/** |
718
|
|
|
* Get a bit at the given ordinal number. |
719
|
|
|
* |
720
|
|
|
* This is the binary bitset implementation. |
721
|
|
|
* |
722
|
|
|
* @param int $ordinal Ordinal number of bit to get |
723
|
|
|
* @return boolean |
724
|
|
|
* @see getBit |
725
|
|
|
* @see doGetBitInt |
726
|
|
|
*/ |
727
|
12 |
|
private function doGetBitBin($ordinal) |
728
|
|
|
{ |
729
|
12 |
|
return (ord($this->bitset[(int) ($ordinal / 8)]) & 1 << ($ordinal % 8)) !== 0; |
730
|
|
|
} |
731
|
|
|
|
732
|
|
|
/** |
733
|
|
|
* Get a bit at the given ordinal number. |
734
|
|
|
* |
735
|
|
|
* This is the integer bitset implementation. |
736
|
|
|
* |
737
|
|
|
* @param int $ordinal Ordinal number of bit to get |
738
|
|
|
* @return boolean |
739
|
|
|
* @see getBit |
740
|
|
|
* @see doGetBitBin |
741
|
|
|
*/ |
742
|
26 |
|
private function doGetBitInt($ordinal) |
743
|
|
|
{ |
744
|
26 |
|
return (bool)($this->bitset & (1 << $ordinal)); |
745
|
|
|
} |
746
|
|
|
|
747
|
|
|
/** |
748
|
|
|
* Set a bit at the given ordinal number |
749
|
|
|
* |
750
|
|
|
* @param int $ordinal Ordnal number of bit to set |
751
|
|
|
* @param bool $bit The bit to set |
752
|
|
|
* @return void |
753
|
|
|
* @see doSetBitBin |
754
|
|
|
* @see doSetBitInt |
755
|
|
|
* @see doUnsetBin |
756
|
|
|
* @see doUnsetInt |
757
|
|
|
*/ |
758
|
|
|
public function setBit($ordinal, $bit) |
759
|
|
|
{ |
760
|
|
|
if ($ordinal < 0 || $ordinal > $this->ordinalMax) { |
761
|
|
|
throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->ordinalMax}"); |
|
|
|
|
762
|
|
|
} |
763
|
|
|
|
764
|
|
|
if ($bit) { |
765
|
|
|
$this->{$this->fnDoSetBit}($ordinal); |
766
|
|
|
} else { |
767
|
|
|
$this->{$this->fnDoUnsetBit}($ordinal); |
768
|
|
|
} |
769
|
|
|
} |
770
|
|
|
|
771
|
|
|
/** |
772
|
|
|
* Set a bit at the given ordinal number. |
773
|
|
|
* |
774
|
|
|
* This is the binary bitset implementation. |
775
|
|
|
* |
776
|
|
|
* @param int $ordinal Ordnal number of bit to set |
777
|
|
|
* @return void |
778
|
|
|
* @see setBit |
779
|
|
|
* @see doSetBitInt |
780
|
|
|
*/ |
781
|
6 |
|
private function doSetBitBin($ordinal) |
782
|
|
|
{ |
783
|
6 |
|
$byte = (int) ($ordinal / 8); |
784
|
6 |
|
$this->bitset[$byte] = $this->bitset[$byte] | chr(1 << ($ordinal % 8)); |
785
|
6 |
|
} |
786
|
|
|
|
787
|
|
|
/** |
788
|
|
|
* Set a bit at the given ordinal number. |
789
|
|
|
* |
790
|
|
|
* This is the binary bitset implementation. |
791
|
|
|
* |
792
|
|
|
* @param int $ordinal Ordnal number of bit to set |
793
|
|
|
* @return void |
794
|
|
|
* @see setBit |
795
|
|
|
* @see doSetBitBin |
796
|
|
|
*/ |
797
|
62 |
|
private function doSetBitInt($ordinal) |
798
|
|
|
{ |
799
|
62 |
|
$this->bitset = $this->bitset | (1 << $ordinal); |
800
|
62 |
|
} |
801
|
|
|
|
802
|
|
|
/** |
803
|
|
|
* Unset a bit at the given ordinal number. |
804
|
|
|
* |
805
|
|
|
* This is the binary bitset implementation. |
806
|
|
|
* |
807
|
|
|
* @param int $ordinal Ordinal number of bit to unset |
808
|
|
|
* @return void |
809
|
|
|
* @see setBit |
810
|
|
|
* @see doUnsetBitInt |
811
|
|
|
*/ |
812
|
4 |
|
private function doUnsetBitBin($ordinal) |
813
|
|
|
{ |
814
|
4 |
|
$byte = (int) ($ordinal / 8); |
815
|
4 |
|
$this->bitset[$byte] = $this->bitset[$byte] & chr(~(1 << ($ordinal % 8))); |
816
|
4 |
|
} |
817
|
|
|
|
818
|
|
|
/** |
819
|
|
|
* Unset a bit at the given ordinal number. |
820
|
|
|
* |
821
|
|
|
* This is the integer bitset implementation. |
822
|
|
|
* |
823
|
|
|
* @param int $ordinal Ordinal number of bit to unset |
824
|
|
|
* @return void |
825
|
|
|
* @see setBit |
826
|
|
|
* @see doUnsetBitBin |
827
|
|
|
*/ |
828
|
8 |
|
private function doUnsetBitInt($ordinal) |
829
|
|
|
{ |
830
|
8 |
|
$this->bitset = $this->bitset & ~(1 << $ordinal); |
831
|
8 |
|
} |
832
|
|
|
} |
833
|
|
|
|
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.