Passed
Branch master (5a719b)
by Marc
03:59
created

EnumMap::search()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 2
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
namespace MabeEnum;
4
5
use ArrayAccess;
6
use Countable;
7
use InvalidArgumentException;
8
use OutOfBoundsException;
9
use SeekableIterator;
10
use UnexpectedValueException;
11
12
/**
13
 * A map of enumerators (EnumMap<T>) and mixed values.
14
 *
15
 * @link http://github.com/marc-mabe/php-enum for the canonical source repository
16
 * @copyright Copyright (c) 2017 Marc Bennewitz
17
 * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
18
 */
19
class EnumMap implements ArrayAccess, Countable, SeekableIterator
20
{
21
    /**
22
     * The classname of the enumeration type
23
     * @var string
24
     */
25
    private $enumeration;
26
27
    /**
28
     * Internal map of ordinal number and value
29
     * @var array
30
     */
31
    private $map = [];
32
33
    /**
34
     * List of ordinal numbers
35
     * @var int[]
36
     */
37
    private $ordinals = [];
38
39
    /**
40
     * Current iterator position
41
     * @var int
42
     */
43
    private $pos = 0;
44
45
    /**
46
     * Constructor
47
     * @param string $enumeration The classname of the enumeration type
48
     * @throws InvalidArgumentException
49
     */
50 42
    public function __construct($enumeration)
51
    {
52 42
        if (!\is_subclass_of($enumeration, Enum::class)) {
53 3
            throw new InvalidArgumentException(\sprintf(
54 3
                "This EnumMap can handle subclasses of '%s' only",
55 2
                Enum::class
56 1
            ));
57
        }
58 39
        $this->enumeration = $enumeration;
59 39
    }
60
61
    /**
62
     * Get the classname of the enumeration
63
     * @return string
64
     */
65 3
    public function getEnumeration()
66
    {
67 3
        return $this->enumeration;
68
    }
69
70
    /**
71
     * Get a list of map keys
72
     * @return Enum[]
73
     */
74 9
    public function getKeys()
75
    {
76 9
        return \array_map([$this->enumeration, 'byOrdinal'], $this->ordinals);
77
    }
78
79
    /**
80
     * Get a list of map values
81
     * @return mixed[]
82
     */
83 6
    public function getValues()
84
    {
85 6
        return \array_values($this->map);
86
    }
87
88
    /**
89
     * Search for the given value
90
     * @param mixed $value
91
     * @param bool $strict Use strict type comparison
92
     * @return Enum|null The found key or NULL
93
     */
94 6
    public function search($value, $strict = false)
95
    {
96 6
        $ord = \array_search($value, $this->map, $strict);
97 6
        if ($ord !== false) {
98 6
            $enumeration = $this->enumeration;
99 6
            return $enumeration::byOrdinal($ord);
100
        }
101
102 6
        return null;
103
    }
104
105
    /**
106
     * Test if the given enumerator exists
107
     * @param Enum|null|boolean|int|float|string $enumerator
108
     * @return boolean
109
     * @see offsetExists
110
     */
111 12 View Code Duplication
    public function contains($enumerator)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
112
    {
113
        try {
114 12
            $enumeration = $this->enumeration;
115 12
            $ord  = $enumeration::get($enumerator)->getOrdinal();
116 9
            return array_key_exists($ord, $this->map);
117 3
        } catch (InvalidArgumentException $e) {
118
            // An invalid enumerator can't be contained in this map
119 3
            return false;
120
        }
121
    }
122
123
    /**
124
     * Test if the given enumerator key exists and is not NULL
125
     * @param Enum|null|boolean|int|float|string $enumerator
126
     * @return boolean
127
     * @see contains
128
     */
129 15 View Code Duplication
    public function offsetExists($enumerator)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130
    {
131
        try {
132 15
            $enumeration = $this->enumeration;
133 15
            $ord  = $enumeration::get($enumerator)->getOrdinal();
134 12
            return isset($this->map[$ord]);
135 3
        } catch (InvalidArgumentException $e) {
136
            // An invalid enumerator can't be an offset of this map
137 3
            return false;
138
        }
139
    }
140
141
    /**
142
     * Get mapped data for the given enumerator
143
     * @param Enum|null|boolean|int|float|string $enumerator
144
     * @return mixed
145
     * @throws InvalidArgumentException On an invalid given enumerator
146
     */
147 18
    public function offsetGet($enumerator)
148
    {
149 18
        $enumeration = $this->enumeration;
150 18
        $ord = $enumeration::get($enumerator)->getOrdinal();
151 18
        if (!isset($this->map[$ord]) && !array_key_exists($ord, $this->map)) {
152 3
            throw new UnexpectedValueException(\sprintf(
153 3
                "Enumerator '%s' could not be found",
154 3
                \is_object($enumerator) ? $enumerator->getValue() : $enumerator
155 1
            ));
156
        }
157
158 15
        return $this->map[$ord];
159
    }
160
161
    /**
162
     * Attach a new enumerator or overwrite an existing one
163
     * @param Enum|null|boolean|int|float|string $enumerator
164
     * @param mixed                              $value
165
     * @return void
166
     * @throws InvalidArgumentException On an invalid given enumerator
167
     * @see attach()
168
     */
169 33
    public function offsetSet($enumerator, $value = null)
170
    {
171 33
        $enumeration = $this->enumeration;
172 33
        $ord = $enumeration::get($enumerator)->getOrdinal();
173
174 30
        if (!array_key_exists($ord, $this->map)) {
175 30
            $this->ordinals[] = $ord;
176 10
        }
177 30
        $this->map[$ord] = $value;
178 30
    }
179
180
    /**
181
     * Detach an existing enumerator
182
     * @param Enum|null|boolean|int|float|string $enumerator
183
     * @return void
184
     * @throws InvalidArgumentException On an invalid given enumerator
185
     * @see detach()
186
     */
187 12
    public function offsetUnset($enumerator)
188
    {
189 12
        $enumeration = $this->enumeration;
190 12
        $ord = $enumeration::get($enumerator)->getOrdinal();
191
192 12
        if (($idx = \array_search($ord, $this->ordinals, true)) !== false) {
193 12
            unset($this->map[$ord], $this->ordinals[$idx]);
194 12
            $this->ordinals = \array_values($this->ordinals);
195 4
        }
196 12
    }
197
198
    /**
199
     * Seeks to the given iterator position.
200
     * @param int $pos
201
     */
202 3
    public function seek($pos)
203
    {
204 3
        $pos = (int)$pos;
205 3
        if (!isset($this->ordinals[$pos])) {
206 3
            throw new OutOfBoundsException("Position {$pos} not found");
207
        }
208
209 3
        $this->pos = $pos;
210 3
    }
211
212
    /**
213
     * Get the current value
214
     * @return mixed
215
     */
216 6
    public function current()
217
    {
218 6
        if (!isset($this->ordinals[$this->pos])) {
219 3
            return null;
220
        }
221
222 6
        return $this->map[$this->ordinals[$this->pos]];
223
    }
224
225
    /**
226
     * Get the current key
227
     * @return Enum|null
228
     */
229 12
    public function key()
230
    {
231 12
        if (!isset($this->ordinals[$this->pos])) {
232 3
            return null;
233
        }
234
235 12
        $enumeration = $this->enumeration;
236 12
        return $enumeration::byOrdinal($this->ordinals[$this->pos]);
237
    }
238
239
    /**
240
     * Reset the iterator position to zero.
241
     * @return void
242
     */
243 6
    public function rewind()
244
    {
245 6
        $this->pos = 0;
246 6
    }
247
248
    /**
249
     * Increment the iterator position by one.
250
     * @return void
251
     */
252 3
    public function next()
253
    {
254 3
        ++$this->pos;
255 3
    }
256
257
    /**
258
     * Test if the iterator is in a valid state
259
     * @return boolean
260
     */
261 6
    public function valid()
262
    {
263 6
        return isset($this->ordinals[$this->pos]);
264
    }
265
266
    /**
267
     * Count the number of elements
268
     *
269
     * @return int
270
     */
271 6
    public function count()
272
    {
273 6
        return \count($this->ordinals);
274
    }
275
}
276