PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.2
WP 2FA – Two-factor authentication for WordPress v2.9.2
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 10 months ago AbstractEnum.php 10 months ago EnumMap.php 10 months ago NullValue.php 10 months ago
EnumMap.php
307 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 Serializable, 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->unserialize(\serialize($data));
92 }
93 /**
94 * Checks whether the map types match the supplied ones.
95 *
96 * You should call this method when an EnumMap is passed to you and you want to ensure that it's made up of the
97 * correct types.
98 *
99 * @throws ExpectationException when supplied key type mismatches local key type
100 * @throws ExpectationException when supplied value type mismatches local value type
101 * @throws ExpectationException when the supplied map allows null values, abut should not
102 */
103 public function expect(string $keyType, string $valueType, bool $allowNullValues) : void
104 {
105 if ($keyType !== $this->keyType) {
106 throw new ExpectationException(\sprintf('Callee expected an EnumMap with key type %s, but got %s', $keyType, $this->keyType));
107 }
108 if ($valueType !== $this->valueType) {
109 throw new ExpectationException(\sprintf('Callee expected an EnumMap with value type %s, but got %s', $keyType, $this->keyType));
110 }
111 if ($allowNullValues !== $this->allowNullValues) {
112 throw new ExpectationException(\sprintf('Callee expected an EnumMap with nullable flag %s, but got %s', $allowNullValues ? 'true' : 'false', $this->allowNullValues ? 'true' : 'false'));
113 }
114 }
115 /**
116 * Returns the number of key-value mappings in this map.
117 */
118 public function size() : int
119 {
120 return $this->size;
121 }
122 /**
123 * Returns true if this map maps one or more keys to the specified value.
124 */
125 public function containsValue($value) : bool
126 {
127 return \in_array($this->maskNull($value), $this->values, \true);
128 }
129 /**
130 * Returns true if this map contains a mapping for the specified key.
131 */
132 public function containsKey(AbstractEnum $key) : bool
133 {
134 $this->checkKeyType($key);
135 return null !== $this->values[$key->ordinal()];
136 }
137 /**
138 * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
139 *
140 * More formally, if this map contains a mapping from a key to a value, then this method returns the value;
141 * otherwise it returns null (there can be at most one such mapping).
142 *
143 * A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also
144 * possible that hte map explicitly maps the key to null. The {@see self::containsKey()} operation may be used to
145 * distinguish these two cases.
146 *
147 * @return mixed
148 */
149 public function get(AbstractEnum $key)
150 {
151 $this->checkKeyType($key);
152 return $this->unmaskNull($this->values[$key->ordinal()]);
153 }
154 /**
155 * Associates the specified value with the specified key in this map.
156 *
157 * If the map previously contained a mapping for this key, the old value is replaced.
158 *
159 * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key.
160 * (a null return can also indicate that the map previously associated null with the specified key.)
161 * @throws IllegalArgumentException when the passed values does not match the internal value type
162 */
163 public function put(AbstractEnum $key, $value)
164 {
165 $this->checkKeyType($key);
166 if (!$this->isValidValue($value)) {
167 throw new IllegalArgumentException(\sprintf('Value is not of type %s', $this->valueType));
168 }
169 $index = $key->ordinal();
170 $oldValue = $this->values[$index];
171 $this->values[$index] = $this->maskNull($value);
172 if (null === $oldValue) {
173 ++$this->size;
174 }
175 return $this->unmaskNull($oldValue);
176 }
177 /**
178 * Removes the mapping for this key frm this map if present.
179 *
180 * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key.
181 * (a null return can also indicate that the map previously associated null with the specified key.)
182 */
183 public function remove(AbstractEnum $key)
184 {
185 $this->checkKeyType($key);
186 $index = $key->ordinal();
187 $oldValue = $this->values[$index];
188 $this->values[$index] = null;
189 if (null !== $oldValue) {
190 --$this->size;
191 }
192 return $this->unmaskNull($oldValue);
193 }
194 /**
195 * Removes all mappings from this map.
196 */
197 public function clear() : void
198 {
199 $this->values = \array_fill(0, \count($this->keyUniverse), null);
200 $this->size = 0;
201 }
202 /**
203 * Compares the specified map with this map for quality.
204 *
205 * Returns true if the two maps represent the same mappings.
206 */
207 public function equals(self $other) : bool
208 {
209 if ($this === $other) {
210 return \true;
211 }
212 if ($this->size !== $other->size) {
213 return \false;
214 }
215 return $this->values === $other->values;
216 }
217 /**
218 * Returns the values contained in this map.
219 *
220 * The array will contain the values in the order their corresponding keys appear in the map, which is their natural
221 * order (the order in which the num constants are declared).
222 */
223 public function values() : array
224 {
225 return \array_values(\array_map(function ($value) {
226 return $this->unmaskNull($value);
227 }, \array_filter($this->values, function ($value) : bool {
228 return null !== $value;
229 })));
230 }
231 public function serialize() : string
232 {
233 return \serialize($this->__serialize());
234 }
235 public function unserialize($serialized) : void
236 {
237 $data = \unserialize($serialized);
238 $this->__construct($data['keyType'], $data['valueType'], $data['allowNullValues']);
239 foreach ($this->keyUniverse as $key) {
240 if (\array_key_exists($key->ordinal(), $data['values'])) {
241 $this->put($key, $data['values'][$key->ordinal()]);
242 }
243 }
244 }
245 public function getIterator() : Traversable
246 {
247 foreach ($this->keyUniverse as $key) {
248 if (null === $this->values[$key->ordinal()]) {
249 continue;
250 }
251 (yield $key => $this->unmaskNull($this->values[$key->ordinal()]));
252 }
253 }
254 private function maskNull($value)
255 {
256 if (null === $value) {
257 return NullValue::instance();
258 }
259 return $value;
260 }
261 private function unmaskNull($value)
262 {
263 if ($value instanceof NullValue) {
264 return null;
265 }
266 return $value;
267 }
268 /**
269 * @throws IllegalArgumentException when the passed key does not match the internal key type
270 */
271 private function checkKeyType(AbstractEnum $key) : void
272 {
273 if (\get_class($key) !== $this->keyType) {
274 throw new IllegalArgumentException(\sprintf('Object of type %s is not the same type as %s', \get_class($key), $this->keyType));
275 }
276 }
277 private function isValidValue($value) : bool
278 {
279 if (null === $value) {
280 if ($this->allowNullValues) {
281 return \true;
282 }
283 return \false;
284 }
285 switch ($this->valueType) {
286 case 'mixed':
287 return \true;
288 case 'bool':
289 case 'boolean':
290 return \is_bool($value);
291 case 'int':
292 case 'integer':
293 return \is_int($value);
294 case 'float':
295 case 'double':
296 return \is_float($value);
297 case 'string':
298 return \is_string($value);
299 case 'object':
300 return \is_object($value);
301 case 'array':
302 return \is_array($value);
303 }
304 return $value instanceof $this->valueType;
305 }
306 }
307