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

AbstractMap::addTypeDescription()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 5
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 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
        // xx manage aliases
73 11
        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 9
    public function hasAlias($alias)
84
    {
85 9
        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 2
    public function addTypeDescription($type, $description)
150
    {
151
        // Consistency checks.
152 2
        if ($this->hasAlias($type)) {
153 1
            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 3
    public function addTypeAlias($type, $alias)
173
    {
174 3
        $type = strtolower($type);
175 3
        $alias = strtolower($alias);
176
177
        // Consistency checks.
178 3
        if (!$this->hasType($type)) {
179 1
            throw new MappingException("Cannot set '{$alias}' as alias for '{$type}', '{$type}' not defined");
180
        }
181 2
        if ($this->hasType($alias)) {
182 2
            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 6
    public function addTypeExtensionMapping($type, $extension)
203
    {
204 6
        $type = strtolower($type);
205 6
        $extension = strtolower($extension);
206
207
        // Consistency checks.
208 6
        if ($this->hasAlias($type)) {
209 1
            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 1
            $this->removeTypeAlias($type, $alias);
291
        }
292
293 1
        unset(static::$map['t'][$type]);
294
295 1
        return true;
296
    }
297
298
    /**
299
     * Removes a MIME type alias.
300
     *
301
     * @param string $type
302
     *   A MIME type.
303
     * @param string $alias
304
     *   The alias to be removed.
305
     *
306
     * @return bool
307
     *   true if the alias was removed, false if the alias was not present.
308
     */
309 1
    public function removeTypeAlias($type, $alias)
310
    {
311 1
        $type = strtolower($type);
312 1
        $alias = strtolower($alias);
313
314 1
        $type_ret = $this->removeMapSubEntry('t', $type, 'a', $alias);
315 1
        $alias_ret = $this->removeMapSubEntry('a', $alias, 't', $type);
316
317 1
        return $type_ret && $alias_ret;
318
    }
319
320
    /**
321
     * Removes a type-to-extension mapping.
322
     *
323
     * @param string $type
324
     *   A MIME type.
325
     * @param string $extension
326
     *   The file extension to be removed.
327
     *
328
     * @return bool
329
     *   true if the mapping was removed, false if the mapping was not present.
330
     */
331 1
    public function removeTypeExtensionMapping($type, $extension)
332
    {
333 1
        $type = strtolower($type);
334 1
        $extension = strtolower($extension);
335
336 1
        $type_ret = $this->removeMapSubEntry('t', $type, 'e', $extension);
337 1
        $extension_ret = $this->removeMapSubEntry('e', $extension, 't', $type);
338
339 1
        return $type_ret && $extension_ret;
340
    }
341
342
    /**
343
     * Gets the content of an entry from the 'extensions' array.
344
     *
345
     * @param string $extension The extension to be found.
346
     *
347
     * @return string[] The mapped MIME types.
348
     */
349 9
    public function getExtensionTypes($extension)
350
    {
351 9
        $res = $this->getMapSubEntry('e', $extension, 't');
352 9
        return $res ?: [];
353
    }
354
355
    /**
356
     * Changes the default MIME type for a file extension.
357
     *
358
     * @param string $extension
359
     *   A file extension.
360
     * @param string $type
361
     *   A MIME type.
362
     *
363
     * @throws MappingException if no mapping found.
364
     *
365
     * @return $this
366
     */
367 3
    public function setExtensionDefaultType($extension, $type)
368
    {
369 3
        return $this->setValueAsDefault('e', $extension, 't', $type);
370
    }
371
372
    /**
373
     * Gets a list of entries of the map.
374
     *
375
     * @param string $entry
376
     *   The main array entry.
377
     * @param string $match
378
     *   (Optional) a match wildcard to limit the list.
379
     *
380
     * @return array
381
     *   The list of the entries.
382
     */
383 7
    protected function listEntries($entry, $match = null)
384
    {
385 7
        $entry = strtolower($entry);
386
387 7
        if (!isset(static::$map[$entry])) {
388 1
            return [];
389
        }
390
391 7
        $list = array_keys(static::$map[$entry]);
392
393 7
        if (is_null($match)) {
394 3
            return $list;
395
        } else {
396 4
            $re = strtr($match, ['/' => '\\/', '*' => '.*']);
397
            return array_filter($list, function ($v) use ($re) {
398 4
                return preg_match("/$re/", $v) === 1;
399 4
            });
400
        }
401
    }
402
403
    /**
404
     * Gets the content of an entry of the map.
405
     *
406
     * @param string $entry
407
     *   The main array entry.
408
     * @param string $entry_key
409
     *   The main entry value.
410
     *
411
     * @return mixed|null
412
     *   The value of the entry, or null if missing.
413
     */
414 18
    protected function getMapEntry($entry, $entry_key)
415
    {
416 18
        $entry = strtolower($entry);
417 18
        $entry_key = strtolower($entry_key);
418 18
        return isset(static::$map[$entry][$entry_key]) ? static::$map[$entry][$entry_key] : null;
419
    }
420
421
    /**
422
     * Gets the content of a subentry of the map.
423
     *
424
     * @param string $entry
425
     *   The main array entry.
426
     * @param string $entry_key
427
     *   The main entry value.
428
     * @param string $sub_entry
429
     *   The sub entry.
430
     *
431
     * @return mixed|null
432
     *   The value of the entry, or null if missing.
433
     */
434 12
    protected function getMapSubEntry($entry, $entry_key, $sub_entry)
435
    {
436 12
        $entry = strtolower($entry);
437 12
        $entry_key = strtolower($entry_key);
438 12
        $sub_entry = strtolower($sub_entry);
439 12
        return isset(static::$map[$entry][$entry_key][$sub_entry]) ? static::$map[$entry][$entry_key][$sub_entry] : null;
440
    }
441
442
    /**
443
     * Adds an entry to the map.
444
     *
445
     * Checks that no duplicate entries are made.
446
     *
447
     * @param string $entry
448
     *   The main array entry.
449
     * @param string $entry_key
450
     *   The main entry value.
451
     * @param string $sub_entry
452
     *   The sub entry.
453
     * @param string $value
454
     *   The value to add.
455
     *
456
     * @return $this
457
     */
458 5
    protected function addMapSubEntry($entry, $entry_key, $sub_entry, $value)
459
    {
460 5
        $entry = strtolower($entry);
461 5
        $entry_key = strtolower($entry_key);
462 5
        $sub_entry = strtolower($sub_entry);
463 5
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
464 5
            static::$map[$entry][$entry_key][$sub_entry] = [$value];
465
        } else {
466 4
            if (array_search($value, static::$map[$entry][$entry_key][$sub_entry]) === false) {
467 4
                static::$map[$entry][$entry_key][$sub_entry][] = $value;
468
            }
469
        }
470 5
        return $this;
471
    }
472
473
    /**
474
     * Removes an entry from the map.
475
     *
476
     * @param string $entry
477
     *   The main array entry.
478
     * @param string $entry_key
479
     *   The main entry value.
480
     * @param string $sub_entry
481
     *   The sub entry.
482
     * @param string $value
483
     *   The value to remove.
484
     *
485
     * @return bool
486
     *   true if the entry was removed, false if the entry was not present.
487
     */
488 1
    protected function removeMapSubEntry($entry, $entry_key, $sub_entry, $value)
489
    {
490 1
        $entry = strtolower($entry);
491 1
        $entry_key = strtolower($entry_key);
492 1
        $sub_entry = strtolower($sub_entry);
493
494
        // Return false if no entry.
495 1
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
496 1
            return false;
497
        }
498
499
        // Return false if no value.
500 1
        $k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
501 1
        if ($k === false) {
502 1
            return false;
503
        }
504
505
        // Remove the map entry.
506 1
        unset(static::$map[$entry][$entry_key][$sub_entry][$k]);
507
508
        // Remove the entry itself if no more values.
509 1
        if (empty(static::$map[$entry][$entry_key][$sub_entry])) {
510 1
            unset(static::$map[$entry][$entry_key][$sub_entry]);
511
        } else {
512
            // Resequence the remaining values.
513 1
            $tmp = [];
514 1
            foreach (static::$map[$entry][$entry_key][$sub_entry] as $v) {
515 1
                $tmp[] = $v;
516
            }
517 1
            static::$map[$entry][$entry_key][$sub_entry] = $tmp;
518
        }
519
520 1
        return true;
521
    }
522
523
    /**
524
     * Sets a value as the default for an entry.
525
     *
526
     * @param string $entry
527
     *   The main array entry.
528
     * @param string $entry_key
529
     *   The main entry value.
530
     * @param string $sub_entry
531
     *   The sub entry.
532
     * @param string $value
533
     *   The value to add.
534
     *
535
     * @throws MappingException if no mapping found.
536
     *
537
     * @return $this
538
     */
539 6
    protected function setValueAsDefault($entry, $entry_key, $sub_entry, $value)
540
    {
541 6
        $entry = strtolower($entry);
542 6
        $entry_key = strtolower($entry_key);
543 6
        $sub_entry = strtolower($sub_entry);
544
545
        // Throw exception if no entry.
546 6
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
547 2
            throw new MappingException("Cannot set '{$value}' as default for '{$entry_key}', '{$entry_key}' not defined");
548
        }
549
550
        // Throw exception if no entry-value pair.
551 4
        $k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
552 4
        if ($k === false) {
553 2
            throw new MappingException("Cannot set '{$value}' as default for '{$entry_key}', '{$value}' not associated to '{$entry_key}'");
554
        }
555
556
        // Move value to top of array and resequence the rest.
557 2
        $tmp = [$value];
558 2
        foreach (static::$map[$entry][$entry_key][$sub_entry] as $kk => $v) {
559 2
            if ($kk === $k) {
560 2
                continue;
561
            }
562 2
            $tmp[] = $v;
563
        }
564 2
        static::$map[$entry][$entry_key][$sub_entry] = $tmp;
565
566 2
        return $this;
567
    }
568
}
569