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

AbstractMap::getTypeExtensions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
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 37
    public static function getInstance()
20
    {
21 37
        if (!static::$instance) {
22 3
            static::$instance = new static();
23
        }
24 37
        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 11
    public function hasType($type)
71
    {
72 11
        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 9
    public function hasAlias($alias)
83
    {
84 9
        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 1
    public function hasExtension($extension)
95
    {
96 1
        return (bool) $this->getMapEntry('e', $extension);
97
    }
98
99
    /**
100
     * Lists all the MIME types defined in the map.
101
     *
102
     * @param string $match (Optional) a match wildcard to limit the list.
103
     *
104
     * @return string[]
105
     */
106 7
    public function listTypes($match = null)
107
    {
108 7
        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 2
    public function listAliases($match = null)
119
    {
120 2
        return $this->listEntries('a', $match);
121
    }
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 3
    public function listExtensions($match = null)
131
    {
132 3
        return $this->listEntries('e', $match);
133
    }
134
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
     *
145
     * @return $this
146
     */
147 2
    public function addTypeDescription($type, $description)
148
    {
149
        // Consistency checks.
150 2
        if ($this->hasAlias($type)) {
151 1
            throw new MappingException("Cannot add description for '{$type}', '{$type}' is an alias");
152
        }
153
154 1
        $this->addMapSubEntry('t', $type, 'desc', $description);
155 1
        return $this;
156
    }
157
158
    /**
159
     * 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 3
    public function addTypeAlias($type, $alias)
171
    {
172 3
        $type = strtolower($type);
173 3
        $alias = strtolower($alias);
174
175
        // Consistency checks.
176 3
        if (!$this->hasType($type)) {
177 1
            throw new MappingException("Cannot set '{$alias}' as alias for '{$type}', '{$type}' not defined");
178
        }
179 2
        if ($this->hasType($alias)) {
180 2
            throw new MappingException("Cannot set '{$alias}' as alias for '{$type}', '{$alias}' is already defined as a type");
181
        }
182
183 1
        $this->addMapSubEntry('t', $type, 'a', $alias);
184 1
        $this->addMapSubEntry('a', $alias, 't', $type);
185 1
        return $this;
186
    }
187
188
    /**
189
     * Adds a type-to-extension mapping.
190
     *
191
     * @param string $type
192
     *   A MIME type.
193
     * @param string $extension
194
     *   A file extension.
195
     *
196
     * @throws MappingException if $type is an alias.
197
     *
198
     * @return $this
199
     */
200 6
    public function addTypeExtensionMapping($type, $extension)
201
    {
202 6
        $type = strtolower($type);
203 6
        $extension = strtolower($extension);
204
205
        // Consistency checks.
206 6
        if ($this->hasAlias($type)) {
207 1
            throw new MappingException("Cannot map '{$extension}' to '{$type}', '{$type}' is an alias");
208
        }
209
210
        // Add entry to 't'.
211 5
        $this->addMapSubEntry('t', $type, 'e', $extension);
212
213
        // Add entry to 'e'.
214 5
        $this->addMapSubEntry('e', $extension, 't', $type);
215
216 5
        return $this;
217
    }
218
219
    /**
220
     * Gets the aliases of a MIME type.
221
     *
222
     * @param string $type The type to be found.
223
     *
224
     * @return string[] The mapped aliases.
225
     */
226 1
    public function getTypeAliases($type)
227
    {
228 1
        $res = $this->getMapSubEntry('t', $type, 'a');
229 1
        return $res ?: [];
230
    }
231
232
    /**
233
     * Gets the content of an entry from the 't' array.
234
     *
235
     * @param string $type The type to be found.
236
     *
237
     * @return string[] The mapped file extensions.
238
     */
239 5
    public function getTypeExtensions($type)
240
    {
241 5
        $res = $this->getMapSubEntry('t', $type, 'e');
242 5
        return $res ?: [];
243
    }
244
245
    /**
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 3
    public function setTypeDefaultExtension($type, $extension)
258
    {
259 3
        return $this->setValueAsDefault('t', $type, 'e', $extension);
260
    }
261
262
    /**
263
     * Removes the entire mapping of a type.
264
     *
265
     * @param string $type
266
     *   A MIME type.
267
     *
268
     * @return bool
269
     *   true if the mapping was removed, false if the type was not present.
270
     */
271 1
    public function removeType($type)
272
    {
273 1
        $type = strtolower($type);
274
275
        // Return false if type is not found.
276 1
        if (!$this->hasType($type)) {
277 1
            return false;
278
        }
279
280
        // Loop through all the associated extensions and remove them.
281 1
        foreach ($this->getTypeExtensions($type) as $extension) {
282 1
            $this->removeTypeExtensionMapping($type, $extension);
283
        }
284
285
        // Loop through all the associated aliases and remove them.
286 1
        foreach ($this->getTypeAliases($type) as $alias) {
287 1
            $this->removeTypeAlias($type, $alias);
288
        }
289
290 1
        unset(static::$map['t'][$type]);
291
292 1
        return true;
293
    }
294
295
    /**
296
     * Removes a MIME type alias.
297
     *
298
     * @param string $type
299
     *   A MIME type.
300
     * @param string $alias
301
     *   The alias to be removed.
302
     *
303
     * @return bool
304
     *   true if the alias was removed, false if the alias was not present.
305
     */
306 1
    public function removeTypeAlias($type, $alias)
307
    {
308 1
        $type = strtolower($type);
309 1
        $alias = strtolower($alias);
310
311 1
        $type_ret = $this->removeMapSubEntry('t', $type, 'a', $alias);
312 1
        $alias_ret = $this->removeMapSubEntry('a', $alias, 't', $type);
313
314 1
        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
     *   The file extension to be removed.
324
     *
325
     * @return bool
326
     *   true if the mapping was removed, false if the mapping was not present.
327
     */
328 1
    public function removeTypeExtensionMapping($type, $extension)
329
    {
330 1
        $type = strtolower($type);
331 1
        $extension = strtolower($extension);
332
333 1
        $type_ret = $this->removeMapSubEntry('t', $type, 'e', $extension);
334 1
        $extension_ret = $this->removeMapSubEntry('e', $extension, 't', $type);
335
336 1
        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 9
    public function getExtensionTypes($extension)
347
    {
348 9
        $res = $this->getMapSubEntry('e', $extension, 't');
349 9
        return $res ?: [];
350
    }
351
352
    /**
353
     * Changes the default MIME type for a file extension.
354
     *
355
     * @param string $extension
356
     *   A file extension.
357
     * @param string $type
358
     *   A MIME type.
359
     *
360
     * @throws MappingException if no mapping found.
361
     *
362
     * @return $this
363
     */
364 3
    public function setExtensionDefaultType($extension, $type)
365
    {
366 3
        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
     *
377
     * @return array
378
     *   The list of the entries.
379
     */
380 7
    protected function listEntries($entry, $match = null)
381
    {
382 7
        $entry = strtolower($entry);
383
384 7
        if (!isset(static::$map[$entry])) {
385 1
            return [];
386
        }
387
388 7
        $list = array_keys(static::$map[$entry]);
389
390 7
        if (is_null($match)) {
391 3
            return $list;
392
        } else {
393 4
            $re = strtr($match, ['/' => '\\/', '*' => '.*']);
394
            return array_filter($list, function ($v) use ($re) {
395 4
                return preg_match("/$re/", $v) === 1;
396 4
            });
397
        }
398
    }
399
400
    /**
401
     * 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 18
    protected function getMapEntry($entry, $entry_key)
412
    {
413 18
        $entry = strtolower($entry);
414 18
        $entry_key = strtolower($entry_key);
415 18
        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 12
    protected function getMapSubEntry($entry, $entry_key, $sub_entry)
432
    {
433 12
        $entry = strtolower($entry);
434 12
        $entry_key = strtolower($entry_key);
435 12
        $sub_entry = strtolower($sub_entry);
436 12
        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 5
    protected function addMapSubEntry($entry, $entry_key, $sub_entry, $value)
456
    {
457 5
        $entry = strtolower($entry);
458 5
        $entry_key = strtolower($entry_key);
459 5
        $sub_entry = strtolower($sub_entry);
460 5
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
461 5
            static::$map[$entry][$entry_key][$sub_entry] = [$value];
462
        } else {
463 4
            if (array_search($value, static::$map[$entry][$entry_key][$sub_entry]) === false) {
464 4
                static::$map[$entry][$entry_key][$sub_entry][] = $value;
465
            }
466
        }
467 5
        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 1
    protected function removeMapSubEntry($entry, $entry_key, $sub_entry, $value)
486
    {
487 1
        $entry = strtolower($entry);
488 1
        $entry_key = strtolower($entry_key);
489 1
        $sub_entry = strtolower($sub_entry);
490
491
        // Return false if no entry.
492 1
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
493 1
            return false;
494
        }
495
496
        // Return false if no value.
497 1
        $k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
498 1
        if ($k === false) {
499 1
            return false;
500
        }
501
502
        // Remove the map entry.
503 1
        unset(static::$map[$entry][$entry_key][$sub_entry][$k]);
504
505
        // Remove the entry itself if no more values.
506 1
        if (empty(static::$map[$entry][$entry_key][$sub_entry])) {
507 1
            unset(static::$map[$entry][$entry_key][$sub_entry]);
508
        } else {
509
            // Resequence the remaining values.
510 1
            $tmp = [];
511 1
            foreach (static::$map[$entry][$entry_key][$sub_entry] as $v) {
512 1
                $tmp[] = $v;
513
            }
514 1
            static::$map[$entry][$entry_key][$sub_entry] = $tmp;
515
        }
516
517 1
        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 6
    protected function setValueAsDefault($entry, $entry_key, $sub_entry, $value)
537
    {
538 6
        $entry = strtolower($entry);
539 6
        $entry_key = strtolower($entry_key);
540 6
        $sub_entry = strtolower($sub_entry);
541
542
        // Throw exception if no entry.
543 6
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
544 2
            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 4
        $k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
549 4
        if ($k === false) {
550 2
            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 2
        $tmp = [$value];
555 2
        foreach (static::$map[$entry][$entry_key][$sub_entry] as $kk => $v) {
556 2
            if ($kk === $k) {
557 2
                continue;
558
            }
559 2
            $tmp[] = $v;
560
        }
561 2
        static::$map[$entry][$entry_key][$sub_entry] = $tmp;
562
563 2
        return $this;
564
    }
565
}
566