Passed
Push — immutable-set ( 4ef4b4...fb566a )
by Marc
01:49
created

EnumSet::withUnion()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.2109

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 1
dl 0
loc 12
ccs 5
cts 8
cp 0.625
crap 2.2109
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MabeEnum;
6
7
use Countable;
8
use InvalidArgumentException;
9
use Iterator;
10
use IteratorAggregate;
11
use OutOfBoundsException;
12
13
/**
14
 * A set of enumerators of the given enumeration (EnumSet<T>)
15
 * based on an integer or binary bitset depending of given enumeration size.
16
 *
17
 * @copyright 2019 Marc Bennewitz
18
 * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
19
 * @link http://github.com/marc-mabe/php-enum for the canonical source repository
20
 */
3 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
21
class EnumSet implements IteratorAggregate, Countable
22
{
23
    /**
24
     * The classname of the Enumeration
25
     * @var string
26
     */
27
    private $enumeration;
1 ignored issue
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
Coding Style introduced by
Private member variable "enumeration" must contain a leading underscore
Loading history...
28
29
    /**
30
     * Number of enumerators defined in the enumeration
31
     * @var int
32
     */
33
    private $enumerationCount;
1 ignored issue
show
Coding Style introduced by
Private member variable "enumerationCount" must contain a leading underscore
Loading history...
34
35
    /**
36
     * Integer or binary (little endian) bitset
37
     * @var int|string
38
     */
39
    private $bitset = 0;
1 ignored issue
show
Coding Style introduced by
Private member variable "bitset" must contain a leading underscore
Loading history...
40
41
    /**#@+
42
     * Defines private method names to be called depended of how the bitset type was set too.
43
     * ... Integer or binary bitset.
44
     * ... *Int or *Bin method
45
     * 
46
     * @var string
47
     */
48
    private $fnDoGetIterator       = 'doGetIteratorInt';
1 ignored issue
show
Coding Style introduced by
Private member variable "fnDoGetIterator" must contain a leading underscore
Loading history...
49
    private $fnDoCount             = 'doCountInt';
1 ignored issue
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
Coding Style introduced by
Private member variable "fnDoCount" must contain a leading underscore
Loading history...
50
    private $fnDoGetOrdinals       = 'doGetOrdinalsInt';
1 ignored issue
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
Coding Style introduced by
Private member variable "fnDoGetOrdinals" must contain a leading underscore
Loading history...
51
    private $fnDoGetBit            = 'doGetBitInt';
1 ignored issue
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
Coding Style introduced by
Private member variable "fnDoGetBit" must contain a leading underscore
Loading history...
52
    private $fnDoSetBit            = 'doSetBitInt';
1 ignored issue
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
Coding Style introduced by
Private member variable "fnDoSetBit" must contain a leading underscore
Loading history...
53
    private $fnDoUnsetBit          = 'doUnsetBitInt';
1 ignored issue
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
Coding Style introduced by
Private member variable "fnDoUnsetBit" must contain a leading underscore
Loading history...
54
    private $fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeInt';
1 ignored issue
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
Coding Style introduced by
Private member variable "fnDoGetBinaryBitsetLe" must contain a leading underscore
Loading history...
55
    private $fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeInt';
1 ignored issue
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
Coding Style introduced by
Private member variable "fnDoSetBinaryBitsetLe" must contain a leading underscore
Loading history...
56
    /**#@-*/
57
58
    /**
59
     * Constructor
60
     *
61
     * @param string $enumeration The classname of the enumeration
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
Coding Style introduced by
Expected 8 spaces after parameter type; 1 found
Loading history...
62
     * @param iterable|null $enumerators iterable list of enumerators initializing the set
1 ignored issue
show
introduced by
Parameter comment must start with a capital letter
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
63
     * @throws InvalidArgumentException
0 ignored issues
show
introduced by
Comment missing for @throws tag in function comment
Loading history...
64
     */
65 71
    public function __construct(string $enumeration, iterable $enumerators = null)
2 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$enumerators" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$enumerators"; expected 0 but found 1
Loading history...
66
    {
67 71
        if (!\is_subclass_of($enumeration, Enum::class)) {
68 1
            throw new InvalidArgumentException(\sprintf(
69 1
                '%s can handle subclasses of %s only',
70 1
                __METHOD__,
71 1
                Enum::class
72
            ));
73
        }
74
75 70
        $this->enumeration      = $enumeration;
76 70
        $this->enumerationCount = \count($enumeration::getConstants());
77
78
        // By default the bitset is initialized as integer bitset
79
        // in case the enumeraton has more enumerators then integer bits
80
        // we will switch this into a binary bitset
81 70
        if ($this->enumerationCount > \PHP_INT_SIZE * 8) {
82
            // init binary bitset with zeros
83 15
            $this->bitset = \str_repeat("\0", (int)\ceil($this->enumerationCount / 8));
84
85
            // switch internal binary bitset functions
86 15
            $this->fnDoGetIterator       = 'doGetIteratorBin';
87 15
            $this->fnDoCount             = 'doCountBin';
88 15
            $this->fnDoGetOrdinals       = 'doGetOrdinalsBin';
89 15
            $this->fnDoGetBit            = 'doGetBitBin';
90 15
            $this->fnDoSetBit            = 'doSetBitBin';
91 15
            $this->fnDoUnsetBit          = 'doUnsetBitBin';
92 15
            $this->fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeBin';
93 15
            $this->fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeBin';
94
        }
95
96 70
        if ($enumerators !== null) {
97
            foreach ($enumerators as $enumerator) {
98
                $this->{$this->fnDoSetBit}($enumeration::get($enumerator)->getOrdinal());
99
            }
100
        }
101 70
    }
102
103
    /**
104
     * Get the classname of the enumeration
105
     * @return string
106
     */
107 2
    public function getEnumeration(): string
108
    {
109 2
        return $this->enumeration;
110
    }
111
112
    /**
113
     * Attach an enumerator object or value
114
     * @param Enum|null|bool|int|float|string|array $enumerator Enumerator object or value
2 ignored issues
show
Coding Style introduced by
Expected "Enum|null|boolean|integer|float|string|array" but found "Enum|null|bool|int|float|string|array" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
115
     * @return void
116
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
117
     */
118 6
    public function attachEnumerator($enumerator): void
119
    {
120 6
        $this->{$this->fnDoSetBit}(($this->enumeration)::get($enumerator)->getOrdinal());
121 6
    }
122
123
    /**
124
     * Creates a new set with the given enumerator object or value attached
125
     * @param Enum|null|bool|int|float|string|array $enumerator Enumerator object or value
2 ignored issues
show
Coding Style introduced by
Expected "Enum|null|boolean|integer|float|string|array" but found "Enum|null|bool|int|float|string|array" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
126
     * @return static
127
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
128
     */
129 41
    public function withEnumerator($enumerator): self
130
    {
131 41
        $clone = clone $this;
132 41
        $clone->{$this->fnDoSetBit}(($this->enumeration)::get($enumerator)->getOrdinal());
133 41
        return $clone;
134
    }
135
136
    /**
137
     * Attach all enumerator objects or values of the given iterable
138
     * @param iterable $enumerators Iterable list of enumerator objects or values
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
139
     * @return void
140
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
141
     */
142
    public function attachEnumerators($enumerators): void
0 ignored issues
show
introduced by
Type hint "iterable" missing for $enumerators
Loading history...
143
    {
144
        foreach ($enumerators as $enumerator) {
145
            $this->{$this->fnDoSetBit}(($this->enumeration)::get($enumerator)->getOrdinal());
146
        }
147
    }
148
149
    /**
150
     * Creates a new set with the given enumeration objects or values attached
151
     * @param iterable $enumerators Iterable list of enumerator objects or values
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
152
     * @return static
153
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
154
     */
155
    public function withEnumerators(iterable $enumerators): self
156
    {
157
        $clone = clone $this;
158
        foreach ($enumerators as $enumerator) {
159
            $clone->{$this->fnDoSetBit}(($this->enumeration)::get($enumerator)->getOrdinal());
160
        }
161
        return $clone;
162
    }
163
164
    /**
165
     * Detach the given enumerator object or value
166
     * @param Enum|null|bool|int|float|string|array $enumerator Enumerator object or value
2 ignored issues
show
Coding Style introduced by
Expected "Enum|null|boolean|integer|float|string|array" but found "Enum|null|bool|int|float|string|array" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
167
     * @return void
168
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
169
     */
170 2
    public function detachEnumerator($enumerator): void
171
    {
172 2
        $this->{$this->fnDoUnsetBit}(($this->enumeration)::get($enumerator)->getOrdinal());
173 2
    }
174
175
    /**
176
     * Create a new set with the given enumerator object or value detached
177
     * @param Enum|null|bool|int|float|string|array $enumerator Enumerator object or value
2 ignored issues
show
Coding Style introduced by
Expected "Enum|null|boolean|integer|float|string|array" but found "Enum|null|bool|int|float|string|array" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
178
     * @return static
179
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
180
     */
181 4
    public function withoutEnumerator($enumerator): self
182
    {
183 4
        $clone = clone $this;
184 4
        $clone->{$this->fnDoUnsetBit}(($this->enumeration)::get($enumerator)->getOrdinal());
185 4
        return $clone;
186
    }
187
188
    /**
189
     * Detach all enumerator objects or values of the given iterable
190
     * @param iterable $enumerators Iterable list of enumerator objects or values
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
191
     * @return void
192
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
193
     */
194
    public function detachEnumerators(iterable $enumerators): void
195
    {
196
        foreach ($enumerators as $enumerator) {
197
            $this->{$this->fnDoUnsetBit}(($this->enumeration)::get($enumerator)->getOrdinal());
198
        }
199
    }
200
201
    /**
202
     * Creates a new set with the given enumeration objects or values detached
203
     * @param iterable $enumerators Iterable list of enumerator objects or values
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
204
     * @return static
205
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
206
     */
207
    public function withoutEnumerators(iterable $enumerators): self
208
    {
209
        $clone = clone $this;
210
        foreach ($enumerators as $enumerator) {
211
            $clone->{$this->fnDoUnsetBit}(($this->enumeration)::get($enumerator)->getOrdinal());
212
        }
213
        return $clone;
214
    }
215
216
    /**
217
     * Test if the given enumerator exists
218
     * @param Enum|null|bool|int|float|string|array $enumerator
1 ignored issue
show
Documentation introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected "Enum|null|boolean|integer|float|string|array" but found "Enum|null|bool|int|float|string|array" for parameter type
Loading history...
219
     * @return bool
1 ignored issue
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
220
     */
221 13
    public function contains($enumerator): bool
222
    {
223 13
        return $this->{$this->fnDoGetBit}(($this->enumeration)::get($enumerator)->getOrdinal());
224
    }
225
226
    /* IteratorAggregate */
227
228
    /**
229
     * Get a new iterator
230
     * @return Iterator
231
     * @uses doGetIteratorInt()
232
     * @uses doGetIteratorBin()
233
     */
234 17
    public function getIterator(): Iterator
235
    {
236 17
        return $this->{$this->fnDoGetIterator}();
237
    }
238
239
    /**
240
     * Get a new Iterator.
241
     *
242
     * This is the binary bitset implementation.
243
     *
244
     * @return Iterator
245
     * @see getIterator()
246
     * @see goGetIteratorInt()
247
     */
248 5
    private function doGetIteratorBin()
249
    {
250 5
        $bitset   = $this->bitset;
251 5
        $byteLen  = \strlen($bitset);
252 5
        for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) {
253 5
            if ($bitset[$bytePos] === "\0") {
254
                // fast skip null byte
255 3
                continue;
256
            }
257
258 5
            $ord = \ord($bitset[$bytePos]);
259 5
            for ($bitPos = 0; $bitPos < 8; ++$bitPos) {
260 5
                if ($ord & (1 << $bitPos)) {
261 5
                    $ordinal = $bytePos * 8 + $bitPos;
262 5
                    yield $ordinal => ($this->enumeration)::byOrdinal($ordinal);
263
                }
264
            }
265
        }
266 5
    }
267
268
    /**
269
     * Get a new Iterator.
270
     *
271
     * This is the integer bitset implementation.
272
     *
273
     * @return Iterator
274
     * @see getIterator()
275
     * @see doGetIteratorBin()
276
     */
277 12
    private function doGetIteratorInt()
278
    {
279 12
        $count  = $this->enumerationCount;
280 12
        $bitset = $this->bitset;
281 12
        for ($ordinal = 0; $ordinal < $count; ++$ordinal) {
282 12
            if ($bitset & (1 << $ordinal)) {
283 10
                yield $ordinal => ($this->enumeration)::byOrdinal($ordinal);
284
            }
285
        }
286 10
    }
287
288
    /* Countable */
289
290
    /**
291
     * Count the number of elements
292
     *
293
     * @return int
1 ignored issue
show
Coding Style introduced by
Expected "integer" but found "int" for function return type
Loading history...
294
     * @uses doCountBin()
295
     * @uses doCountInt()
296
     */
297 15
    public function count(): int
298
    {
299 15
        return $this->{$this->fnDoCount}();
300
    }
301
302
    /**
303
     * Count the number of elements.
304
     *
305
     * This is the binary bitset implementation.
306
     *
307
     * @return int
1 ignored issue
show
Coding Style introduced by
Expected "integer" but found "int" for function return type
Loading history...
308
     * @see count()
309
     * @see doCountInt()
310
     */
311 7
    private function doCountBin()
312
    {
313 7
        $count   = 0;
314 7
        $bitset  = $this->bitset;
315 7
        $byteLen = \strlen($bitset);
316 7
        for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) {
317 7
            if ($bitset[$bytePos] === "\0") {
318
                // fast skip null byte
319 5
                continue;
320
            }
321
322 7
            $ord = \ord($bitset[$bytePos]);
323 7
            if ($ord & 0b00000001) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
324 7
            if ($ord & 0b00000010) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
325 7
            if ($ord & 0b00000100) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
326 7
            if ($ord & 0b00001000) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
327 7
            if ($ord & 0b00010000) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
328 7
            if ($ord & 0b00100000) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
329 7
            if ($ord & 0b01000000) ++$count;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
330 7
            if ($ord & 0b10000000) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
331
        }
332 7
        return $count;
333
    }
334
335
    /**
336
     * Count the number of elements.
337
     *
338
     * This is the integer bitset implementation.
339
     *
340
     * @return int
1 ignored issue
show
Coding Style introduced by
Expected "integer" but found "int" for function return type
Loading history...
341
     * @see count()
342
     * @see doCountBin()
343
     */
344 8
    private function doCountInt()
345
    {
346 8
        $count  = 0;
347 8
        $bitset = $this->bitset;
348
349
        // PHP does not support right shift unsigned
350 8
        if ($bitset < 0) {
351 2
            $count  = 1;
352 2
            $bitset = $bitset & \PHP_INT_MAX;
353
        }
354
355
        // iterate byte by byte and count set bits
356 8
        $phpIntBitSize = \PHP_INT_SIZE * 8;
357 8
        for ($bitPos = 0; $bitPos < $phpIntBitSize; $bitPos += 8) {
358 8
            $bitChk = 0xff << $bitPos;
359 8
            $byte = $bitset & $bitChk;
360 8
            if ($byte) {
361 7
                $byte = $byte >> $bitPos;
362 7
                if ($byte & 0b00000001) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
363 7
                if ($byte & 0b00000010) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
364 7
                if ($byte & 0b00000100) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
365 7
                if ($byte & 0b00001000) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
366 7
                if ($byte & 0b00010000) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
367 7
                if ($byte & 0b00100000) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
368 7
                if ($byte & 0b01000000) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
369 7
                if ($byte & 0b10000000) ++$count;
1 ignored issue
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
370
            }
371
372 8
            if ($bitset <= $bitChk) {
373 7
                break;
374
            }
375
        }
376
377 8
        return $count;
378
    }
379
380
    /**
381
     * Check if this EnumSet is the same as other
382
     * @param EnumSet $other
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
383
     * @return bool
1 ignored issue
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
384
     */
385 3
    public function isEqual(EnumSet $other): bool
386
    {
387 3
        return $this->enumeration === $other->enumeration
388 3
            && $this->bitset === $other->bitset;
389
    }
390
391
    /**
392
     * Check if this EnumSet is a subset of other
393
     * @param EnumSet $other
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
394
     * @return bool
1 ignored issue
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
395
     */
396 4
    public function isSubset(EnumSet $other): bool
397
    {
398 4
        return $this->enumeration === $other->enumeration
399 4
            && ($this->bitset & $other->bitset) === $this->bitset;
400
    }
401
402
    /**
403
     * Check if this EnumSet is a superset of other
404
     * @param EnumSet $other
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
405
     * @return bool
1 ignored issue
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
406
     */
407 4
    public function isSuperset(EnumSet $other): bool
408
    {
409 4
        return $this->enumeration === $other->enumeration
410 4
            && ($this->bitset | $other->bitset) === $this->bitset;
411
    }
412
413
    /**
414
     * Modify this set from both this and other (this | other)
415
     *
416
     * @param EnumSet $other EnumSet of the same enumeration to produce the union
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
417
     * @return void
418
     * @throws InvalidArgumentException If $other doesn't match the enumeration
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
419
     */
420 2
    public function union(EnumSet $other): void
421
    {
422 2
        if ($this->enumeration !== $other->enumeration) {
423 1
            throw new InvalidArgumentException(\sprintf(
424 1
                'Other should be of the same enumeration as this %s',
425 1
                $this->enumeration
426
            ));
427
        }
428
429 1
        $this->bitset = $this->bitset | $other->bitset;
430 1
    }
431
432
    /**
433
     * Create a new set with enumerators from both this and other (this | other)
434
     *
435
     * @param EnumSet $other EnumSet of the same enumeration to produce the union
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
436
     * @return static
437
     * @throws InvalidArgumentException If $other doesn't match the enumeration
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
438
     */
439 1
    public function withUnion(EnumSet $other): self
440
    {
441 1
        if ($this->enumeration !== $other->enumeration) {
442
            throw new InvalidArgumentException(\sprintf(
443
                'Other should be of the same enumeration as this %s',
444
                $this->enumeration
445
            ));
446
        }
447
448 1
        $clone = clone $this;
449 1
        $clone->bitset = $this->bitset | $other->bitset;
450 1
        return $clone;
451
    }
452
453
    /**
454
     * Modify this set with enumerators common to both this and other (this & other)
455
     *
456
     * @param EnumSet $other EnumSet of the same enumeration to produce the intersect
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
457
     * @return void
458
     * @throws InvalidArgumentException If $other doesn't match the enumeration
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
459
     */
460 2
    public function intersect(EnumSet $other): void
461
    {
462 2
        if ($this->enumeration !== $other->enumeration) {
463 1
            throw new InvalidArgumentException(\sprintf(
464 1
                'Other should be of the same enumeration as this %s',
465 1
                $this->enumeration
466
            ));
467
        }
468
469 1
        $this->bitset = $this->bitset & $other->bitset;
470 1
    }
471
472
    /**
473
     * Create a new set with enumerators common to both this and other (this & other)
474
     *
475
     * @param EnumSet $other EnumSet of the same enumeration to produce the intersect
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
476
     * @return static
477
     * @throws InvalidArgumentException If $other doesn't match the enumeration
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
478
     */
479 1
    public function withIntersect(EnumSet $other): self
480
    {
481 1
        if ($this->enumeration !== $other->enumeration) {
482
            throw new InvalidArgumentException(\sprintf(
483
                'Other should be of the same enumeration as this %s',
484
                $this->enumeration
485
            ));
486
        }
487
488 1
        $clone = clone $this;
489 1
        $clone->bitset = $this->bitset & $other->bitset;
490 1
        return $clone;
491
    }
492
493
    /**
494
     * Modify this set with enumerators in this but not in other (this - other)
495
     *
496
     * @param EnumSet $other EnumSet of the same enumeration to produce the diff
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
497
     * @return void
498
     * @throws InvalidArgumentException If $other doesn't match the enumeration
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
499
     */
500 2
    public function diff(EnumSet $other): void
501
    {
502 2
        if ($this->enumeration !== $other->enumeration) {
503 1
            throw new InvalidArgumentException(\sprintf(
504 1
                'Other should be of the same enumeration as this %s',
505 1
                $this->enumeration
506
            ));
507
        }
508
509 1
        $this->bitset = $this->bitset & ~$other->bitset;
510 1
    }
511
512
    /**
513
     * Modify this set with enumerators in this but not in other (this - other)
514
     *
515
     * @param EnumSet $other EnumSet of the same enumeration to produce the diff
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
516
     * @return static
517
     * @throws InvalidArgumentException If $other doesn't match the enumeration
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
518
     */
519 1
    public function withDiff(EnumSet $other): self
520
    {
521 1
        if ($this->enumeration !== $other->enumeration) {
522
            throw new InvalidArgumentException(\sprintf(
523
                'Other should be of the same enumeration as this %s',
524
                $this->enumeration
525
            ));
526
        }
527
528 1
        $clone = clone $this;
529 1
        $clone->bitset = $this->bitset & ~$other->bitset;
530 1
        return $clone;
531
    }
532
533
    /**
534
     * Modify this set with enumerators in either this and other but not in both (this ^ other)
535
     *
536
     * @param EnumSet $other EnumSet of the same enumeration to produce the symmetric difference
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
537
     * @return void
538
     * @throws InvalidArgumentException If $other doesn't match the enumeration
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
539
     */
540 2
    public function symDiff(EnumSet $other): void
541
    {
542 2
        if ($this->enumeration !== $other->enumeration) {
543 1
            throw new InvalidArgumentException(\sprintf(
544 1
                'Other should be of the same enumeration as this %s',
545 1
                $this->enumeration
546
            ));
547
        }
548
549 1
        $this->bitset = $this->bitset ^ $other->bitset;
550 1
    }
551
552
    /**
553
     * Create a new set with enumerators in either this and other but not in both (this ^ other)
554
     *
555
     * @param EnumSet $other EnumSet of the same enumeration to produce the symmetric difference
1 ignored issue
show
introduced by
Parameter comment must end with a full stop
Loading history...
556
     * @return static
557
     * @throws InvalidArgumentException If $other doesn't match the enumeration
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
558
     */
559 1
    public function withSymDiff(EnumSet $other): self
560
    {
561 1
        if ($this->enumeration !== $other->enumeration) {
562
            throw new InvalidArgumentException(\sprintf(
563
                'Other should be of the same enumeration as this %s',
564
                $this->enumeration
565
            ));
566
        }
567
568 1
        $clone = clone $this;
569 1
        $clone->bitset = $this->bitset ^ $other->bitset;
570 1
        return $clone;
571
    }
572
573
    /**
574
     * Get ordinal numbers of the defined enumerators as array
575
     * @return int[]
576
     * @uses  doGetOrdinalsBin()
577
     * @uses  doGetOrdinalsInt()
578
     */
579 13
    public function getOrdinals(): array
580
    {
581 13
        return $this->{$this->fnDoGetOrdinals}();
582
    }
583
584
    /**
585
     * Get ordinal numbers of the defined enumerators as array.
586
     *
587
     * This is the binary bitset implementation.
588
     *
589
     * @return int[]
590
     * @see getOrdinals()
591
     * @see goGetOrdinalsInt()
592
     */
593 1
    private function doGetOrdinalsBin()
594
    {
595 1
        $ordinals = [];
596 1
        $bitset   = $this->bitset;
597 1
        $byteLen  = \strlen($bitset);
598 1
        for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) {
599 1
            if ($bitset[$bytePos] === "\0") {
600
                // fast skip null byte
601 1
                continue;
602
            }
603
604 1
            $ord = \ord($bitset[$bytePos]);
605 1
            for ($bitPos = 0; $bitPos < 8; ++$bitPos) {
606 1
                if ($ord & (1 << $bitPos)) {
607 1
                    $ordinals[] = $bytePos * 8 + $bitPos;
608
                }
609
            }
610
        }
611 1
        return $ordinals;
612
    }
613
614
    /**
615
     * Get ordinal numbers of the defined enumerators as array.
616
     *
617
     * This is the integer bitset implementation.
618
     *
619
     * @return int[]
620
     * @see getOrdinals()
621
     * @see doGetOrdinalsBin()
622
     */
623 12
    private function doGetOrdinalsInt()
624
    {
625 12
        $ordinals = [];
626 12
        $count    = $this->enumerationCount;
627 12
        $bitset   = $this->bitset;
628 12
        for ($ordinal = 0; $ordinal < $count; ++$ordinal) {
629 12
            if ($bitset & (1 << $ordinal)) {
630 12
                $ordinals[] = $ordinal;
631
            }
632
        }
633 12
        return $ordinals;
634
    }
635
636
    /**
637
     * Get values of the defined enumerators as array
638
     * @return mixed[]
639
     */
640 9
    public function getValues(): array
641
    {
642 9
        $enumeration = $this->enumeration;
643 9
        $values      = [];
644 9
        foreach ($this->getOrdinals() as $ord) {
645 9
            $values[] = $enumeration::byOrdinal($ord)->getValue();
646
        }
647 9
        return $values;
648
    }
649
650
    /**
651
     * Get names of the defined enumerators as array
652
     * @return string[]
653
     */
654 1
    public function getNames(): array
655
    {
656 1
        $enumeration = $this->enumeration;
657 1
        $names       = [];
658 1
        foreach ($this->getOrdinals() as $ord) {
659 1
            $names[] = $enumeration::byOrdinal($ord)->getName();
660
        }
661 1
        return $names;
662
    }
663
664
    /**
665
     * Get the defined enumerators as array
666
     * @return Enum[]
667
     */
668 1
    public function getEnumerators(): array
669
    {
670 1
        $enumeration = $this->enumeration;
671 1
        $enumerators = [];
672 1
        foreach ($this->getOrdinals() as $ord) {
673 1
            $enumerators[] = $enumeration::byOrdinal($ord);
674
        }
675 1
        return $enumerators;
676
    }
677
678
    /**
679
     * Get binary bitset in little-endian order
680
     * 
681
     * @return string
682
     * @uses doGetBinaryBitsetLeBin()
683
     * @uses doGetBinaryBitsetLeInt()
684
     */
685 6
    public function getBinaryBitsetLe(): string
686
    {
687 6
        return $this->{$this->fnDoGetBinaryBitsetLe}();
688
    }
689
690
    /**
691
     * Get binary bitset in little-endian order.
692
     *
693
     * This is the binary bitset implementation.
694
     *
695
     * @return string
696
     * @see getBinaryBitsetLe()
697
     * @see doGetBinaryBitsetLeInt()
698
     */
699 4
    private function doGetBinaryBitsetLeBin()
700
    {
701 4
        return $this->bitset;
702
    }
703
704
    /**
705
     * Get binary bitset in little-endian order.
706
     *
707
     * This is the integer bitset implementation.
708
     *
709
     * @return string
710
     * @see getBinaryBitsetLe()
711
     * @see doGetBinaryBitsetLeBin()
712
     */
713 2
    private function doGetBinaryBitsetLeInt()
714
    {
715 2
        $bin = \pack(\PHP_INT_SIZE === 8 ? 'P' : 'V', $this->bitset);
716 2
        return \substr($bin, 0, (int)\ceil($this->enumerationCount / 8));
717
    }
718
719
    /**
720
     * Set the given binary bitset in little-endian order
721
     *
722
     * @param string $bitset
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
723
     * @return void
724
     * @throws InvalidArgumentException On out-of-range bits given as input bitset
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
725
     * @uses doSetBinaryBitsetLeBin()
726
     * @uses doSetBinaryBitsetLeInt()
727
     */
728
    public function setBinaryBitsetLe(string $bitset): void
729
    {
730
        $this->{$this->fnDoSetBinaryBitsetLe}($bitset);
731
    }
732
733
    /**
734
     * Create a new set with the given binary bitset in little-endian order
735
     *
736
     * @param string $bitset
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
737
     * @return static
738
     * @throws InvalidArgumentException On out-of-range bits given as input bitset
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
739
     * @uses doSetBinaryBitsetLeBin()
740
     * @uses doSetBinaryBitsetLeInt()
741
     */
742 12
    public function withBinaryBitsetLe(string $bitset): self
743
    {
744 12
        $clone = clone $this;
745 12
        $clone->{$this->fnDoSetBinaryBitsetLe}($bitset);
746 6
        return $clone;
747
    }
748
749
    /**
750
     * Set binary bitset in little-endian order
751
     *
752
     * @param string $bitset
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
753
     * @return void
754
     * @throws InvalidArgumentException On out-of-range bits given as input bitset
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
755
     * @see setBinaryBitsetLeBin()
756
     * @see doSetBinaryBitsetLeInt()
757
     */
758 7
    private function doSetBinaryBitsetLeBin($bitset): void
0 ignored issues
show
Coding Style introduced by
Type hint "string" missing for $bitset
Loading history...
759
    {
760 7
        $size   = \strlen($this->bitset);
761 7
        $sizeIn = \strlen($bitset);
762
763 7
        if ($sizeIn < $size) {
764
            // add "\0" if the given bitset is not long enough
765 1
            $bitset .= \str_repeat("\0", $size - $sizeIn);
766 6
        } elseif ($sizeIn > $size) {
767 2
            if (\ltrim(\substr($bitset, $size), "\0") !== '') {
768 1
                throw new InvalidArgumentException('out-of-range bits detected');
769
            }
770 1
            $bitset = \substr($bitset, 0, $size);
771
        }
772
773
        // truncate out-of-range bits of last byte
774 6
        $lastByteMaxOrd = $this->enumerationCount % 8;
775 6
        if ($lastByteMaxOrd !== 0) {
776 6
            $lastByte         = $bitset[-1];
777 6
            $lastByteExpected = \chr((1 << $lastByteMaxOrd) - 1) & $lastByte;
778 6
            if ($lastByte !== $lastByteExpected) {
779 2
                throw new InvalidArgumentException('out-of-range bits detected');
780
            }
781
782 4
            $this->bitset = \substr($bitset, 0, -1) . $lastByteExpected;
783
        }
784
785 4
        $this->bitset = $bitset;
786 4
    }
787
788
    /**
789
     * Set binary bitset in little-endian order
790
     *
791
     * @param string $bitset
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
792
     * @return void
793
     * @throws InvalidArgumentException On out-of-range bits given as input bitset
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
794
     * @see setBinaryBitsetLeBin()
795
     * @see doSetBinaryBitsetLeBin()
796
     */
797 5
    private function doSetBinaryBitsetLeInt($bitset): void
0 ignored issues
show
Coding Style introduced by
Type hint "string" missing for $bitset
Loading history...
798
    {
799 5
        $len = \strlen($bitset);
800 5
        $int = 0;
801 5
        for ($i = 0; $i < $len; ++$i) {
802 5
            $ord = \ord($bitset[$i]);
803
804 5
            if ($ord && $i > \PHP_INT_SIZE - 1) {
805 1
                throw new InvalidArgumentException('out-of-range bits detected');
806
            }
807
808 5
            $int |= $ord << (8 * $i);
809
        }
810
811 4
        if ($int & (~0 << $this->enumerationCount)) {
812 2
            throw new InvalidArgumentException('out-of-range bits detected');
813
        }
814
815 2
        $this->bitset = $int;
816 2
    }
817
818
    /**
819
     * Get binary bitset in big-endian order
820
     * 
821
     * @return string
822
     */
823 1
    public function getBinaryBitsetBe(): string
824
    {
825 1
        return \strrev($this->bitset);
826
    }
827
828
    /**
829
     * Set the given binary bitset in big-endian order
830
     *
831
     * @param string $bitset
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
832
     * @return void
833
     * @throws InvalidArgumentException On out-of-range bits given as input bitset
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
834
     */
835
    public function setBinaryBitsetBe(string $bitset): void
836
    {
837
        $this->setBinaryBitsetLe(\strrev($bitset));
838
    }
839
840
    /**
841
     * Create a new set with the given binary bitset in big-endian order
842
     *
843
     * @param string $bitset
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
844
     * @return static
845
     * @throws InvalidArgumentException On out-of-range bits given as input bitset
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
846
     */
847 1
    public function withBinaryBitsetBe(string $bitset): self
848
    {
849 1
        return $this->withBinaryBitsetLe(\strrev($bitset));
850
    }
851
852
    /**
853
     * Get a bit at the given ordinal number
854
     *
855
     * @param int $ordinal Ordinal number of bit to get
2 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
856
     * @return bool
1 ignored issue
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
857
     * @throws InvalidArgumentException If the given ordinal number is out-of-range
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
858
     * @uses doGetBitBin()
859
     * @uses doGetBitInt()
860
     */
861 3
    public function getBit(int $ordinal): bool
862
    {
863 3
        if ($ordinal < 0 || $ordinal > $this->enumerationCount) {
864 1
            throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->enumerationCount}");
1 ignored issue
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 113 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
865
        }
866
867 2
        return $this->{$this->fnDoGetBit}($ordinal);
868
    }
869
870
    /**
871
     * Get a bit at the given ordinal number.
872
     *
873
     * This is the binary bitset implementation.
874
     *
875
     * @param int $ordinal Ordinal number of bit to get
2 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
876
     * @return bool
1 ignored issue
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
877
     * @see getBit()
878
     * @see doGetBitInt()
879
     */
880 5
    private function doGetBitBin($ordinal)
0 ignored issues
show
Coding Style introduced by
Type hint "int" missing for $ordinal
Loading history...
881
    {
882 5
        return (\ord($this->bitset[(int) ($ordinal / 8)]) & 1 << ($ordinal % 8)) !== 0;
883
    }
884
885
    /**
886
     * Get a bit at the given ordinal number.
887
     *
888
     * This is the integer bitset implementation.
889
     * 
890
     * @param int $ordinal Ordinal number of bit to get
2 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
891
     * @return bool
1 ignored issue
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
892
     * @see getBit()
893
     * @see doGetBitBin()
894
     */
895 9
    private function doGetBitInt($ordinal)
0 ignored issues
show
Coding Style introduced by
Type hint "int" missing for $ordinal
Loading history...
896
    {
897 9
        return (bool)($this->bitset & (1 << $ordinal));
898
    }
899
900
    /**
901
     * Set a bit at the given ordinal number
902
     *
903
     * @param int $ordinal Ordinal number of bit to set
2 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
904
     * @param bool $bit    The bit to set
2 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for parameter type
Loading history...
Coding Style introduced by
Expected 5 spaces after parameter name; 4 found
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
905
     * @return void
906
     * @throws InvalidArgumentException If the given ordinal number is out-of-range
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
907
     * @uses doSetBitBin()
908
     * @uses doSetBitInt()
909
     * @uses doUnsetBitBin()
910
     * @uses doUnsetBitInt()
911
     */
912
    public function setBit(int $ordinal, bool $bit): void
913
    {
914
        if ($ordinal < 0 || $ordinal > $this->enumerationCount) {
915
            throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->enumerationCount}");
1 ignored issue
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 113 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
916
        }
917
        if ($bit) {
918
            $this->{$this->fnDoSetBit}($ordinal);
919
        } else {
920
            $this->{$this->fnDoUnsetBit}($ordinal);
921
        }
922
    }
923
924
    /**
925
     * Create a new set with the bit at the given ordinal number set
926
     *
927
     * @param int $ordinal Ordinal number of bit to set
2 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
928
     * @param bool $bit    The bit to set
2 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for parameter type
Loading history...
Coding Style introduced by
Expected 5 spaces after parameter name; 4 found
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
929
     * @return static
930
     * @throws InvalidArgumentException If the given ordinal number is out-of-range
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
931
     * @uses doSetBitBin()
932
     * @uses doSetBitInt()
933
     * @uses doUnsetBitBin()
934
     * @uses doUnsetBitInt()
935
     */
936 2
    public function withBit(int $ordinal, bool $bit): self
937
    {
938 2
        if ($ordinal < 0 || $ordinal > $this->enumerationCount) {
939 1
            throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->enumerationCount}");
1 ignored issue
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 113 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
940
        }
941
942 1
        $clone = clone $this;
943 1
        if ($bit) {
944 1
            $clone->{$this->fnDoSetBit}($ordinal);
945
        } else {
946 1
            $clone->{$this->fnDoUnsetBit}($ordinal);
947
        }
948 1
        return $clone;
949
    }
950
951
    /**
952
     * Set a bit at the given ordinal number.
953
     *
954
     * This is the binary bitset implementation.
955
     * 
956
     * @param int $ordinal Ordinal number of bit to set
2 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
957
     * @return void
958
     * @see setBit()
959
     * @see doSetBitInt()
960
     */
961 8
    private function doSetBitBin($ordinal): void
0 ignored issues
show
Coding Style introduced by
Type hint "int" missing for $ordinal
Loading history...
962
    {
963 8
        $byte = (int) ($ordinal / 8);
964 8
        $this->bitset[$byte] = $this->bitset[$byte] | \chr(1 << ($ordinal % 8));
965 8
    }
966
967
    /**
968
     * Set a bit at the given ordinal number.
969
     *
970
     * This is the binary bitset implementation.
971
     *
972
     * @param int $ordinal Ordinal number of bit to set
2 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
973
     * @return void
974
     * @see setBit()
975
     * @see doSetBitBin()
976
     */
977 40
    private function doSetBitInt($ordinal): void
0 ignored issues
show
Coding Style introduced by
Type hint "int" missing for $ordinal
Loading history...
978
    {
979 40
        $this->bitset = $this->bitset | (1 << $ordinal);
980 40
    }
981
982
    /**
983
     * Unset a bit at the given ordinal number.
984
     *
985
     * This is the binary bitset implementation.
986
     *
987
     * @param int $ordinal Ordinal number of bit to unset
2 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
988
     * @return void
989
     * @see setBit()
990
     * @see doUnsetBitInt()
991
     */
992 2
    private function doUnsetBitBin($ordinal): void
0 ignored issues
show
Coding Style introduced by
Type hint "int" missing for $ordinal
Loading history...
993
    {
994 2
        $byte = (int) ($ordinal / 8);
995 2
        $this->bitset[$byte] = $this->bitset[$byte] & \chr(~(1 << ($ordinal % 8)));
996 2
    }
997
998
    /**
999
     * Unset a bit at the given ordinal number.
1000
     *
1001
     * This is the integer bitset implementation.
1002
     *
1003
     * @param int $ordinal Ordinal number of bit to unset
2 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
introduced by
Parameter comment must end with a full stop
Loading history...
1004
     * @return void
1005
     * @see setBit()
1006
     * @see doUnsetBitBin()
1007
     */
1008 5
    private function doUnsetBitInt($ordinal): void
0 ignored issues
show
Coding Style introduced by
Type hint "int" missing for $ordinal
Loading history...
1009
    {
1010 5
        $this->bitset = $this->bitset & ~(1 << $ordinal);
1011 5
    }
1012
}
1013