1212use UnexpectedValueException ;
1313
1414/**
15- * A map of enumerators (EnumMap<T>) and mixed values .
15+ * A map of enumerators and data values (EnumMap<K extends Enum, V>) .
1616 *
1717 * @copyright 2019 Marc Bennewitz
1818 * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
@@ -27,17 +27,18 @@ class EnumMap implements ArrayAccess, Countable, IteratorAggregate
2727 private $ enumeration ;
2828
2929 /**
30- * Internal map of ordinal number and value
30+ * Internal map of ordinal number and data value
3131 * @var array
3232 */
3333 private $ map = [];
3434
3535 /**
3636 * Constructor
3737 * @param string $enumeration The classname of the enumeration type
38+ * @param null|iterable $map Initialize map
3839 * @throws InvalidArgumentException
3940 */
40- public function __construct (string $ enumeration )
41+ public function __construct (string $ enumeration, iterable $ map = null )
4142 {
4243 if (!\is_subclass_of ($ enumeration , Enum::class)) {
4344 throw new InvalidArgumentException (\sprintf (
@@ -47,10 +48,129 @@ public function __construct(string $enumeration)
4748 ));
4849 }
4950 $ this ->enumeration = $ enumeration ;
51+
52+ if ($ map ) {
53+ $ this ->addIterable ($ map );
54+ }
55+ }
56+
57+ /* write access (mutable) */
58+
59+ /**
60+ * Adds the given enumerator (object or value) mapping to the specified data value.
61+ * @param Enum|null|bool|int|float|string|array $enumerator
62+ * @param mixed $value
63+ * @throws InvalidArgumentException On an invalid given enumerator
64+ * @see offsetSet()
65+ */
66+ public function add ($ enumerator , $ value ): void
67+ {
68+ $ ord = ($ this ->enumeration )::get ($ enumerator )->getOrdinal ();
69+ $ this ->map [$ ord ] = $ value ;
5070 }
5171
5272 /**
53- * Get the classname of the enumeration
73+ * Adds the given iterable, mapping enumerators (objects or values) to data values.
74+ * @param iterable $map
75+ * @throws InvalidArgumentException On an invalid given enumerator
76+ */
77+ public function addIterable (iterable $ map ): void
78+ {
79+ $ innerMap = $ this ->map ;
80+ foreach ($ map as $ enumerator => $ value ) {
81+ $ ord = ($ this ->enumeration )::get ($ enumerator )->getOrdinal ();
82+ $ innerMap [$ ord ] = $ value ;
83+ }
84+ $ this ->map = $ innerMap ;
85+ }
86+
87+ /**
88+ * Removes the given enumerator (object or value) mapping.
89+ * @param Enum|null|bool|int|float|string|array $enumerator
90+ * @throws InvalidArgumentException On an invalid given enumerator
91+ * @see offsetUnset()
92+ */
93+ public function remove ($ enumerator ): void
94+ {
95+ $ ord = ($ this ->enumeration )::get ($ enumerator )->getOrdinal ();
96+ unset($ this ->map [$ ord ]);
97+ }
98+
99+ /**
100+ * Removes the given iterable enumerator (object or value) mappings.
101+ * @param iterable $enumerators
102+ * @throws InvalidArgumentException On an invalid given enumerator
103+ */
104+ public function removeIterable (iterable $ enumerators ): void
105+ {
106+ $ map = $ this ->map ;
107+ foreach ($ enumerators as $ enumerator ) {
108+ $ ord = ($ this ->enumeration )::get ($ enumerator )->getOrdinal ();
109+ unset($ map [$ ord ]);
110+ }
111+
112+ $ this ->map = $ map ;
113+ }
114+
115+ /* write access (immutable) */
116+
117+ /**
118+ * Creates a new map with the given enumerator (object or value) mapping to the specified data value added.
119+ * @param Enum|null|bool|int|float|string|array $enumerator
120+ * @param mixed $value
121+ * @return static
122+ * @throws InvalidArgumentException On an invalid given enumerator
123+ */
124+ public function with ($ enumerator , $ value ): self
125+ {
126+ $ clone = clone $ this ;
127+ $ clone ->add ($ enumerator , $ value );
128+ return $ clone ;
129+ }
130+
131+ /**
132+ * Creates a new map with the given iterable mapping enumerators (objects or values) to data values added.
133+ * @param iterable $map
134+ * @return static
135+ * @throws InvalidArgumentException On an invalid given enumerator
136+ */
137+ public function withIterable (iterable $ map ): self
138+ {
139+ $ clone = clone $ this ;
140+ $ clone ->addIterable ($ map );
141+ return $ clone ;
142+ }
143+
144+ /**
145+ * Create a new map with the given enumerator mapping removed.
146+ * @param Enum|null|bool|int|float|string|array $enumerator
147+ * @return static
148+ * @throws InvalidArgumentException On an invalid given enumerator
149+ */
150+ public function without ($ enumerator ): self
151+ {
152+ $ clone = clone $ this ;
153+ $ clone ->remove ($ enumerator );
154+ return $ clone ;
155+ }
156+
157+ /**
158+ * Creates a new map with the given iterable enumerator (object or value) mappings removed.
159+ * @param iterable $enumerators
160+ * @return static
161+ * @throws InvalidArgumentException On an invalid given enumerator
162+ */
163+ public function withoutIterable (iterable $ enumerators ): self
164+ {
165+ $ clone = clone $ this ;
166+ $ clone ->removeIterable ($ enumerators );
167+ return $ clone ;
168+ }
169+
170+ /* read access */
171+
172+ /**
173+ * Get the classname of the enumeration type.
54174 * @return string
55175 */
56176 public function getEnumeration (): string
@@ -59,7 +179,29 @@ public function getEnumeration(): string
59179 }
60180
61181 /**
62- * Get a list of map keys
182+ * Get the mapped data value of the given enumerator (object or value).
183+ * @param Enum|null|bool|int|float|string|array $enumerator
184+ * @return mixed
185+ * @throws InvalidArgumentException On an invalid given enumerator
186+ * @throws UnexpectedValueException If the given enumerator does not exist in this map
187+ * @see offsetGet()
188+ */
189+ public function get ($ enumerator )
190+ {
191+ $ enumerator = ($ this ->enumeration )::get ($ enumerator );
192+ $ ord = $ enumerator ->getOrdinal ();
193+ if (!\array_key_exists ($ ord , $ this ->map )) {
194+ throw new UnexpectedValueException (sprintf (
195+ 'Enumerator %s could not be found ' ,
196+ \var_export ($ enumerator ->getValue (), true )
197+ ));
198+ }
199+
200+ return $ this ->map [$ ord ];
201+ }
202+
203+ /**
204+ * Get a list of enumerator keys.
63205 * @return Enum[]
64206 */
65207 public function getKeys (): array
@@ -68,7 +210,7 @@ public function getKeys(): array
68210 }
69211
70212 /**
71- * Get a list of map values
213+ * Get a list of mapped data values.
72214 * @return mixed[]
73215 */
74216 public function getValues (): array
@@ -77,10 +219,10 @@ public function getValues(): array
77219 }
78220
79221 /**
80- * Search for the given value
222+ * Search for the given data value.
81223 * @param mixed $value
82224 * @param bool $strict Use strict type comparison
83- * @return Enum|null The found key or NULL
225+ * @return Enum|null The enumerator object of the first matching data value or NULL
84226 */
85227 public function search ($ value , bool $ strict = false )
86228 {
@@ -93,7 +235,7 @@ public function search($value, bool $strict = false)
93235 }
94236
95237 /**
96- * Test if the given enumerator exists
238+ * Test if the given enumerator key (object or value) exists.
97239 * @param Enum|null|bool|int|float|string|array $enumerator
98240 * @return bool
99241 * @see offsetExists
@@ -109,8 +251,10 @@ public function contains($enumerator): bool
109251 }
110252 }
111253
254+ /* ArrayAccess */
255+
112256 /**
113- * Test if the given enumerator key exists and is not NULL
257+ * Test if the given enumerator key (object or value) exists and is not NULL
114258 * @param Enum|null|bool|int|float|string|array $enumerator
115259 * @return bool
116260 * @see contains
@@ -126,57 +270,52 @@ public function offsetExists($enumerator): bool
126270 }
127271
128272 /**
129- * Get mapped data for the given enumerator
273+ * Get the mapped data value of the given enumerator (object or value).
130274 * @param Enum|null|bool|int|float|string|array $enumerator
131- * @return mixed
275+ * @return mixed The mapped date value of the given enumerator or NULL
132276 * @throws InvalidArgumentException On an invalid given enumerator
133- * @throws UnexpectedValueException If the given enumerator does not exist in this map
277+ * @see get()
134278 */
135279 public function offsetGet ($ enumerator )
136280 {
137- $ enumerator = ($ this ->enumeration )::get ($ enumerator );
138- $ ord = $ enumerator ->getOrdinal ();
139- if (!isset ($ this ->map [$ ord ]) && !\array_key_exists ($ ord , $ this ->map )) {
140- throw new UnexpectedValueException (sprintf (
141- 'Enumerator %s could not be found ' ,
142- \var_export ($ enumerator ->getValue (), true )
143- ));
281+ try {
282+ return $ this ->get ($ enumerator );
283+ } catch (UnexpectedValueException $ e ) {
284+ return null ;
144285 }
145-
146- return $ this ->map [$ ord ];
147286 }
148287
149288 /**
150- * Attach a new enumerator or overwrite an existing one
289+ * Adds the given enumerator (object or value) mapping to the specified data value.
151290 * @param Enum|null|bool|int|float|string|array $enumerator
152291 * @param mixed $value
153292 * @return void
154293 * @throws InvalidArgumentException On an invalid given enumerator
155- * @see attach ()
294+ * @see add ()
156295 */
157296 public function offsetSet ($ enumerator , $ value = null ): void
158297 {
159- $ ord = ($ this ->enumeration )::get ($ enumerator )->getOrdinal ();
160- $ this ->map [$ ord ] = $ value ;
298+ $ this ->add ($ enumerator , $ value );
161299 }
162300
163301 /**
164- * Detach an existing enumerator
302+ * Removes the given enumerator (object or value) mapping.
165303 * @param Enum|null|bool|int|float|string|array $enumerator
166304 * @return void
167305 * @throws InvalidArgumentException On an invalid given enumerator
168- * @see detach ()
306+ * @see remove ()
169307 */
170308 public function offsetUnset ($ enumerator ): void
171309 {
172- $ ord = ($ this ->enumeration )::get ($ enumerator )->getOrdinal ();
173- unset($ this ->map [$ ord ]);
310+ $ this ->remove ($ enumerator );
174311 }
175312
313+ /* IteratorAggregate */
314+
176315 /**
177316 * Get a new Iterator.
178317 *
179- * @return Iterator
318+ * @return Iterator Iterator<K extends Enum, V>
180319 */
181320 public function getIterator (): Iterator
182321 {
@@ -186,6 +325,8 @@ public function getIterator(): Iterator
186325 }
187326 }
188327
328+ /* Countable */
329+
189330 /**
190331 * Count the number of elements
191332 *
0 commit comments