EnumMap::get()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 1
dl 0
loc 12
ccs 8
cts 8
cp 1
crap 2
rs 10
c 0
b 0
f 0
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<T of Enum, mixed>).
16
 *
17
 * @template T of Enum
18
 * @implements ArrayAccess<T, mixed>
19
 * @implements IteratorAggregate<T, mixed>
20
 *
21
 * @copyright 2020, Marc Bennewitz
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
22
 * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
23
 * @link http://github.com/marc-mabe/php-enum for the canonical source repository
24
 */
25
class EnumMap implements ArrayAccess, Countable, IteratorAggregate
26
{
27
    /**
28
     * The classname of the enumeration type
29
     * @var class-string<T>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
30
     */
31
    private $enumeration;
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line before member var; 0 found
Loading history...
32
33
    /**
34
     * Internal map of ordinal number and data value
35
     * @var array<int, mixed>
36
     */
37
    private $map = [];
38
39
    /**
40
     * Constructor
41
     * @param class-string<T> $enumeration The classname of the enumeration type
0 ignored issues
show
Coding Style introduced by
Expected 49 spaces after parameter type; 1 found
Loading history...
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
42
     * @param null|iterable<T|null|bool|int|float|string|array<mixed>, mixed> $map Initialize map
0 ignored issues
show
Coding Style introduced by
Expected 9 spaces after parameter name; 1 found
Loading history...
43
     * @throws InvalidArgumentException
0 ignored issues
show
introduced by
Comment missing for @throws tag in function comment
Loading history...
44
     */
45 21
    public function __construct(string $enumeration, iterable $map = null)
46
    {
47 21
        if (!\is_subclass_of($enumeration, Enum::class)) {
48 1
            throw new InvalidArgumentException(\sprintf(
49 1
                '%s can handle subclasses of %s only',
50 1
                 __CLASS__,
51 1
                Enum::class
52
            ));
53
        }
54 20
        $this->enumeration = $enumeration;
55
56 20
        if ($map) {
0 ignored issues
show
introduced by
$map is of type iterable|null, thus it always evaluated to false.
Loading history...
57 3
            $this->addIterable($map);
58
        }
59 20
    }
60
61
    /**
62
     * Add virtual private property "__pairs" with a list of key-value-pairs
63
     * to the result of var_dump.
64
     *
65
     * This helps debugging as internally the map is using the ordinal number.
66
     *
67
     * @return array<string, mixed>
68
     */
69 1
    public function __debugInfo(): array {
70 1
        $dbg = (array)$this;
71 1
        $dbg["\0" . self::class . "\0__pairs"] = array_map(function ($k, $v) {
72 1
            return [$k, $v];
73 1
        }, $this->getKeys(), $this->getValues());
74 1
        return $dbg;
75
    }
76
77
    /* write access (mutable) */
78
79
    /**
80
     * Adds the given enumerator (object or value) mapping to the specified data value.
81
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
Bug introduced by
The type MabeEnum\T was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
82
     * @param mixed                                                 $value
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 37 spaces after parameter type; 49 found
Loading history...
83
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
84
     * @see offsetSet()
85
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
86 13
    public function add($enumerator, $value): void
87
    {
88 13
        $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
89 12
        $this->map[$ord] = $value;
90 12
    }
91
92
    /**
93
     * Adds the given iterable, mapping enumerators (objects or values) to data values.
94
     * @param iterable<T|null|bool|int|float|string|array<mixed>, mixed> $map
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
95
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
96
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
97 5
    public function addIterable(iterable $map): void
98
    {
99 5
        $innerMap = $this->map;
100 5
        foreach ($map as $enumerator => $value) {
101 5
            $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
102 5
            $innerMap[$ord] = $value;
103
        }
104 5
        $this->map = $innerMap;
105 5
    }
106
107
    /**
108
     * Removes the given enumerator (object or value) mapping.
109
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
110
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
111
     * @see offsetUnset()
112
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
113 5
    public function remove($enumerator): void
114
    {
115 5
        $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
116 5
        unset($this->map[$ord]);
117 5
    }
118
119
    /**
120
     * Removes the given iterable enumerator (object or value) mappings.
121
     * @param iterable<T|null|bool|int|float|string|array<mixed>> $enumerators
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
122
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
123
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
124 3
    public function removeIterable(iterable $enumerators): void
125
    {
126 3
        $map = $this->map;
127 3
        foreach ($enumerators as $enumerator) {
128 3
            $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
129 3
            unset($map[$ord]);
130
        }
131
132 3
        $this->map = $map;
133 3
    }
134
135
    /* write access (immutable) */
136
137
    /**
138
     * Creates a new map with the given enumerator (object or value) mapping to the specified data value added.
139
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
140
     * @param mixed                                                 $value
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 37 spaces after parameter type; 49 found
Loading history...
141
     * @return static
142
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
143
     */
144 1
    public function with($enumerator, $value): self
145
    {
146 1
        $clone = clone $this;
147 1
        $clone->add($enumerator, $value);
148 1
        return $clone;
149
    }
150
151
    /**
152
     * Creates a new map with the given iterable mapping enumerators (objects or values) to data values added.
153
     * @param iterable<T|null|bool|int|float|string|array<mixed>, mixed> $map
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
154
     * @return static
155
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
156
     */
157 1
    public function withIterable(iterable $map): self
158
    {
159 1
        $clone = clone $this;
160 1
        $clone->addIterable($map);
161 1
        return $clone;
162
    }
163
164
    /**
165
     * Create a new map with the given enumerator mapping removed.
166
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
167
     * @return static
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 1
    public function without($enumerator): self
171
    {
172 1
        $clone = clone $this;
173 1
        $clone->remove($enumerator);
174 1
        return $clone;
175
    }
176
177
    /**
178
     * Creates a new map with the given iterable enumerator (object or value) mappings removed.
179
     * @param iterable<T|null|bool|int|float|string|array<mixed>> $enumerators
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
180
     * @return static
181
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
182
     */
183 1
    public function withoutIterable(iterable $enumerators): self
184
    {
185 1
        $clone = clone $this;
186 1
        $clone->removeIterable($enumerators);
187 1
        return $clone;
188
    }
189
190
    /* read access */
191
192
    /**
193
     * Get the classname of the enumeration type.
194
     * @return class-string<T>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
195
     */
196 1
    public function getEnumeration(): string
197
    {
198 1
        return $this->enumeration;
199
    }
200
201
    /**
202
     * Get the mapped data value of the given enumerator (object or value).
203
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
204
     * @return mixed
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
     * @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...
207
     * @see offsetGet()
208
     */
209 10
    public function get($enumerator)
210
    {
211 10
        $enumerator = ($this->enumeration)::get($enumerator);
212 10
        $ord = $enumerator->getOrdinal();
213 10
        if (!\array_key_exists($ord, $this->map)) {
214 2
            throw new UnexpectedValueException(sprintf(
215 2
                'Enumerator %s could not be found',
216 2
                \var_export($enumerator->getValue(), true)
217
            ));
218
        }
219
220 8
        return $this->map[$ord];
221
    }
222
223
    /**
224
     * Get a list of enumerator keys.
225
     * @return T[]
226
     *
227
     * @phpstan-return array<int, T>
228
     * @psalm-return list<T>
229
     */
230 8
    public function getKeys(): array
231
    {
232
        /** @var callable $byOrdinalFn */
233 8
        $byOrdinalFn = [$this->enumeration, 'byOrdinal'];
234
235 8
        return \array_map($byOrdinalFn, \array_keys($this->map));
236
    }
237
238
    /**
239
     * Get a list of mapped data values.
240
     * @return mixed[]
241
     *
242
     * @phpstan-return array<int, mixed>
243
     * @psalm-return list<mixed>
244
     */
245 8
    public function getValues(): array
246
    {
247 8
        return \array_values($this->map);
248
    }
249
250
    /**
251
     * Search for the given data value.
252
     * @param mixed $value
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
253
     * @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...
254
     * @return T|null The enumerator object of the first matching data value or NULL
255
     */
256 2
    public function search($value, bool $strict = false)
257
    {
258
        /** @var false|int $ord */
259 2
        $ord = \array_search($value, $this->map, $strict);
260 2
        if ($ord !== false) {
261 2
            return ($this->enumeration)::byOrdinal($ord);
262
        }
263
264 2
        return null;
265
    }
266
267
    /**
268
     * Test if the given enumerator key (object or value) exists.
269
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
270
     * @return bool
271
     * @see offsetExists()
272
     */
273 8
    public function has($enumerator): bool
274
    {
275
        try {
276 8
            $ord = ($this->enumeration)::get($enumerator)->getOrdinal();
277 7
            return \array_key_exists($ord, $this->map);
278 1
        } catch (InvalidArgumentException $e) {
279
            // An invalid enumerator can't be contained in this map
280 1
            return false;
281
        }
282
    }
283
284
    /**
285
     * Test if the given enumerator key (object or value) exists.
286
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
287
     * @return bool
288
     * @see offsetExists()
289
     * @see has()
290
     * @deprecated Will trigger deprecation warning in last 4.x and removed in 5.x
291
     */
292 1
    public function contains($enumerator): bool
293
    {
294 1
        return $this->has($enumerator);
295
    }
296
297
    /* ArrayAccess */
298
299
    /**
300
     * Test if the given enumerator key (object or value) exists and is not NULL
301
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
302
     * @return bool
303
     * @see contains()
304
     */
305 5
    public function offsetExists($enumerator): bool
306
    {
307
        try {
308 5
            return isset($this->map[($this->enumeration)::get($enumerator)->getOrdinal()]);
309 1
        } catch (InvalidArgumentException $e) {
310
            // An invalid enumerator can't be an offset of this map
311 1
            return false;
312
        }
313
    }
314
315
    /**
316
     * Get the mapped data value of the given enumerator (object or value).
317
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
318
     * @return mixed The mapped date value of the given enumerator or NULL
319
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
320
     * @see get()
321
     */
322 4
    public function offsetGet($enumerator)
323
    {
324
        try {
325 4
            return $this->get($enumerator);
326 1
        } catch (UnexpectedValueException $e) {
327 1
            return null;
328
        }
329
    }
330
331
    /**
332
     * Adds the given enumerator (object or value) mapping to the specified data value.
333
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
334
     * @param mixed                                     $value
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
335
     * @return void
336
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
337
     * @see add()
338
     */
339 7
    public function offsetSet($enumerator, $value = null): void
340
    {
341 7
        $this->add($enumerator, $value);
342 6
    }
343
344
    /**
345
     * Removes the given enumerator (object or value) mapping.
346
     * @param T|null|bool|int|float|string|array<mixed> $enumerator
0 ignored issues
show
Documentation introduced by
Missing parameter comment
Loading history...
347
     * @return void
348
     * @throws InvalidArgumentException On an invalid given enumerator
0 ignored issues
show
introduced by
@throws tag comment must end with a full stop
Loading history...
349
     * @see remove()
350
     */
351 2
    public function offsetUnset($enumerator): void
352
    {
353 2
        $this->remove($enumerator);
354 2
    }
355
356
    /* IteratorAggregate */
357
358
    /**
359
     * Get a new Iterator.
360
     *
361
     * @return Iterator<T, mixed> Iterator<K extends Enum, V>
362
     */
363 2
    public function getIterator(): Iterator
364
    {
365 2
        $map = $this->map;
366 2
        foreach ($map as $ordinal => $value) {
367 2
            yield ($this->enumeration)::byOrdinal($ordinal) => $value;
368
        }
369 2
    }
370
371
    /* Countable */
372
373
    /**
374
     * Count the number of elements
375
     *
376
     * @return int
377
     */
378 3
    public function count(): int
379
    {
380 3
        return \count($this->map);
381
    }
382
383
    /**
384
     * Tests if the map is empty
385
     *
386
     * @return bool
387
     */
388 1
    public function isEmpty(): bool
389
    {
390 1
        return empty($this->map);
391
    }
392
}
393