Passed
Push — master ( 2b87da...5b7515 )
by mon
01:43
created

AbstractMap::addTypeExtensionMapping()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 2
dl 0
loc 17
rs 10
c 0
b 0
f 0
ccs 8
cts 8
cp 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 42
    public static function getInstance()
20
    {
21 42
        if (!static::$instance) {
22 3
            static::$instance = new static();
0 ignored issues
show
Bug Best Practice introduced by
The property instance does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
23
        }
24 42
        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 13
    public function hasType($type)
71
    {
72 13
        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 19
    public function hasAlias($alias)
83
    {
84 19
        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 descriptions of a MIME type.
221
     *
222
     * @param string $type The type to be found.
223
     *
224
     * @return string[] The mapped descriptions.
225
     */
226 1
    public function getTypeDescriptions($type)
227
    {
228 1
        $res = $this->getMapSubEntry('t', $type, 'desc');
229 1
        return $res ?: [];
230
    }
231
232
    /**
233
     * Gets the aliases of a MIME type.
234
     *
235
     * @param string $type The type to be found.
236
     *
237
     * @return string[] The mapped aliases.
238
     */
239 2
    public function getTypeAliases($type)
240
    {
241 2
        $res = $this->getMapSubEntry('t', $type, 'a');
242 2
        return $res ?: [];
243
    }
244
245
    /**
246
     * Gets the content of an entry from the 't' array.
247
     *
248
     * @param string $type The type to be found.
249
     *
250
     * @return string[] The mapped file extensions.
251
     */
252 5
    public function getTypeExtensions($type)
253
    {
254 5
        $res = $this->getMapSubEntry('t', $type, 'e');
255 5
        return $res ?: [];
256
    }
257
258
    /**
259
     * Changes the default extension for a MIME type.
260
     *
261
     * @param string $type
262
     *   A MIME type.
263
     * @param string $extension
264
     *   A file extension.
265
     *
266
     * @throws MappingException if no mapping found.
267
     *
268
     * @return $this
269
     */
270 3
    public function setTypeDefaultExtension($type, $extension)
271
    {
272 3
        return $this->setValueAsDefault('t', $type, 'e', $extension);
273
    }
274
275
    /**
276
     * Removes the entire mapping of a type.
277
     *
278
     * @param string $type
279
     *   A MIME type.
280
     *
281
     * @return bool
282
     *   true if the mapping was removed, false if the type was not present.
283
     */
284 1
    public function removeType($type)
285
    {
286 1
        $type = strtolower($type);
287
288
        // Return false if type is not found.
289 1
        if (!$this->hasType($type)) {
290 1
            return false;
291
        }
292
293
        // Loop through all the associated extensions and remove them.
294 1
        foreach ($this->getTypeExtensions($type) as $extension) {
295 1
            $this->removeTypeExtensionMapping($type, $extension);
296
        }
297
298
        // Loop through all the associated aliases and remove them.
299 1
        foreach ($this->getTypeAliases($type) as $alias) {
300 1
            $this->removeTypeAlias($type, $alias);
301
        }
302
303 1
        unset(static::$map['t'][$type]);
304
305 1
        return true;
306
    }
307
308
    /**
309
     * Removes a MIME type alias.
310
     *
311
     * @param string $type
312
     *   A MIME type.
313
     * @param string $alias
314
     *   The alias to be removed.
315
     *
316
     * @return bool
317
     *   true if the alias was removed, false if the alias was not present.
318
     */
319 1
    public function removeTypeAlias($type, $alias)
320
    {
321 1
        $type = strtolower($type);
322 1
        $alias = strtolower($alias);
323
324 1
        $type_ret = $this->removeMapSubEntry('t', $type, 'a', $alias);
325 1
        $alias_ret = $this->removeMapSubEntry('a', $alias, 't', $type);
326
327 1
        return $type_ret && $alias_ret;
328
    }
329
330
    /**
331
     * Removes a type-to-extension mapping.
332
     *
333
     * @param string $type
334
     *   A MIME type.
335
     * @param string $extension
336
     *   The file extension to be removed.
337
     *
338
     * @return bool
339
     *   true if the mapping was removed, false if the mapping was not present.
340
     */
341 1
    public function removeTypeExtensionMapping($type, $extension)
342
    {
343 1
        $type = strtolower($type);
344 1
        $extension = strtolower($extension);
345
346 1
        $type_ret = $this->removeMapSubEntry('t', $type, 'e', $extension);
347 1
        $extension_ret = $this->removeMapSubEntry('e', $extension, 't', $type);
348
349 1
        return $type_ret && $extension_ret;
350
    }
351
352
    /**
353
     * Gets the parent types of an alias.
354
     *
355
     * @param string $alias The alias to be found.
356
     *
357
     * @return string[]
358
     */
359 3
    public function getAliasTypes($alias)
360
    {
361 3
        $res = $this->getMapSubEntry('a', $alias, 't');
362 3
        return $res ?: [];
363
    }
364
365
    /**
366
     * Gets the content of an entry from the 'extensions' array.
367
     *
368
     * @param string $extension The extension to be found.
369
     *
370
     * @return string[] The mapped MIME types.
371
     */
372 9
    public function getExtensionTypes($extension)
373
    {
374 9
        $res = $this->getMapSubEntry('e', $extension, 't');
375 9
        return $res ?: [];
376
    }
377
378
    /**
379
     * Changes the default MIME type for a file extension.
380
     *
381
     * @param string $extension
382
     *   A file extension.
383
     * @param string $type
384
     *   A MIME type.
385
     *
386
     * @throws MappingException if no mapping found.
387
     *
388
     * @return $this
389
     */
390 3
    public function setExtensionDefaultType($extension, $type)
391
    {
392 3
        return $this->setValueAsDefault('e', $extension, 't', $type);
393
    }
394
395
    /**
396
     * Gets a list of entries of the map.
397
     *
398
     * @param string $entry
399
     *   The main array entry.
400
     * @param string $match
401
     *   (Optional) a match wildcard to limit the list.
402
     *
403
     * @return array
404
     *   The list of the entries.
405
     */
406 7
    protected function listEntries($entry, $match = null)
407
    {
408 7
        $entry = strtolower($entry);
409
410 7
        if (!isset(static::$map[$entry])) {
411 1
            return [];
412
        }
413
414 7
        $list = array_keys(static::$map[$entry]);
415
416 7
        if (is_null($match)) {
417 3
            return $list;
418
        } else {
419 4
            $re = strtr($match, ['/' => '\\/', '*' => '.*']);
420
            return array_filter($list, function ($v) use ($re) {
421 4
                return preg_match("/$re/", $v) === 1;
422 4
            });
423
        }
424
    }
425
426
    /**
427
     * Gets the content of an entry of the map.
428
     *
429
     * @param string $entry
430
     *   The main array entry.
431
     * @param string $entry_key
432
     *   The main entry value.
433
     *
434
     * @return mixed|null
435
     *   The value of the entry, or null if missing.
436
     */
437 23
    protected function getMapEntry($entry, $entry_key)
438
    {
439 23
        $entry = strtolower($entry);
440 23
        $entry_key = strtolower($entry_key);
441 23
        return isset(static::$map[$entry][$entry_key]) ? static::$map[$entry][$entry_key] : null;
442
    }
443
444
    /**
445
     * Gets the content of a subentry of the map.
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
     *
454
     * @return mixed|null
455
     *   The value of the entry, or null if missing.
456
     */
457 14
    protected function getMapSubEntry($entry, $entry_key, $sub_entry)
458
    {
459 14
        $entry = strtolower($entry);
460 14
        $entry_key = strtolower($entry_key);
461 14
        $sub_entry = strtolower($sub_entry);
462 14
        return isset(static::$map[$entry][$entry_key][$sub_entry]) ? static::$map[$entry][$entry_key][$sub_entry] : null;
463
    }
464
465
    /**
466
     * Adds an entry to the map.
467
     *
468
     * Checks that no duplicate entries are made.
469
     *
470
     * @param string $entry
471
     *   The main array entry.
472
     * @param string $entry_key
473
     *   The main entry value.
474
     * @param string $sub_entry
475
     *   The sub entry.
476
     * @param string $value
477
     *   The value to add.
478
     *
479
     * @return $this
480
     */
481 5
    protected function addMapSubEntry($entry, $entry_key, $sub_entry, $value)
482
    {
483 5
        $entry = strtolower($entry);
484 5
        $entry_key = strtolower($entry_key);
485 5
        $sub_entry = strtolower($sub_entry);
486 5
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
487 5
            static::$map[$entry][$entry_key][$sub_entry] = [$value];
0 ignored issues
show
Bug Best Practice introduced by
The property map does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
488
        } else {
489 4
            if (array_search($value, static::$map[$entry][$entry_key][$sub_entry]) === false) {
490 4
                static::$map[$entry][$entry_key][$sub_entry][] = $value;
491
            }
492
        }
493 5
        return $this;
494
    }
495
496
    /**
497
     * Removes an entry from the map.
498
     *
499
     * @param string $entry
500
     *   The main array entry.
501
     * @param string $entry_key
502
     *   The main entry value.
503
     * @param string $sub_entry
504
     *   The sub entry.
505
     * @param string $value
506
     *   The value to remove.
507
     *
508
     * @return bool
509
     *   true if the entry was removed, false if the entry was not present.
510
     */
511 1
    protected function removeMapSubEntry($entry, $entry_key, $sub_entry, $value)
512
    {
513 1
        $entry = strtolower($entry);
514 1
        $entry_key = strtolower($entry_key);
515 1
        $sub_entry = strtolower($sub_entry);
516
517
        // Return false if no entry.
518 1
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
519 1
            return false;
520
        }
521
522
        // Return false if no value.
523 1
        $k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
524 1
        if ($k === false) {
525 1
            return false;
526
        }
527
528
        // Remove the map entry.
529 1
        unset(static::$map[$entry][$entry_key][$sub_entry][$k]);
530
531
        // Remove the entry itself if no more values.
532 1
        if (empty(static::$map[$entry][$entry_key][$sub_entry])) {
533 1
            unset(static::$map[$entry][$entry_key][$sub_entry]);
534
        } else {
535
            // Resequence the remaining values.
536 1
            $tmp = [];
537 1
            foreach (static::$map[$entry][$entry_key][$sub_entry] as $v) {
538 1
                $tmp[] = $v;
539
            }
540 1
            static::$map[$entry][$entry_key][$sub_entry] = $tmp;
0 ignored issues
show
Bug Best Practice introduced by
The property map does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
541
        }
542
543 1
        return true;
544
    }
545
546
    /**
547
     * Sets a value as the default for an entry.
548
     *
549
     * @param string $entry
550
     *   The main array entry.
551
     * @param string $entry_key
552
     *   The main entry value.
553
     * @param string $sub_entry
554
     *   The sub entry.
555
     * @param string $value
556
     *   The value to add.
557
     *
558
     * @throws MappingException if no mapping found.
559
     *
560
     * @return $this
561
     */
562 6
    protected function setValueAsDefault($entry, $entry_key, $sub_entry, $value)
563
    {
564 6
        $entry = strtolower($entry);
565 6
        $entry_key = strtolower($entry_key);
566 6
        $sub_entry = strtolower($sub_entry);
567
568
        // Throw exception if no entry.
569 6
        if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
570 2
            throw new MappingException("Cannot set '{$value}' as default for '{$entry_key}', '{$entry_key}' not defined");
571
        }
572
573
        // Throw exception if no entry-value pair.
574 4
        $k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
575 4
        if ($k === false) {
576 2
            throw new MappingException("Cannot set '{$value}' as default for '{$entry_key}', '{$value}' not associated to '{$entry_key}'");
577
        }
578
579
        // Move value to top of array and resequence the rest.
580 2
        $tmp = [$value];
581 2
        foreach (static::$map[$entry][$entry_key][$sub_entry] as $kk => $v) {
582 2
            if ($kk === $k) {
583 2
                continue;
584
            }
585 2
            $tmp[] = $v;
586
        }
587 2
        static::$map[$entry][$entry_key][$sub_entry] = $tmp;
0 ignored issues
show
Bug Best Practice introduced by
The property map does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
588
589 2
        return $this;
590
    }
591
}
592