Completed
Push — refactor_EnumMap ( 1d0ac6...de90b7 )
by Marc
01:25
created

EnumMap::next()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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 26
    public function __construct($enumeration)
51
    {
52 26
        if (!\is_subclass_of($enumeration, Enum::class)) {
53 2
            throw new InvalidArgumentException(\sprintf(
54 2
                "This EnumMap can handle subclasses of '%s' only",
55 1
                Enum::class
56 1
            ));
57
        }
58 24
        $this->enumeration = $enumeration;
59 24
    }
60
61
    /**
62
     * Get the classname of the enumeration
63
     * @return string
64
     */
65 2
    public function getEnumeration()
66
    {
67 2
        return $this->enumeration;
68
    }
69
70
    /**
71
     * Get a list of map keys
72
     * @return Enum[]
73
     */
74 6
    public function getKeys()
75
    {
76 6
        return \array_map([$this->enumeration, 'byOrdinal'], $this->ordinals);
77
    }
78
79
    /**
80
     * Get a list of map values
81
     * @return mixed[]
82
     */
83 4
    public function getValues()
84
    {
85 4
        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 4
    public function search($value, $strict = false)
95
    {
96 4
        $ord = \array_search($value, $this->map, $strict);
97 4
        if ($ord !== false) {
98 4
            $enumeration = $this->enumeration;
99 4
            return $enumeration::byOrdinal($ord);
100
        }
101
102 4
        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 8 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 8
            $enumeration = $this->enumeration;
115 8
            $ord  = $enumeration::get($enumerator)->getOrdinal();
116 6
            return array_key_exists($ord, $this->map);
117 2
        } catch (InvalidArgumentException $e) {
118
            // An invalid enumerator can't be contained in this map
119 2
            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 10 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 10
            $enumeration = $this->enumeration;
133 10
            $ord  = $enumeration::get($enumerator)->getOrdinal();
134 8
            return isset($this->map[$ord]);
135 2
        } catch (InvalidArgumentException $e) {
136
            // An invalid enumerator can't be an offset of this map
137 2
            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 8
    public function offsetGet($enumerator)
148
    {
149 8
        $enumeration = $this->enumeration;
150 8
        $ord = $enumeration::get($enumerator)->getOrdinal();
151 8
        if (!isset($this->map[$ord])) {
152
            throw new UnexpectedValueException(\sprintf(
153
                "Enumerator '%s' could not be found",
154
                \is_object($enumerator) ? $enumerator->getValue() : $enumerator
155
            ));
156
        }
157
158 8
        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                              $data
0 ignored issues
show
Bug introduced by
There is no parameter named $data. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
165
     * @return void
166
     * @throws InvalidArgumentException On an invalid given enumerator
167
     * @see attach()
168
     */
169 22
    public function offsetSet($enumerator, $value = null)
170
    {
171 22
        $enumeration = $this->enumeration;
172 22
        $ord = $enumeration::get($enumerator)->getOrdinal();
173
174 20
        if (!array_key_exists($ord, $this->map)) {
175 20
            $this->ordinals[] = $ord;
176 10
        }
177 20
        $this->map[$ord] = $value;
178 20
    }
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 8
    public function offsetUnset($enumerator)
188
    {
189 8
        $enumeration = $this->enumeration;
190 8
        $ord = $enumeration::get($enumerator)->getOrdinal();
191
192 8
        if (($idx = \array_search($ord, $this->ordinals, true)) !== false) {
193 8
            unset($this->map[$ord], $this->ordinals[$idx]);
194 8
            $this->ordinals = \array_values($this->ordinals);
195 4
        }
196 8
    }
197
198
    /**
199
     * Seeks to the given iterator position.
200
     * @param int $pos
201
     */
202 2
    public function seek($pos)
203
    {
204 2
        $pos = (int)$pos;
205 2
        if (!isset($this->ordinals[$pos])) {
206 2
            throw new OutOfBoundsException("Position {$pos} not found");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $pos instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
207
        }
208
209 2
        $this->pos = $pos;
210 2
    }
211
212
    /**
213
     * Get the current value
214
     * @return mixed
215
     */
216 4
    public function current()
217
    {
218 4
        if (!isset($this->ordinals[$this->pos])) {
219
            return null;
220
        }
221
222 4
        return $this->map[$this->ordinals[$this->pos]];
223
    }
224
225
    /**
226
     * Get the current key
227
     * @return Enum|null
228
     */
229 8
    public function key()
230
    {
231 8
        if (!isset($this->ordinals[$this->pos])) {
232 2
            return null;
233
        }
234
235 8
        $enumeration = $this->enumeration;
236 8
        return $enumeration::byOrdinal($this->ordinals[$this->pos]);
237
    }
238
239
    /**
240
     * Reset the iterator position to zero.
241
     * @return void
242
     */
243 4
    public function rewind()
244
    {
245 4
        $this->pos = 0;
246 4
    }
247
248
    /**
249
     * Increment the iterator position by one.
250
     * @return void
251
     */
252 2
    public function next()
253
    {
254 2
        ++$this->pos;
255 2
    }
256
257
    /**
258
     * Test if the iterator is in a valid state
259
     * @return boolean
260
     */
261 4
    public function valid()
262
    {
263 4
        return isset($this->ordinals[$this->pos]);
264
    }
265
266
    /**
267
     * Count the number of elements
268
     *
269
     * @return int
270
     */
271 4
    public function count()
272
    {
273 4
        return \count($this->ordinals);
274
    }
275
}
276