Passed
Pull Request — master (#15)
by mon
02:14
created

AbstractMap   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 582
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 1

Test Coverage

Coverage 96.03%

Importance

Changes 0
Metric Value
wmc 58
lcom 2
cbo 1
dl 0
loc 582
ccs 145
cts 151
cp 0.9603
rs 4.5599
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A getInstance() 0 7 2
A getMapArray() 0 4 1
A getFileName() 0 4 1
A sort() 0 10 3
A hasType() 0 5 1
A hasAlias() 0 4 1
A hasExtension() 0 4 1
A listTypes() 0 5 1
A listAliases() 0 4 1
A listExtensions() 0 4 1
A addTypeDescription() 0 10 2
A addTypeAlias() 0 17 3
A addTypeExtensionMapping() 0 18 2
A getTypeAliases() 0 5 2
A getTypeExtensions() 0 6 2
A setTypeDefaultExtension() 0 4 1
A removeType() 0 21 4
A removeTypeAlias() 0 10 2
A removeTypeExtensionMapping() 0 10 2
A getExtensionTypes() 0 5 2
A setExtensionDefaultType() 0 4 1
A listEntries() 0 19 3
A getMapEntry() 0 6 2
A removeMapEntry() 0 15 2
A getMapSubEntry() 0 7 2
A addMapSubEntry() 0 14 3
A removeMapSubEntry() 0 34 5
A setValueAsDefault() 0 29 5

How to fix   Complexity   

Complex Class

Complex classes like AbstractMap 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 AbstractMap, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace FileEye\MimeMap\Map;
4
5
use FileEye\MimeMap\MappingException;
6
7
/**
8
 * Abstract class for mapping file extensions to MIME types.
9
 *
10
 * This class cannot be instantiated; extend from it to implement a map.
11
 */
12
abstract class AbstractMap
13
{
14
    /**
15
     * Returns the singleton.
16
     *
17
     * @return string
18
     */
19 33
    public static function getInstance()
20
    {
21 33
        if (!static::$instance) {
22 3
            static::$instance = new static();
23
        }
24 33
        return static::$instance;
25
    }
26
27
    /**
28
     * Returns this file's full qualified filename.
29
     *
30
     * @return string
31
     */
32 1
    public function getFileName()
33
    {
34 1
        throw new \LogicException(__METHOD__ . ' is not implemented in ' . get_called_class());
35
    }
36
37
    /**
38
     * Gets the map array.
39
     *
40
     * @return array
41
     */
42 5
    public function getMapArray()
43
    {
44 5
        return static::$map;
45
    }
46
47
    /**
48
     * Sorts the map.
49
     *
50
     * @return $this
51
     */
52 6
    public function sort()
53
    {
54 6
        foreach (array_keys(static::$map) as $k) {
55 4
            ksort(static::$map[$k]);
56 4
            foreach (static::$map[$k] as &$sub) {
57 4
                ksort($sub);
58
            }
59
        }
60 6
        return $this;
61
    }
62
63
    /**
64
     * Determines if a MIME type exists.
65
     *
66
     * @param string $type The type to be found.
67
     *
68
     * @return bool
69
     */
70 9
    public function hasType($type)
71
    {
72
        // xx manage aliases
73 9
        return (bool) $this->getMapEntry('t', $type);
74
    }
75
76
    /**
77
     * Determines if a MIME type alias exists.
78
     *
79
     * @param string $alias The alias to be found.
80
     *
81
     * @return bool
82
     */
83 6
    public function hasAlias($alias)
84
    {
85 6
        return (bool) $this->getMapEntry('a', $alias);
86
    }
87
88
    /**
89
     * Determines if an entry exists from the 'extensions' array.
90
     *
91
     * @param string $extension The extension to be found.
92
     *
93
     * @return bool
94
     */
95 1
    public function hasExtension($extension)
96
    {
97 1
        return (bool) $this->getMapEntry('e', $extension);
98
    }
99
100
    /**
101
     * Lists all the MIME types defined in the map.
102
     *
103
     * @param string $match (Optional) a match wildcard to limit the list.
104
     *
105
     * @return string[]
106
     */
107 7
    public function listTypes($match = null)
108
    {
109
        // xx manage aliases
110 7
        return $this->listEntries('t', $match);
111
    }
112
113
    /**
114
     * Lists all the MIME types aliases defined in the map.
115
     *
116
     * @param string $match (Optional) a match wildcard to limit the list.
117
     *
118
     * @return string[]
119
     */
120 2
    public function listAliases($match = null)
121
    {
122 2
        return $this->listEntries('a', $match);
123
    }
124
125
    /**
126
     * Lists all the extensions defined in the map.
127
     *
128
     * @param string $match (Optional) a match wildcard to limit the list.
129
     *
130
     * @return string[]
131
     */
132 3
    public function listExtensions($match = null)
133
    {
134 3
        return $this->listEntries('e', $match);
135
    }
136
137
    /**
138
     * Adds a description of a MIME type.
139
     *
140
     * @param string $type
141
     *   A MIME type.
142
     * @param string $description
143
     *   The description of the MIME type.
144
     *
145
     * @throws MappingException if $type is an alias.
146
     *
147
     * @return $this
148
     */
149 1
    public function addTypeDescription($type, $description)
150
    {
151
        // Consistency checks.
152 1
        if ($this->hasAlias($type)) {
153
            throw new MappingException("Cannot add description for '{$type}', '{$type}' is an alias");
154
        }
155
156 1
        $this->addMapSubEntry('t', $type, 'desc', $description);
157 1
        return $this;
158
    }
159
160
    /**
161
     * Adds an alias of a MIME type.
162
     *
163
     * @param string $type
164
     *   A MIME type.
165
     * @param string $alias
166
     *   An alias of $type.
167
     *
168
     * @throws MappingException if no $type is found.
169
     *
170
     * @return $this
171
     */
172 1
    public function addTypeAlias($type, $alias)
173
    {
174 1
        $type = strtolower($type);
175 1
        $alias = strtolower($alias);
176
177
        // Consistency checks.
178 1
        if (!$this->hasType($type)) {
179
            throw new MappingException("Cannot set '{$alias}' as alias for '{$type}', '{$type}' not defined");
180
        }
181 1
        if ($this->hasType($alias)) {
182
            throw new MappingException("Cannot set '{$alias}' as alias for '{$type}', '{$alias}' is already defined as a type");
183
        }
184
185 1
        $this->addMapSubEntry('t', $type, 'a', $alias);
186 1
        $this->addMapSubEntry('a', $alias, 't', $type);
187 1
        return $this;
188
    }
189
190
    /**
191
     * Adds a type-to-extension mapping.
192
     *
193
     * @param string $type
194
     *   A MIME type.
195
     * @param string $extension
196
     *   A file extension.
197
     *
198
     * @throws MappingException if $type is an alias.
199
     *
200
     * @return $this
201
     */
202 5
    public function addTypeExtensionMapping($type, $extension)
203
    {
204 5
        $type = strtolower($type);
205 5
        $extension = strtolower($extension);
206
207
        // Consistency checks.
208 5
        if ($this->hasAlias($type)) {
209
            throw new MappingException("Cannot map '{$extension}' to '{$type}', '{$type}' is an alias");
210
        }
211
212
        // Add entry to 't'.
213 5
        $this->addMapSubEntry('t', $type, 'e', $extension);
214
215
        // Add entry to 'e'.
216 5
        $this->addMapSubEntry('e', $extension, 't', $type);
217
218 5
        return $this;
219
    }
220
221
    /**
222
     * Gets the aliases of a MIME type.
223
     *
224
     * @param string $type The type to be found.
225
     *
226
     * @return string[] The mapped aliases.
227
     */
228 1
    public function getTypeAliases($type)
229
    {
230 1
        $res = $this->getMapSubEntry('t', $type, 'a');
231 1
        return $res ?: [];
232
    }
233
234
    /**
235
     * Gets the content of an entry from the 't' array.
236
     *
237
     * @param string $type The type to be found.
238
     *
239
     * @return string[] The mapped file extensions.
240
     */
241 5
    public function getTypeExtensions($type)
242
    {
243
        // xx manage aliases
244 5
        $res = $this->getMapSubEntry('t', $type, 'e');
245 5
        return $res ?: [];
246
    }
247
248
    /**
249
     * Changes the default extension for a MIME type.
250
     *
251
     * @param string $type
252
     *   A MIME type.
253
     * @param string $extension
254
     *   A file extension.
255
     *
256
     * @throws MappingException if no mapping found.
257
     *
258
     * @return $this
259
     */
260 3
    public function setTypeDefaultExtension($type, $extension)
261
    {
262 3
        return $this->setValueAsDefault('t', $type, 'e', $extension);
263
    }
264
265
    /**
266
     * Removes the entire mapping of a type.
267
     *
268
     * @param string $type
269
     *   A MIME type.
270
     *
271
     * @return bool
272
     *   true if the mapping was removed, false if the type was not present.
273
     */
274 1
    public function removeType($type)
275
    {
276 1
        $type = strtolower($type);
277
278
        // Return false if type is not found.
279 1
        if (!$this->hasType($type)) {
280 1
            return false;
281
        }
282
283
        // Loop through all the associated extensions and remove them.
284 1
        foreach ($this->getTypeExtensions($type) as $extension) {
285 1
            $this->removeTypeExtensionMapping($type, $extension);
286
        }
287
288
        // Loop through all the associated aliases and remove them.
289 1
        foreach ($this->getTypeAliases($type) as $alias) {
290
            $this->removeTypeAlias($type, $alias);
291
        }
292
293 1
        return $this->removeMapEntry('t', $type);
294
    }
295
296
    /**
297
     * Removes a MIME type alias.
298
     *
299
     * @param string $type
300
     *   A MIME type.
301
     * @param string $alias
302
     *   The alias to be removed.
303
     *
304
     * @return bool
305
     *   true if the alias was removed, false if the alias was not present.
306
     */
307 1
    public function removeTypeAlias($type, $alias)
308
    {
309 1
        $type = strtolower($type);
310 1
        $alias = strtolower($alias);
311
312 1
        $type_ret = $this->removeMapSubEntry('t', $type, 'a', $alias);
313 1
        $alias_ret = $this->removeMapSubEntry('a', $alias, 't', $type);
314
315 1
        return $type_ret && $alias_ret;
316
    }
317
318
    /**
319
     * Removes a type-to-extension mapping.
320
     *
321
     * @param string $type
322
     *   A MIME type.
323
     * @param string $extension
324
     *   The file extension to be removed.
325
     *
326
     * @return bool
327
     *   true if the mapping was removed, false if the mapping was not present.
328
     */
329 1
    public function removeTypeExtensionMapping($type, $extension)
330
    {
331 1
        $type = strtolower($type);
332 1
        $extension = strtolower($extension);
333
334 1
        $type_ret = $this->removeMapSubEntry('t', $type, 'e', $extension);
335 1
        $extension_ret = $this->removeMapSubEntry('e', $extension, 't', $type);
336
337 1
        return $type_ret && $extension_ret;
338
    }
339
340
    /**
341
     * Gets the content of an entry from the 'extensions' array.
342
     *
343
     * @param string $extension The extension to be found.
344
     *
345
     * @return string[] The mapped MIME types.
346
     */
347 9
    public function getExtensionTypes($extension)
348
    {
349 9
        $res = $this->getMapSubEntry('e', $extension, 't');
350 9
        return $res ?: [];
351
    }
352
353
    /**
354
     * Changes the default MIME type for a file extension.
355
     *
356
     * @param string $extension
357
     *   A file extension.
358
     * @param string $type
359
     *   A MIME type.
360
     *
361
     * @throws MappingException if no mapping found.
362
     *
363
     * @return $this
364
     */
365 3
    public function setExtensionDefaultType($extension, $type)
366
    {
367 3
        return $this->setValueAsDefault('e', $extension, 't', $type);
368
    }
369
370
    /**
371
     * Gets a list of entries of the map.
372
     *
373
     * @param string $entry
374
     *   The main array entry.
375
     * @param string $match
376
     *   (Optional) a match wildcard to limit the list.
377
     *
378
     * @return array
379
     *   The list of the entries.
380
     */
381 7
    protected function listEntries($entry, $match = null)
382
    {
383 7
        $entry = strtolower($entry);
384
385 7
        if (!isset(static::$map[$entry])) {
386 1
            return [];
387
        }
388
389 7
        $list = array_keys(static::$map[$entry]);
390
391 7
        if (is_null($match)) {
392 3
            return $list;
393
        } else {
394 4
            $re = strtr($match, ['/' => '\\/', '*' => '.*']);
395
            return array_filter($list, function ($v) use ($re) {
396 4
                return preg_match("/$re/", $v) === 1;
397 4
            });
398
        }
399
    }
400
401
    /**
402
     * Gets the content of an entry of the map.
403
     *
404
     * @param string $entry
405
     *   The main array entry.
406
     * @param string $entry_key
407
     *   The main entry value.
408
     *
409
     * @return mixed|null
410
     *   The value of the entry, or null if missing.
411
     */
412 14
    protected function getMapEntry($entry, $entry_key)
413
    {
414 14
        $entry = strtolower($entry);
415 14
        $entry_key = strtolower($entry_key);
416 14
        return isset(static::$map[$entry][$entry_key]) ? static::$map[$entry][$entry_key] : null;
417
    }
418
419
    /**
420
     * Removes an entry from the map.
421
     *
422
     * @param string $entry
423
     *   The main array entry.
424
     * @param string $entry_key
425
     *   The main entry value.
426
     *
427
     * @return bool
428
     *   true if the entry was removed, false if the entry was not present.
429
     */
430 1
    protected function removeMapEntry($entry, $entry_key)
431
    {
432 1
        $entry = strtolower($entry);
433 1
        $entry_key = strtolower($entry_key);
434
435
        // Return false if no entry.
436 1
        if (!isset(static::$map[$entry][$entry_key])) {
437
            return false;
438
        }
439
440
        // Remove the map entry.
441 1
        unset(static::$map[$entry][$entry_key]);
442
443 1
        return true;
444
    }
445
446
    /**
447
     * Gets the content of a subentry of the map.
448
     *
449
     * @param string $entry
450
     *   The main array entry.
451
     * @param string $entry_key
452
     *   The main entry value.
453
     * @param string $sub_entry
454
     *   The sub entry.
455
     *
456
     * @return mixed|null
457
     *   The value of the entry, or null if missing.
458
     */
459 12
    protected function getMapSubEntry($entry, $entry_key, $sub_entry)
460
    {
461 12
        $entry = strtolower($entry);
462 12
        $entry_key = strtolower($entry_key);
463 12
        $sub_entry = strtolower($sub_entry);
464 12
        return isset(static::$map[$entry][$entry_key][$sub_entry]) ? static::$map[$entry][$entry_key][$sub_entry] : null;
465
    }
466
467
    /**
468
     * Adds an entry to the map.
469
     *
470
     * Checks that no duplicate entries are made.
471
     *
472
     * @param string $entry
473
     *   The main array entry.
474
     * @param string $entry_key
475
     *   The main entry value.
476
     * @param string $sub_entry
477
     *   The sub entry.
478
     * @param string $value
479
     *   The value to add.
480
     *
481
     * @return $this
482
     */
483 5
    protected function addMapSubEntry($entry, $entry_key, $sub_entry, $value)
484
    {
485 5
        $entry = strtolower($entry);
486 5
        $entry_key = strtolower($entry_key);
487 5
        $sub_entry = strtolower($sub_entry);
488 5
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
489 5
            static::$map[$entry][$entry_key][$sub_entry] = [$value];
490
        } else {
491 4
            if (array_search($value, static::$map[$entry][$entry_key][$sub_entry]) === false) {
492 4
                static::$map[$entry][$entry_key][$sub_entry][] = $value;
493
            }
494
        }
495 5
        return $this;
496
    }
497
498
    /**
499
     * Removes an entry from the map.
500
     *
501
     * @param string $entry
502
     *   The main array entry.
503
     * @param string $entry_key
504
     *   The main entry value.
505
     * @param string $sub_entry
506
     *   The sub entry.
507
     * @param string $value
508
     *   The value to remove.
509
     *
510
     * @return bool
511
     *   true if the entry was removed, false if the entry was not present.
512
     */
513 1
    protected function removeMapSubEntry($entry, $entry_key, $sub_entry, $value)
514
    {
515 1
        $entry = strtolower($entry);
516 1
        $entry_key = strtolower($entry_key);
517 1
        $sub_entry = strtolower($sub_entry);
518
519
        // Return false if no entry.
520 1
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
521 1
            return false;
522
        }
523
524
        // Return false if no value.
525 1
        $k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
526 1
        if ($k === false) {
527 1
            return false;
528
        }
529
530
        // Remove the map entry.
531 1
        unset(static::$map[$entry][$entry_key][$sub_entry][$k]);
532
533
        // Remove the entry itself if no more values.
534 1
        if (empty(static::$map[$entry][$entry_key][$sub_entry])) {
535 1
            unset(static::$map[$entry][$entry_key][$sub_entry]);
536
        } else {
537
            // Resequence the remaining values.
538 1
            $tmp = [];
539 1
            foreach (static::$map[$entry][$entry_key][$sub_entry] as $v) {
540 1
                $tmp[] = $v;
541
            }
542 1
            static::$map[$entry][$entry_key][$sub_entry] = $tmp;
543
        }
544
545 1
        return true;
546
    }
547
548
    /**
549
     * Sets a value as the default for an entry.
550
     *
551
     * @param string $entry
552
     *   The main array entry.
553
     * @param string $entry_key
554
     *   The main entry value.
555
     * @param string $sub_entry
556
     *   The sub entry.
557
     * @param string $value
558
     *   The value to add.
559
     *
560
     * @throws MappingException if no mapping found.
561
     *
562
     * @return $this
563
     */
564 6
    protected function setValueAsDefault($entry, $entry_key, $sub_entry, $value)
565
    {
566 6
        $entry = strtolower($entry);
567 6
        $entry_key = strtolower($entry_key);
568 6
        $sub_entry = strtolower($sub_entry);
569
570
        // Throw exception if no entry.
571 6
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
572 2
            throw new MappingException("Cannot set '{$value}' as default for '{$entry_key}', '{$entry_key}' not defined");
573
        }
574
575
        // Throw exception if no entry-value pair.
576 4
        $k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
577 4
        if ($k === false) {
578 2
            throw new MappingException("Cannot set '{$value}' as default for '{$entry_key}', '{$value}' not associated to '{$entry_key}'");
579
        }
580
581
        // Move value to top of array and resequence the rest.
582 2
        $tmp = [$value];
583 2
        foreach (static::$map[$entry][$entry_key][$sub_entry] as $kk => $v) {
584 2
            if ($kk === $k) {
585 2
                continue;
586
            }
587 2
            $tmp[] = $v;
588
        }
589 2
        static::$map[$entry][$entry_key][$sub_entry] = $tmp;
590
591 2
        return $this;
592
    }
593
}
594