Test Failed
Pull Request — master (#15)
by mon
02:42
created

AbstractMap::removeTypeExtensionMapping()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

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