55namespace MabeEnum ;
66
77use Countable ;
8- use Iterator ;
98use InvalidArgumentException ;
9+ use Iterator ;
10+ use IteratorAggregate ;
11+ use OutOfBoundsException ;
1012
1113/**
1214 * A set of enumerators of the given enumeration (EnumSet<T>)
1618 * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
1719 * @link http://github.com/marc-mabe/php-enum for the canonical source repository
1820 */
19- class EnumSet implements Iterator , Countable
21+ class EnumSet implements IteratorAggregate , Countable
2022{
2123 /**
2224 * The classname of the Enumeration
@@ -25,16 +27,10 @@ class EnumSet implements Iterator, Countable
2527 private $ enumeration ;
2628
2729 /**
28- * Ordinal number of current iterator position
30+ * Number of enumerators defined in the enumeration
2931 * @var int
3032 */
31- private $ ordinal = 0 ;
32-
33- /**
34- * Highest possible ordinal number
35- * @var int
36- */
37- private $ ordinalMax ;
33+ private $ enumerationCount ;
3834
3935 /**
4036 * Integer or binary (little endian) bitset
@@ -49,7 +45,6 @@ class EnumSet implements Iterator, Countable
4945 *
5046 * @var string
5147 */
52- private $ fnDoRewind = 'doRewindInt ' ;
5348 private $ fnDoCount = 'doCountInt ' ;
5449 private $ fnDoGetOrdinals = 'doGetOrdinalsInt ' ;
5550 private $ fnDoGetBit = 'doGetBitInt ' ;
@@ -75,18 +70,17 @@ public function __construct(string $enumeration)
7570 ));
7671 }
7772
78- $ this ->enumeration = $ enumeration ;
79- $ this ->ordinalMax = \count ($ enumeration ::getConstants ());
73+ $ this ->enumeration = $ enumeration ;
74+ $ this ->enumerationCount = \count ($ enumeration ::getConstants ());
8075
8176 // By default the bitset is initialized as integer bitset
8277 // in case the enumeraton has more enumerators then integer bits
8378 // we will switch this into a binary bitset
84- if ($ this ->ordinalMax > \PHP_INT_SIZE * 8 ) {
79+ if ($ this ->enumerationCount > \PHP_INT_SIZE * 8 ) {
8580 // init binary bitset with zeros
86- $ this ->bitset = \str_repeat ("\0" , (int )\ceil ($ this ->ordinalMax / 8 ));
81+ $ this ->bitset = \str_repeat ("\0" , (int )\ceil ($ this ->enumerationCount / 8 ));
8782
8883 // switch internal binary bitset functions
89- $ this ->fnDoRewind = 'doRewindBin ' ;
9084 $ this ->fnDoCount = 'doCountBin ' ;
9185 $ this ->fnDoGetOrdinals = 'doGetOrdinalsBin ' ;
9286 $ this ->fnDoGetBit = 'doGetBitBin ' ;
@@ -138,106 +132,22 @@ public function contains($enumerator): bool
138132 return $ this ->{$ this ->fnDoGetBit }(($ this ->enumeration )::get ($ enumerator )->getOrdinal ());
139133 }
140134
141- /* Iterator */
142-
143- /**
144- * Get the current enumerator
145- * @return Enum|null Returns the current enumerator or NULL on an invalid iterator position
146- */
147- public function current (): ?Enum
148- {
149- if ($ this ->valid ()) {
150- return ($ this ->enumeration )::byOrdinal ($ this ->ordinal );
151- }
152-
153- return null ;
154- }
135+ /* IteratorAggregate */
155136
156137 /**
157- * Get the ordinal number of the current iterator position
158- * @return int
138+ * Create and return a new iterator
139+ * @return Iterator
159140 */
160- public function key (): int
141+ public function getIterator (): Iterator
161142 {
162- return $ this ->ordinal ;
163- }
164-
165- /**
166- * Go to the next valid iterator position.
167- * If no valid iterator position is found the iterator position will be the last possible + 1.
168- * @return void
169- */
170- public function next (): void
171- {
172- do {
173- if (++$ this ->ordinal >= $ this ->ordinalMax ) {
174- $ this ->ordinal = $ this ->ordinalMax ;
175- return ;
143+ $ ordinal = -1 ;
144+ while (++$ ordinal < $ this ->enumerationCount ) {
145+ if ($ this ->{$ this ->fnDoGetBit }($ ordinal )) {
146+ yield $ ordinal => ($ this ->enumeration )::byOrdinal ($ ordinal );
176147 }
177- } while (!$ this ->{$ this ->fnDoGetBit }($ this ->ordinal ));
178- }
179-
180- /**
181- * Go to the first valid iterator position.
182- * If no valid iterator position was found the iterator position will be 0.
183- * @return void
184- * @uses doRewindBin()
185- * @uses doRewindInt()
186- */
187- public function rewind (): void
188- {
189- $ this ->{$ this ->fnDoRewind }();
190- }
191-
192- /**
193- * Go to the first valid iterator position.
194- * If no valid iterator position was found the iterator position will be 0.
195- *
196- * This is the binary bitset implementation.
197- *
198- * @return void
199- * @see rewind()
200- * @see doRewindInt()
201- */
202- private function doRewindBin (): void
203- {
204- if (\ltrim ($ this ->bitset , "\0" ) !== '' ) {
205- $ this ->ordinal = -1 ;
206- $ this ->next ();
207- } else {
208- $ this ->ordinal = 0 ;
209148 }
210149 }
211150
212- /**
213- * Go to the first valid iterator position.
214- * If no valid iterator position was found the iterator position will be 0.
215- *
216- * This is the binary bitset implementation.
217- *
218- * @return void
219- * @see rewind()
220- * @see doRewindBin()
221- */
222- private function doRewindInt (): void
223- {
224- if ($ this ->bitset ) {
225- $ this ->ordinal = -1 ;
226- $ this ->next ();
227- } else {
228- $ this ->ordinal = 0 ;
229- }
230- }
231-
232- /**
233- * Test if the iterator is in a valid state
234- * @return bool
235- */
236- public function valid (): bool
237- {
238- return $ this ->ordinal !== $ this ->ordinalMax && $ this ->{$ this ->fnDoGetBit }($ this ->ordinal );
239- }
240-
241151 /* Countable */
242152
243153 /**
@@ -500,7 +410,7 @@ private function doGetOrdinalsBin(): array
500410 private function doGetOrdinalsInt (): array
501411 {
502412 $ ordinals = [];
503- $ ordinalMax = $ this ->ordinalMax ;
413+ $ ordinalMax = $ this ->enumerationCount ;
504414 $ bitset = $ this ->bitset ;
505415 for ($ ord = 0 ; $ ord < $ ordinalMax ; ++$ ord ) {
506416 if ($ bitset & (1 << $ ord )) {
@@ -590,7 +500,7 @@ private function doGetBinaryBitsetLeBin(): string
590500 private function doGetBinaryBitsetLeInt (): string
591501 {
592502 $ bin = \pack (\PHP_INT_SIZE === 8 ? 'P ' : 'V ' , $ this ->bitset );
593- return \substr ($ bin , 0 , (int )\ceil ($ this ->ordinalMax / 8 ));
503+ return \substr ($ bin , 0 , (int )\ceil ($ this ->enumerationCount / 8 ));
594504 }
595505
596506 /**
@@ -607,9 +517,6 @@ private function doGetBinaryBitsetLeInt(): string
607517 public function setBinaryBitsetLe (string $ bitset ): void
608518 {
609519 $ this ->{$ this ->fnDoSetBinaryBitsetLe }($ bitset );
610-
611- // reset the iterator position
612- $ this ->rewind ();
613520 }
614521
615522 /**
@@ -639,7 +546,7 @@ private function doSetBinaryBitsetLeBin(string $bitset): void
639546 }
640547
641548 // truncate out-of-range bits of last byte
642- $ lastByteMaxOrd = $ this ->ordinalMax % 8 ;
549+ $ lastByteMaxOrd = $ this ->enumerationCount % 8 ;
643550 if ($ lastByteMaxOrd !== 0 ) {
644551 $ lastByte = $ bitset [-1 ];
645552 $ lastByteExpected = \chr ((1 << $ lastByteMaxOrd ) - 1 ) & $ lastByte ;
@@ -678,7 +585,7 @@ private function doSetBinaryBitsetLeInt(string $bitset): void
678585 $ int |= $ ord << (8 * $ i );
679586 }
680587
681- if ($ int & (~0 << $ this ->ordinalMax )) {
588+ if ($ int & (~0 << $ this ->enumerationCount )) {
682589 throw new InvalidArgumentException ('out-of-range bits detected ' );
683590 }
684591
@@ -720,8 +627,8 @@ public function setBinaryBitsetBe(string $bitset): void
720627 */
721628 public function getBit (int $ ordinal ): bool
722629 {
723- if ($ ordinal < 0 || $ ordinal > $ this ->ordinalMax ) {
724- throw new InvalidArgumentException ("Ordinal number must be between 0 and {$ this ->ordinalMax }" );
630+ if ($ ordinal < 0 || $ ordinal > $ this ->enumerationCount ) {
631+ throw new InvalidArgumentException ("Ordinal number must be between 0 and {$ this ->enumerationCount }" );
725632 }
726633
727634 return $ this ->{$ this ->fnDoGetBit }($ ordinal );
@@ -771,8 +678,8 @@ private function doGetBitInt(int $ordinal): bool
771678 */
772679 public function setBit (int $ ordinal , bool $ bit ): void
773680 {
774- if ($ ordinal < 0 || $ ordinal > $ this ->ordinalMax ) {
775- throw new InvalidArgumentException ("Ordinal number must be between 0 and {$ this ->ordinalMax }" );
681+ if ($ ordinal < 0 || $ ordinal > $ this ->enumerationCount ) {
682+ throw new InvalidArgumentException ("Ordinal number must be between 0 and {$ this ->enumerationCount }" );
776683 }
777684
778685 if ($ bit ) {
0 commit comments