22
33namespace MabeEnum ;
44
5- use SplObjectStorage ;
5+ use ArrayAccess ;
6+ use Countable ;
67use InvalidArgumentException ;
8+ use Iterator ;
9+ use UnexpectedValueException ;
710
811/**
9- * A map of enumerator keys of the given enumeration (EnumMap<T>)
10- * based on SplObjectStorage
12+ * A map of enumerators (EnumMap<T>) and mixed values.
1113 *
1214 * @link http://github.com/marc-mabe/php-enum for the canonical source repository
1315 * @copyright Copyright (c) 2017 Marc Bennewitz
1416 * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
1517 */
16- class EnumMap extends SplObjectStorage
18+ class EnumMap implements ArrayAccess, Countable, Iterator
1719{
1820 /**
1921 * The classname of the enumeration type
2022 * @var string
2123 */
2224 private $ enumeration ;
2325
26+ /**
27+ * Internal map of ordinal number and value
28+ * @var array
29+ */
30+ private $ map = [];
31+
32+ /**
33+ * List of ordinal numbers
34+ * @var int[]
35+ */
36+ private $ ordinals = [];
37+
38+ /**
39+ * Current iterator position
40+ * @var int
41+ */
42+ private $ pos = 0 ;
43+
2444 /**
2545 * Constructor
2646 * @param string $enumeration The classname of the enumeration type
@@ -29,7 +49,7 @@ class EnumMap extends SplObjectStorage
2949 public function __construct ($ enumeration )
3050 {
3151 if (!\is_subclass_of ($ enumeration , Enum::class)) {
32- throw new InvalidArgumentException (sprintf (
52+ throw new InvalidArgumentException (\ sprintf (
3353 "This EnumMap can handle subclasses of '%s' only " ,
3454 Enum::class
3555 ));
@@ -55,8 +75,7 @@ public function getEnumeration()
5575 */
5676 public function attach ($ enumerator , $ data = null )
5777 {
58- $ enumeration = $ this ->enumeration ;
59- parent ::attach ($ enumeration ::get ($ enumerator ), $ data );
78+ return $ this ->offsetSet ($ enumerator , $ data );
6079 }
6180
6281 /**
@@ -66,13 +85,7 @@ public function attach($enumerator, $data = null)
6685 */
6786 public function contains ($ enumerator )
6887 {
69- try {
70- $ enumeration = $ this ->enumeration ;
71- return parent ::contains ($ enumeration ::get ($ enumerator ));
72- } catch (InvalidArgumentException $ e ) {
73- // On an InvalidArgumentException the given argument can't be contained in this map
74- return false ;
75- }
88+ return $ this ->offsetExists ($ enumerator );
7689 }
7790
7891 /**
@@ -83,8 +96,7 @@ public function contains($enumerator)
8396 */
8497 public function detach ($ enumerator )
8598 {
86- $ enumeration = $ this ->enumeration ;
87- parent ::detach ($ enumeration ::get ($ enumerator ));
99+ $ this ->offsetUnset ($ enumerator );
88100 }
89101
90102 /**
@@ -95,7 +107,14 @@ public function detach($enumerator)
95107 */
96108 public function offsetExists ($ enumerator )
97109 {
98- return $ this ->contains ($ enumerator );
110+ try {
111+ $ enumeration = $ this ->enumeration ;
112+ $ ord = $ enumeration ::get ($ enumerator )->getOrdinal ();
113+ return isset ($ this ->map [$ ord ]);
114+ } catch (InvalidArgumentException $ e ) {
115+ // An invalid enumerator can't be contained in this map
116+ return false ;
117+ }
99118 }
100119
101120 /**
@@ -107,7 +126,15 @@ public function offsetExists($enumerator)
107126 public function offsetGet ($ enumerator )
108127 {
109128 $ enumeration = $ this ->enumeration ;
110- return parent ::offsetGet ($ enumeration ::get ($ enumerator ));
129+ $ ord = $ enumeration ::get ($ enumerator )->getOrdinal ();
130+ if (!isset ($ this ->map [$ ord ])) {
131+ throw new UnexpectedValueException (\sprintf (
132+ "Enumerator '%s' could not be found " ,
133+ \is_object ($ enumerator ) ? $ enumerator ->getValue () : $ enumerator
134+ ));
135+ }
136+
137+ return $ this ->map [$ ord ];
111138 }
112139
113140 /**
@@ -121,7 +148,12 @@ public function offsetGet($enumerator)
121148 public function offsetSet ($ enumerator , $ data = null )
122149 {
123150 $ enumeration = $ this ->enumeration ;
124- parent ::offsetSet ($ enumeration ::get ($ enumerator ), $ data );
151+ $ ord = $ enumeration ::get ($ enumerator )->getOrdinal ();
152+
153+ if (!isset ($ this ->map [$ ord ])) {
154+ $ this ->ordinals [] = $ ord ;
155+ }
156+ $ this ->map [$ ord ] = $ data ;
125157 }
126158
127159 /**
@@ -134,7 +166,11 @@ public function offsetSet($enumerator, $data = null)
134166 public function offsetUnset ($ enumerator )
135167 {
136168 $ enumeration = $ this ->enumeration ;
137- parent ::offsetUnset ($ enumeration ::get ($ enumerator ));
169+ $ ord = $ enumeration ::get ($ enumerator )->getOrdinal ();
170+
171+ if (($ idx = \array_search ($ ord , $ this ->ordinals , true )) !== false ) {
172+ unset($ this ->map [$ ord ], $ this ->ordinals [$ idx ]);
173+ }
138174 }
139175
140176 /**
@@ -143,7 +179,11 @@ public function offsetUnset($enumerator)
143179 */
144180 public function current ()
145181 {
146- return parent ::getInfo ();
182+ if (!isset ($ this ->ordinals [$ this ->pos ])) {
183+ return null ;
184+ }
185+
186+ return $ this ->map [$ this ->ordinals [$ this ->pos ]];
147187 }
148188
149189 /**
@@ -152,6 +192,48 @@ public function current()
152192 */
153193 public function key ()
154194 {
155- return parent ::current ();
195+ if (!isset ($ this ->ordinals [$ this ->pos ])) {
196+ return null ;
197+ }
198+
199+ $ enumeration = $ this ->enumeration ;
200+ return $ enumeration ::byOrdinal ($ this ->ordinals [$ this ->pos ]);
201+ }
202+
203+ /**
204+ * Reset the iterator position to zero.
205+ * @return void
206+ */
207+ public function rewind ()
208+ {
209+ $ this ->pos = 0 ;
210+ }
211+
212+ /**
213+ * Increment the iterator position by one.
214+ * @return void
215+ */
216+ public function next ()
217+ {
218+ ++$ this ->pos ;
219+ }
220+
221+ /**
222+ * Test if the iterator is in a valid state
223+ * @return boolean
224+ */
225+ public function valid ()
226+ {
227+ return isset ($ this ->ordinals [$ this ->pos ]);
228+ }
229+
230+ /**
231+ * Count the number of elements
232+ *
233+ * @return int
234+ */
235+ public function count ()
236+ {
237+ return \count ($ this ->ordinals );
156238 }
157239}
0 commit comments