Completed
Push — debugInfo ( 75c149 )
by Marc
04:17
created

EnumMap::__debugInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 4
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MabeEnum;
6
7
use ArrayAccess;
8
use Countable;
9
use InvalidArgumentException;
10
use Iterator;
11
use IteratorAggregate;
12
use UnexpectedValueException;
13
14
/**
15
 * A map of enumerators and data values (EnumMap<K extends Enum, V>).
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
 */
21
class EnumMap implements ArrayAccess, Countable, IteratorAggregate
22
{
23
    /**
24
     * The classname of the enumeration type
25
     * @var string
26
     */
27
    private $enumeration;
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
28
29
    /**
30
     * Internal map of ordinal number and data value
31
     * @var array
32
     */
33
    private $map = [];
34
35
    /**
36
     * Constructor
37
     * @param string $enumeration The classname of the enumeration type
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter type; 1 found
Loading history...
38
     * @param null|iterable $map Initialize map
0 ignored issues
show
Coding Style introduced by
Expected 9 spaces after parameter name; 1 found
Loading history...
39
     * @throws InvalidArgumentException
0 ignored issues
show
introduced by
Comment missing for @throws tag in function comment
Loading history...
40
     */
41 20
    public function __construct(string $enumeration, iterable $map = null)
42
    {
43 20
        if (!\is_subclass_of($enumeration, Enum::class)) {
44 1
            throw new InvalidArgumentException(\sprintf(
45 1
                '%s can handle subclasses of %s only',
46 1
                 __CLASS__,
47 1
                Enum::class
48
            ));
49
        }
50 19
        $this->enumeration = $enumeration;
51
52 19
        if ($map) {
0 ignored issues
show
introduced by
$map is of type iterable|null, thus it always evaluated to false.
Loading history...
53 3
            $this->addIterable($map);
54
        }
55 19
    }
56
57
    /**
58
     * Add virtual private property "__pairs" with a list of key-value-pairs
59
     * to the result of var_dump.
60
     *
61
     * This helps debugging as internally the map is using the ordinal number.
62
     *
63
     * @return array<string, mixed>
64
     */
65
    public function __debugInfo(): array {
66
        $dbg = (array)$this;
67
        $dbg["\0" . self::class . "\0__pairs"] = [$this->getKeys(), $this->getValues()];
68
        return $dbg;
69
    }
70
71
    /* write access (mutable) */
72
73
    /**
74
     * Adds the given enumerator (object or value) mapping to the specified data value.
75
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
76
     * @param mixed                                 $value
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
77
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
78
     * @see offsetSet()
79
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
80 12
    public function add($enumerator, $value): void
81
    {
82 12
        $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
83 11
        $this->map[$ord] = $value;
84 11
    }
85
86
    /**
87
     * Adds the given iterable, mapping enumerators (objects or values) to data values.
88
     * @param iterable $map
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
89
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
90
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
91 5
    public function addIterable(iterable $map): void
92
    {
93 5
        $innerMap = $this->map;
94 5
        foreach ($map as $enumerator => $value) {
95 5
            $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
96 5
            $innerMap[$ord] = $value;
97
        }
98 5
        $this->map = $innerMap;
99 5
    }
100
101
    /**
102
     * Removes the given enumerator (object or value) mapping.
103
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
104
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
105
     * @see offsetUnset()
106
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
107 5
    public function remove($enumerator): void
108
    {
109 5
        $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
110 5
        unset($this->map[$ord]);
111 5
    }
112
113
    /**
114
     * Removes the given iterable enumerator (object or value) mappings.
115
     * @param iterable $enumerators
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
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
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
118 3
    public function removeIterable(iterable $enumerators): void
119
    {
120 3
        $map = $this->map;
121 3
        foreach ($enumerators as $enumerator) {
122 3
            $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
123 3
            unset($map[$ord]);
124
        }
125
126 3
        $this->map = $map;
127 3
    }
128
129
    /* write access (immutable) */
130
131
    /**
132
     * Creates a new map with the given enumerator (object or value) mapping to the specified data value added.
133
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
134
     * @param mixed                                 $value
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
135
     * @return static
136
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
137
     */
138 1
    public function with($enumerator, $value): self
139
    {
140 1
        $clone = clone $this;
141 1
        $clone->add($enumerator, $value);
142 1
        return $clone;
143
    }
144
145
    /**
146
     * Creates a new map with the given iterable mapping enumerators (objects or values) to data values added.
147
     * @param iterable $map
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
148
     * @return static
149
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
150
     */
151 1
    public function withIterable(iterable $map): self
152
    {
153 1
        $clone = clone $this;
154 1
        $clone->addIterable($map);
155 1
        return $clone;
156
    }
157
158
    /**
159
     * Create a new map with the given enumerator mapping removed.
160
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
161
     * @return static
162
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
163
     */
164 1
    public function without($enumerator): self
165
    {
166 1
        $clone = clone $this;
167 1
        $clone->remove($enumerator);
168 1
        return $clone;
169
    }
170
171
    /**
172
     * Creates a new map with the given iterable enumerator (object or value) mappings removed.
173
     * @param iterable $enumerators
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
174
     * @return static
175
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
176
     */
177 1
    public function withoutIterable(iterable $enumerators): self
178
    {
179 1
        $clone = clone $this;
180 1
        $clone->removeIterable($enumerators);
181 1
        return $clone;
182
    }
183
184
    /* read access */
185
186
    /**
187
     * Get the classname of the enumeration type.
188
     * @return string
189
     */
190 1
    public function getEnumeration(): string
191
    {
192 1
        return $this->enumeration;
193
    }
194
195
    /**
196
     * Get the mapped data value of the given enumerator (object or value).
197
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
198
     * @return mixed
199
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
200
     * @throws UnexpectedValueException If the given enumerator does not exist in this map
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
201
     * @see offsetGet()
202
     */
203 10
    public function get($enumerator)
204
    {
205 10
        $enumerator = ($this->enumeration)::get($enumerator);
206 10
        $ord = $enumerator->getOrdinal();
207 10
        if (!\array_key_exists($ord, $this->map)) {
208 2
            throw new UnexpectedValueException(sprintf(
209 2
                'Enumerator %s could not be found',
210 2
                \var_export($enumerator->getValue(), true)
211
            ));
212
        }
213
214 8
        return $this->map[$ord];
215
    }
216
217
    /**
218
     * Get a list of enumerator keys.
219
     * @return Enum[]
220
     */
221 7
    public function getKeys(): array
222
    {
223 7
        return \array_map([$this->enumeration, 'byOrdinal'], \array_keys($this->map));
224
    }
225
226
    /**
227
     * Get a list of mapped data values.
228
     * @return mixed[]
229
     */
230 7
    public function getValues(): array
231
    {
232 7
        return \array_values($this->map);
233
    }
234
235
    /**
236
     * Search for the given data value.
237
     * @param mixed $value
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
238
     * @param bool $strict Use strict type comparison
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
239
     * @return Enum|null The enumerator object of the first matching data value or NULL
240
     */
241 2
    public function search($value, bool $strict = false)
242
    {
243 2
        $ord = \array_search($value, $this->map, $strict);
244 2
        if ($ord !== false) {
245 2
            return ($this->enumeration)::byOrdinal($ord);
246
        }
247
248 2
        return null;
249
    }
250
251
    /**
252
     * Test if the given enumerator key (object or value) exists.
253
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
254
     * @return bool
255
     * @see offsetExists()
256
     */
257 8
    public function has($enumerator): bool
258
    {
259
        try {
260 8
            $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
261 7
            return \array_key_exists($ord, $this->map);
262 1
        } catch (InvalidArgumentException $e) {
263
            // An invalid enumerator can't be contained in this map
264 1
            return false;
265
        }
266
    }
267
268
    /**
269
     * Test if the given enumerator key (object or value) exists.
270
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
271
     * @return bool
272
     * @see offsetExists()
273
     * @see has()
274
     * @deprecated Will trigger deprecation warning in last 4.x and removed in 5.x
275
     */
276 1
    public function contains($enumerator): bool
277
    {
278 1
        return $this->has($enumerator);
279
    }
280
281
    /* ArrayAccess */
282
283
    /**
284
     * Test if the given enumerator key (object or value) exists and is not NULL
285
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
286
     * @return bool
287
     * @see contains()
288
     */
289 5
    public function offsetExists($enumerator): bool
290
    {
291
        try {
292 5
            return isset($this->map[($this->enumeration)::get($enumerator)->getOrdinal()]);
293 1
        } catch (InvalidArgumentException $e) {
294
            // An invalid enumerator can't be an offset of this map
295 1
            return false;
296
        }
297
    }
298
299
    /**
300
     * Get the mapped data value of the given enumerator (object or value).
301
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
302
     * @return mixed The mapped date value of the given enumerator or NULL
303
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
304
     * @see get()
305
     */
306 4
    public function offsetGet($enumerator)
307
    {
308
        try {
309 4
            return $this->get($enumerator);
310 1
        } catch (UnexpectedValueException $e) {
311 1
            return null;
312
        }
313
    }
314
315
    /**
316
     * Adds the given enumerator (object or value) mapping to the specified data value.
317
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
318
     * @param mixed                                 $value
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
319
     * @return void
320
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
321
     * @see add()
322
     */
323 7
    public function offsetSet($enumerator, $value = null): void
324
    {
325 7
        $this->add($enumerator, $value);
326 6
    }
327
328
    /**
329
     * Removes the given enumerator (object or value) mapping.
330
     * @param Enum|null|bool|int|float|string|array $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
331
     * @return void
332
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
333
     * @see remove()
334
     */
335 2
    public function offsetUnset($enumerator): void
336
    {
337 2
        $this->remove($enumerator);
338 2
    }
339
340
    /* IteratorAggregate */
341
342
    /**
343
     * Get a new Iterator.
344
     *
345
     * @return Iterator Iterator<K extends Enum, V>
346
     */
347 2
    public function getIterator(): Iterator
348
    {
349 2
        $map = $this->map;
350 2
        foreach ($map as $ordinal => $value) {
351 2
            yield ($this->enumeration)::byOrdinal($ordinal) => $value;
352
        }
353 2
    }
354
355
    /* Countable */
356
357
    /**
358
     * Count the number of elements
359
     *
360
     * @return int
361
     */
362 3
    public function count(): int
363
    {
364 3
        return \count($this->map);
365
    }
366
367
    /**
368
     * Tests if the map is empty
369
     *
370
     * @return bool
371
     */
372 1
    public function isEmpty(): bool
373
    {
374 1
        return empty($this->map);
375
    }
376
}
377