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

AbstractMap::addTypeExtensionMapping()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2.0078

Importance

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