Passed
Push — master ( a95f40...077740 )
by Domenico
02:21
created

Map   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 363
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 75
c 1
b 0
f 0
dl 0
loc 363
rs 8.48
wmc 49

29 Methods

Rating   Name   Duplication   Size   Complexity  
A values() 0 2 1
A compute() 0 12 3
A offsetSet() 0 2 1
A replace() 0 8 2
A foreEach() 0 3 2
A size() 0 2 1
A instance() 0 2 1
A __construct() 0 2 1
A remove() 0 7 2
A entrySet() 0 3 2
A getIterator() 0 2 1
A getOrDefault() 0 2 2
A putIfAbsent() 0 7 2
A __clone() 0 2 1
A keySet() 0 2 1
A put() 0 5 1
A offsetGet() 0 2 1
A computeIfPresent() 0 14 5
A putAll() 0 3 2
A containsValue() 0 2 1
A offsetExists() 0 2 1
A get() 0 2 1
A containsKey() 0 2 1
A isEmpty() 0 2 1
A replaceIfEquals() 0 10 3
A offsetUnset() 0 2 1
A replaceAll() 0 3 2
A computeIfAbsent() 0 13 5
A clear() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like Map often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Map, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * @author hashashiyyin [email protected] / [email protected]
5
 * Date: 24/05/24
6
 * Time: 13:41
7
 *
8
 */
9
10
namespace Matecat\SubFiltering\Utils;
11
12
use ArrayAccess;
13
use ArrayIterator;
14
use Generator;
15
use IteratorAggregate;
16
17
/**
18
 * Java like interface helper for key/value php arrays
19
 *
20
 */
21
class Map implements IteratorAggregate, ArrayAccess {
22
23
    /**
24
     * @var array
25
     */
26
    private $map;
27
28
    public function __construct( array $map ) {
29
        $this->map = $map;
30
    }
31
32
    /**
33
     * @return ArrayIterator
34
     */
35
    public function getIterator() {
36
        return new ArrayIterator( $this->map );
37
    }
38
39
    /**
40
     * @param $offset
41
     *
42
     * @return bool
43
     */
44
    public function offsetExists( $offset ) {
45
        return array_key_exists( $offset, $this->map );
46
    }
47
48
    /**
49
     * @param $offset
50
     *
51
     * @return mixed|null
52
     */
53
    public function offsetGet( $offset ) {
54
        return $this->get( $offset );
55
    }
56
57
    /**
58
     * @param $offset
59
     * @param $value
60
     *
61
     * @return void
62
     */
63
    public function offsetSet( $offset, $value ) {
64
        $this->put( $offset, $value );
65
    }
66
67
    /**
68
     * @param $offset
69
     *
70
     * @return void
71
     */
72
    public function offsetUnset( $offset ) {
73
        $this->remove( $offset );
74
    }
75
76
77
    /**
78
     * @return Generator
79
     */
80
    public function entrySet() {
81
        foreach ( $this->map as $k => $v ) {
82
            yield [ $k, $v ];
83
        }
84
    }
85
86
    public static function instance( array $map = [] ) {
87
        return new static( $map );
88
    }
89
90
    /**
91
     * Safe key access. Avoid warnings.
92
     *
93
     * @param       $needle
94
     *
95
     * @return mixed|null
96
     */
97
    public function get( $needle ) {
98
        return $this->getOrDefault( $needle, null );
99
    }
100
101
    /**
102
     * @param $needle
103
     * @param $default
104
     *
105
     * @return mixed
106
     */
107
    public function getOrDefault( $needle, $default ) {
108
        return array_key_exists( $needle, $this->map ) ? $this->map [ $needle ] : $default;
109
    }
110
111
    /**
112
     * Removes all the mappings from this map.
113
     * @return void
114
     */
115
    public function clear() {
116
        $this->map = [];
117
    }
118
119
    /**
120
     * Returns a shallow copy of this HashMap instance: the keys and values themselves are not cloned.
121
     * @return $this
122
     */
123
    public function __clone() {
124
        return new static( $this->map );
125
    }
126
127
    public function containsKey( $key ) {
128
        return array_key_exists( $key, $this->map );
129
    }
130
131
    /**
132
     * Returns true if this map maps one or more keys to the specified value.
133
     * If the value is found more than once, the first matching key is returned.
134
     *
135
     * @param $value
136
     *
137
     * @return false|string
138
     */
139
    public function containsValue( $value ) {
140
        return array_search( $value, $this->map, true );
141
    }
142
143
    /**
144
     * Performs the given action for each entry in this map until all entries have been processed or the action throws an exception.
145
     * Actions are performed in the order of entry set iteration.
146
     *
147
     * @param callable $callable
148
     *
149
     * @return Generator
150
     */
151
    public function foreEach( callable $callable ) {
152
        foreach ( $this->map as $k => $v ) {
153
            yield $callable( $k, $v );
154
        }
155
    }
156
157
    /**
158
     * Returns true if this map contains no key-value mappings.
159
     *
160
     * @return bool
161
     */
162
    public function isEmpty() {
163
        return empty( $this->map );
164
    }
165
166
    /**
167
     * @return Map
168
     */
169
    public function keySet() {
170
        return static::instance( array_keys( $this->map ) );
171
    }
172
173
    /**
174
     * Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced.
175
     *
176
     * @param $key
177
     * @param $value
178
     *
179
     * @return mixed|null the previous value associated with `key`, or null if there was no mapping for `key`.
180
     */
181
    public function put( $key, $value ) {
182
        $previousValue     = $this->get( $key );
183
        $this->map[ $key ] = $value;
184
185
        return $previousValue;
186
    }
187
188
    /**
189
     * @param $map
190
     *
191
     * @return void
192
     */
193
    public function putAll( $map ) {
194
        foreach ( $map as $k => $v ) {
195
            $this->map[ $k ] = $v;
196
        }
197
    }
198
199
    /**
200
     * If the specified key is not already associated with a value (or is mapped to null) associates it with the given value and returns null, else returns the current value.
201
     *
202
     * @param $key
203
     * @param $value
204
     *
205
     * @return mixed|null the previous value associated with the specified key, or null if there was no mapping for the key.
206
     *               A null return can also indicate that the map previously associated null with the `key`
207
     */
208
    public function putIfAbsent( $key, $value ) {
209
        $previousValue = $this->get( $key );
210
        if ( $previousValue === null ) {
211
            $this->map[ $key ] = $value;
212
        }
213
214
        return $previousValue;
215
    }
216
217
    /**
218
     * Removes the mapping for the specified key from this map if present.
219
     *
220
     * @param $key
221
     *
222
     * @return bool true if the value was removed
223
     */
224
    public function remove( $key ) {
225
        $exists = array_key_exists( $key, $this->map );
226
        if ( $exists ) {
227
            unset( $this->map[ $key ] );
228
        }
229
230
        return $exists;
231
    }
232
233
    /**
234
     * Replaces the entry for the specified key only if it is currently mapped to some value.
235
     *
236
     * @return mixed|null the previous value associated with the specified key, or null if there was no mapping for the key.
237
     *                      A null return can also indicate that the map previously associated null with the key.
238
     */
239
    public function replace( $key, $value ) {
240
        $exists        = array_key_exists( $key, $this->map );
241
        $previousValue = $this->get( $key );
242
        if ( $exists ) {
243
            $this->map[ $key ] = $value;
244
        }
245
246
        return $previousValue;
247
    }
248
249
    /**
250
     * Replaces the entry for the specified key only if currently mapped to the specified value.
251
     *
252
     * @param $key
253
     * @param $newValue
254
     * @param $oldValue
255
     *
256
     * @return boolean true if the value was replaced
257
     */
258
    public function replaceIfEquals( $key, $newValue, $oldValue ) {
259
        $exists        = array_key_exists( $key, $this->map );
260
        $previousValue = $this->get( $key );
261
        if ( $exists && $previousValue === $oldValue ) {
262
            $this->map[ $key ] = $newValue;
263
264
            return true;
265
        }
266
267
        return false;
268
    }
269
270
    /**
271
     * Replaces each entry's value with the result of invoking the given function on that entry until all entries have been processed,
272
     * or the function throws an exception.
273
     * Exceptions thrown by the function are relayed to the caller.
274
     *
275
     * @param callable $callable
276
     *
277
     * @return void
278
     */
279
    public function replaceAll( callable $callable ) {
280
        foreach ( $this->map as $key => $value ) {
281
            $this->map[ $key ] = $callable( $key, $value );
282
        }
283
    }
284
285
    /**
286
     * @return int
287
     */
288
    public function size() {
289
        return sizeof( $this->map );
290
    }
291
292
    /**
293
     * @return Map
294
     */
295
    public function values() {
296
        return new static( array_values( $this->map ) );
297
    }
298
299
    /**
300
     * Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping).
301
     * For example, to either create or append a String msg to a value mapping:
302
     * <code>
303
     *     $_empty = 'EMPTY';
304
     *     $map->compute( "foo", function( $k, $v ) use ( $_empty ) { ($v == null) ? $_empty : 'NO MORE ' . $_empty  } );
305
     * </code>
306
     *
307
     * If the function returns null, the mapping is removed (or remains absent if initially absent).
308
     * If the function itself throws an (unchecked) exception, the exception is rethrown, and the current mapping is left unchanged.
309
     *
310
     * @param          $key
311
     * @param callable $callable
312
     *
313
     * @return mixed|null the new value associated with the specified key, or null if none
314
     */
315
    public function compute( $key, callable $callable ) {
316
        $exists = array_key_exists( $key, $this->map );
317
        if ( $exists ) {
318
            $res = $callable( $key, $this->get( $key ) );
319
            if ( $res == null ) {
320
                unset( $this->map[ $key ] );
321
            } else {
322
                $this->map[ $key ] = $callable( $key, $this->get( $key ) );
323
            }
324
        }
325
326
        return null;
327
    }
328
329
    /**
330
     * If the specified key is not already associated with a value (or is mapped to null),
331
     * attempts to compute its value using the given mapping function and enters it into this map unless null.
332
     *
333
     * If the function returns null, no mapping is recorded. If the function itself throws an (unchecked) exception,
334
     * the exception is rethrown, and no mapping is recorded.
335
     *
336
     * The most common usage is to construct a new object serving as an initial mapped value or memoized result, as in:
337
     *
338
     * @param          $key
339
     * @param callable $callable
340
     *
341
     * @return mixed|null the current (existing or computed) value associated with the specified key, or null if the computed value is null
342
     */
343
    public function computeIfAbsent( $key, callable $callable ) {
344
        $exists        = array_key_exists( $key, $this->map );
345
        $previousValue = $this->get( $key );
346
        $res           = null;
347
        if ( !$exists || $previousValue !== null ) {
348
            $res = $callable( $key, $this->get( $key ) );
349
            if ( $res != null ) {
350
                $this->map[ $key ] = $res;
351
            }
352
353
        }
354
355
        return $res ?: null;
356
357
    }
358
359
    /**
360
     * If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.
361
     *
362
     * If the function returns null, the mapping is removed.
363
     * If the function itself throws an (unchecked) exception, the exception is rethrown, and the current mapping is left unchanged.
364
     *
365
     * @param          $key
366
     * @param callable $callable
367
     *
368
     * @return mixed|null the new value associated with the specified key, or null if none
369
     */
370
    public function computeIfPresent( $key, callable $callable ) {
371
        $exists        = array_key_exists( $key, $this->map );
372
        $previousValue = $this->get( $key );
373
        $res           = null;
374
        if ( $exists && $previousValue !== null ) {
375
            $res = $callable( $key, $this->map[ $key ] );
376
            if ( $res == null ) {
377
                unset( $this->map[ $key ] );
378
            } else {
379
                $this->map[ $key ] = $res;
380
            }
381
        }
382
383
        return $res ?: null;
384
    }
385
386
}