PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / dasprid / enum / src / EnumMap.php
wp-2fa / includes / classes / dasprid / enum / src Last commit date
Exception 1 day ago AbstractEnum.php 1 day ago EnumMap.php 1 day ago NullValue.php 1 day ago index.php 1 day ago
EnumMap.php
312 lines
1 <?php
2
3 declare (strict_types=1);
4 namespace WP2FA_Vendor\DASPRiD\Enum;
5
6 use WP2FA_Vendor\DASPRiD\Enum\Exception\ExpectationException;
7 use WP2FA_Vendor\DASPRiD\Enum\Exception\IllegalArgumentException;
8 use IteratorAggregate;
9 use Serializable;
10 use Traversable;
11 /**
12 * A specialized map implementation for use with enum type keys.
13 *
14 * All of the keys in an enum map must come from a single enum type that is specified, when the map is created. Enum
15 * maps are represented internally as arrays. This representation is extremely compact and efficient.
16 *
17 * Enum maps are maintained in the natural order of their keys (the order in which the enum constants are declared).
18 * This is reflected in the iterators returned by the collection views {@see self::getIterator()} and
19 * {@see self::values()}.
20 *
21 * Iterators returned by the collection views are not consistent: They may or may not show the effects of modifications
22 * to the map that occur while the iteration is in progress.
23 */
24 final class EnumMap implements IteratorAggregate
25 {
26 /**
27 * The class name of the key.
28 *
29 * @var string
30 */
31 private $keyType;
32 /**
33 * The type of the value.
34 *
35 * @var string
36 */
37 private $valueType;
38 /**
39 * @var bool
40 */
41 private $allowNullValues;
42 /**
43 * All of the constants comprising the enum, cached for performance.
44 *
45 * @var array<int, AbstractEnum>
46 */
47 private $keyUniverse;
48 /**
49 * Array representation of this map. The ith element is the value to which universe[i] is currently mapped, or null
50 * if it isn't mapped to anything, or NullValue if it's mapped to null.
51 *
52 * @var array<int, mixed>
53 */
54 private $values;
55 /**
56 * @var int
57 */
58 private $size = 0;
59 /**
60 * Creates a new enum map.
61 *
62 * @param string $keyType the type of the keys, must extend AbstractEnum
63 * @param string $valueType the type of the values
64 * @param bool $allowNullValues whether to allow null values
65 * @throws IllegalArgumentException when key type does not extend AbstractEnum
66 */
67 public function __construct(string $keyType, string $valueType, bool $allowNullValues)
68 {
69 if (!\is_subclass_of($keyType, AbstractEnum::class)) {
70 throw new IllegalArgumentException(\sprintf('Class %s does not extend %s', $keyType, AbstractEnum::class));
71 }
72 $this->keyType = $keyType;
73 $this->valueType = $valueType;
74 $this->allowNullValues = $allowNullValues;
75 $this->keyUniverse = $keyType::values();
76 $this->values = \array_fill(0, \count($this->keyUniverse), null);
77 }
78 public function __serialize() : array
79 {
80 $values = [];
81 foreach ($this->values as $ordinal => $value) {
82 if (null === $value) {
83 continue;
84 }
85 $values[$ordinal] = $this->unmaskNull($value);
86 }
87 return ['keyType' => $this->keyType, 'valueType' => $this->valueType, 'allowNullValues' => $this->allowNullValues, 'values' => $values];
88 }
89 public function __unserialize(array $data) : void
90 {
91 $this->__construct($data['keyType'], $data['valueType'], $data['allowNullValues']);
92 foreach ($this->keyUniverse as $key) {
93 if (\array_key_exists($key->ordinal(), $data['values'])) {
94 $this->put($key, $data['values'][$key->ordinal()]);
95 }
96 }
97 }
98 /**
99 * Checks whether the map types match the supplied ones.
100 *
101 * You should call this method when an EnumMap is passed to you and you want to ensure that it's made up of the
102 * correct types.
103 *
104 * @throws ExpectationException when supplied key type mismatches local key type
105 * @throws ExpectationException when supplied value type mismatches local value type
106 * @throws ExpectationException when the supplied map allows null values, abut should not
107 */
108 public function expect(string $keyType, string $valueType, bool $allowNullValues) : void
109 {
110 if ($keyType !== $this->keyType) {
111 throw new ExpectationException(\sprintf('Callee expected an EnumMap with key type %s, but got %s', $keyType, $this->keyType));
112 }
113 if ($valueType !== $this->valueType) {
114 throw new ExpectationException(\sprintf('Callee expected an EnumMap with value type %s, but got %s', $keyType, $this->keyType));
115 }
116 if ($allowNullValues !== $this->allowNullValues) {
117 throw new ExpectationException(\sprintf('Callee expected an EnumMap with nullable flag %s, but got %s', $allowNullValues ? 'true' : 'false', $this->allowNullValues ? 'true' : 'false'));
118 }
119 }
120 /**
121 * Returns the number of key-value mappings in this map.
122 */
123 public function size() : int
124 {
125 return $this->size;
126 }
127 /**
128 * Returns true if this map maps one or more keys to the specified value.
129 */
130 public function containsValue($value) : bool
131 {
132 return \in_array($this->maskNull($value), $this->values, \true);
133 }
134 /**
135 * Returns true if this map contains a mapping for the specified key.
136 */
137 public function containsKey(AbstractEnum $key) : bool
138 {
139 $this->checkKeyType($key);
140 return null !== $this->values[$key->ordinal()];
141 }
142 /**
143 * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
144 *
145 * More formally, if this map contains a mapping from a key to a value, then this method returns the value;
146 * otherwise it returns null (there can be at most one such mapping).
147 *
148 * A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also
149 * possible that hte map explicitly maps the key to null. The {@see self::containsKey()} operation may be used to
150 * distinguish these two cases.
151 *
152 * @return mixed
153 */
154 public function get(AbstractEnum $key)
155 {
156 $this->checkKeyType($key);
157 return $this->unmaskNull($this->values[$key->ordinal()]);
158 }
159 /**
160 * Associates the specified value with the specified key in this map.
161 *
162 * If the map previously contained a mapping for this key, the old value is replaced.
163 *
164 * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key.
165 * (a null return can also indicate that the map previously associated null with the specified key.)
166 * @throws IllegalArgumentException when the passed values does not match the internal value type
167 */
168 public function put(AbstractEnum $key, $value)
169 {
170 $this->checkKeyType($key);
171 if (!$this->isValidValue($value)) {
172 throw new IllegalArgumentException(\sprintf('Value is not of type %s', $this->valueType));
173 }
174 $index = $key->ordinal();
175 $oldValue = $this->values[$index];
176 $this->values[$index] = $this->maskNull($value);
177 if (null === $oldValue) {
178 ++$this->size;
179 }
180 return $this->unmaskNull($oldValue);
181 }
182 /**
183 * Removes the mapping for this key frm this map if present.
184 *
185 * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key.
186 * (a null return can also indicate that the map previously associated null with the specified key.)
187 */
188 public function remove(AbstractEnum $key)
189 {
190 $this->checkKeyType($key);
191 $index = $key->ordinal();
192 $oldValue = $this->values[$index];
193 $this->values[$index] = null;
194 if (null !== $oldValue) {
195 --$this->size;
196 }
197 return $this->unmaskNull($oldValue);
198 }
199 /**
200 * Removes all mappings from this map.
201 */
202 public function clear() : void
203 {
204 $this->values = \array_fill(0, \count($this->keyUniverse), null);
205 $this->size = 0;
206 }
207 /**
208 * Compares the specified map with this map for quality.
209 *
210 * Returns true if the two maps represent the same mappings.
211 */
212 public function equals(self $other) : bool
213 {
214 if ($this === $other) {
215 return \true;
216 }
217 if ($this->size !== $other->size) {
218 return \false;
219 }
220 return $this->values === $other->values;
221 }
222 /**
223 * Returns the values contained in this map.
224 *
225 * The array will contain the values in the order their corresponding keys appear in the map, which is their natural
226 * order (the order in which the num constants are declared).
227 */
228 public function values() : array
229 {
230 return \array_values(\array_map(function ($value) {
231 return $this->unmaskNull($value);
232 }, \array_filter($this->values, function ($value) : bool {
233 return null !== $value;
234 })));
235 }
236 public function serialize() : string
237 {
238 return \serialize($this->__serialize());
239 }
240 public function unserialize($serialized) : void
241 {
242 $data = \unserialize($serialized);
243 $this->__construct($data['keyType'], $data['valueType'], $data['allowNullValues']);
244 foreach ($this->keyUniverse as $key) {
245 if (\array_key_exists($key->ordinal(), $data['values'])) {
246 $this->put($key, $data['values'][$key->ordinal()]);
247 }
248 }
249 }
250 public function getIterator() : Traversable
251 {
252 foreach ($this->keyUniverse as $key) {
253 if (null === $this->values[$key->ordinal()]) {
254 continue;
255 }
256 (yield $key => $this->unmaskNull($this->values[$key->ordinal()]));
257 }
258 }
259 private function maskNull($value)
260 {
261 if (null === $value) {
262 return NullValue::instance();
263 }
264 return $value;
265 }
266 private function unmaskNull($value)
267 {
268 if ($value instanceof NullValue) {
269 return null;
270 }
271 return $value;
272 }
273 /**
274 * @throws IllegalArgumentException when the passed key does not match the internal key type
275 */
276 private function checkKeyType(AbstractEnum $key) : void
277 {
278 if (\get_class($key) !== $this->keyType) {
279 throw new IllegalArgumentException(\sprintf('Object of type %s is not the same type as %s', \get_class($key), $this->keyType));
280 }
281 }
282 private function isValidValue($value) : bool
283 {
284 if (null === $value) {
285 if ($this->allowNullValues) {
286 return \true;
287 }
288 return \false;
289 }
290 switch ($this->valueType) {
291 case 'mixed':
292 return \true;
293 case 'bool':
294 case 'boolean':
295 return \is_bool($value);
296 case 'int':
297 case 'integer':
298 return \is_int($value);
299 case 'float':
300 case 'double':
301 return \is_float($value);
302 case 'string':
303 return \is_string($value);
304 case 'object':
305 return \is_object($value);
306 case 'array':
307 return \is_array($value);
308 }
309 return $value instanceof $this->valueType;
310 }
311 }
312