Test Failed
Pull Request — master (#18)
by mon
01:52
created

Type::getExtensions()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace FileEye\MimeMap;
4
5
/**
6
 * Class for working with MIME types
7
 */
8
class Type
9
{
10
    /**
11
     * Short format [e.g. image/jpeg] for strings.
12
     */
13
    const SHORT_TEXT = 0;
14
15
    /**
16
     * Full format [e.g. image/jpeg; p="1"] for strings.
17
     */
18
    const FULL_TEXT = 1;
19
20
    /**
21
     * Full format with comments [e.g. image/jpeg; p="1" (comment)] for strings.
22
     */
23
    const FULL_TEXT_WITH_COMMENTS = 2;
24
25
    /**
26
     * The MIME media type.
27
     *
28
     * @var string
29
     */
30
    protected $media;
31
32
    /**
33
     * The MIME media type comment.
34
     *
35
     * @var string
36
     */
37
    protected $mediaComment;
38
39
    /**
40
     * The MIME media sub-type.
41
     *
42
     * @var string
43
     */
44
    protected $subType;
45
46
    /**
47
     * The MIME media sub-type comment.
48
     *
49
     * @var string
50
     */
51
    protected $subTypeComment;
52
53
    /**
54
     * Optional MIME parameters.
55
     *
56
     * @var TypeParameter[]
57
     */
58
    protected $parameters = [];
59
60
    /**
61
     * The MIME types map.
62
     *
63
     * @var Map\AbstractMap
64
     */
65
    protected $map;
66
67
    /**
68
     * Constructor.
69
     *
70
     * The type string will be parsed and the appropriate class vars set.
71
     *
72
     * @param string $type_string
73
     *   (Optional) MIME type string to be parsed.
74
     * @param string $map_class
75
     *   (Optional) The FQCN of the map class to use.
76
     */
77 61
    public function __construct($type_string = null, $map_class = null)
78
    {
79 61
        TypeParser::parse($type_string, $this);
80 55
        $this->map = MapHandler::map($map_class);
81 55
    }
82
83
    /**
84
     * Gets a MIME type's media.
85
     *
86
     * Note: 'media' refers to the portion before the first slash.
87
     *
88
     * @return string
89
     *   Type's media.
90
     */
91 45
    public function getMedia()
92
    {
93 45
        return $this->media;
94
    }
95
96
    /**
97
     * Sets a MIME type's media.
98
     *
99
     * @param string $media
100
     *   Type's media.
101
     *
102
     * @return $this
103
     */
104 56
    public function setMedia($media)
105
    {
106 56
        $this->media = $media;
107 56
        return $this;
108
    }
109
110
    /**
111
     * Gets a MIME type's media comment.
112
     *
113
     * @return string
114
     *   Type's media comment.
115
     */
116 30
    public function getMediaComment()
117
    {
118 30
        return $this->mediaComment;
119
    }
120
121
    /**
122
     * Sets a MIME type's media comment.
123
     *
124
     * @param string $comment
125
     *   Type's media comment.
126
     *
127
     * @return $this
128
     */
129 56
    public function setMediaComment($comment)
130
    {
131 56
        $this->mediaComment = $comment;
132 56
        return $this;
133
    }
134
135
    /**
136
     * Gets a MIME type's subtype.
137
     *
138
     * @return string
139
     *   Type's subtype, null if invalid mime type.
140
     */
141 46
    public function getSubType()
142
    {
143 46
        return $this->subType;
144
    }
145
146
    /**
147
     * Sets a MIME type's subtype.
148
     *
149
     * @param string $sub_type
150
     *   Type's subtype.
151
     *
152
     * @return $this
153
     */
154 54
    public function setSubType($sub_type)
155
    {
156 54
        $this->subType = $sub_type;
157 54
        return $this;
158
    }
159
160
    /**
161
     * Gets a MIME type's subtype comment.
162
     *
163
     * @return string
164
     *   Type's subtype comment, null if invalid mime type.
165
     */
166 30
    public function getSubTypeComment()
167
    {
168 30
        return $this->subTypeComment;
169
    }
170
171
    /**
172
     * Sets a MIME type's subtype comment.
173
     *
174
     * @param string $comment
175
     *   Type's subtype comment.
176
     *
177
     * @return $this
178
     */
179 54
    public function setSubTypeComment($comment)
180
    {
181 54
        $this->subTypeComment = $comment;
182 54
        return $this;
183
    }
184
185
    /**
186
     * Does this type have any parameters?
187
     *
188
     * @return boolean
189
     *   True if type has parameters, false otherwise.
190
     */
191 30
    public function hasParameters()
192
    {
193 30
        return (bool) $this->parameters;
194
    }
195
196
    /**
197
     * Get a MIME type's parameters.
198
     *
199
     * @return TypeParameter[]
200
     */
201 31
    public function getParameters()
202
    {
203 31
        return $this->parameters;
204
    }
205
206
    /**
207
     * Get a MIME type's parameter.
208
     *
209
     * @param string $name
210
     *   Parameter name
211
     *
212
     * @return TypeParameter|null
213
     */
214 24
    public function getParameter($name)
215
    {
216 24
        return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
217
    }
218
219
    /**
220
     * Add a parameter to this type
221
     *
222
     * @param string $name
223
     *   Parameter name.
224
     * @param string $value
225
     *   Parameter value.
226
     * @param string $comment
227
     *   Comment for this parameter.
228
     *
229
     * @return void
230
     */
231 27
    public function addParameter($name, $value, $comment = null)
232
    {
233 27
        $this->parameters[$name] = new TypeParameter($name, $value, $comment);
234 27
    }
235
236
    /**
237
     * Remove a parameter from this type.
238
     *
239
     * @param string $name
240
     *   Parameter name.
241
     *
242
     * @return void
243
     */
244 1
    public function removeParameter($name)
245
    {
246 1
        unset($this->parameters[$name]);
247 1
    }
248
249
    /**
250
     * Create a textual MIME type from object values.
251
     *
252
     * This function performs the opposite function of parse().
253
     *
254
     * @param int $format
255
     *   The format of the output string.
256
     *
257
     * @return string
258
     *   MIME type string.
259
     */
260 49
    public function toString($format = Type::FULL_TEXT)
261
    {
262 49
        $type = strtolower($this->media ?: '*');
263 49
        if ($format > Type::FULL_TEXT && isset($this->mediaComment)) {
264 3
            $type .= ' (' .  $this->mediaComment . ')';
265
        }
266 49
        $type .= '/' . strtolower($this->subType ?: '*');
267 49
        if ($format > Type::FULL_TEXT && isset($this->subTypeComment)) {
268 6
            $type .= ' (' .  $this->subTypeComment . ')';
269
        }
270 49
        if ($format > Type::SHORT_TEXT && count($this->parameters)) {
271 24
            foreach ($this->parameters as $parameter) {
272 24
                $type .= '; ' . $parameter->toString($format);
273
            }
274
        }
275 49
        return $type;
276
    }
277
278
    /**
279
     * Is this type experimental?
280
     *
281
     * Note: Experimental types are denoted by a leading 'x-' in the media or
282
     *       subtype, e.g. text/x-vcard or x-world/x-vrml.
283
     *
284
     * @return boolean
285
     *   True if type is experimental, false otherwise.
286
     */
287 1
    public function isExperimental()
288
    {
289 1
        return substr($this->getMedia(), 0, 2) == 'x-' || substr($this->getSubType(), 0, 2) == 'x-';
290
    }
291
292
    /**
293
     * Is this a vendor MIME type?
294
     *
295
     * Note: Vendor types are denoted with a leading 'vnd. in the subtype.
296
     *
297
     * @return boolean
298
     *   True if type is a vendor type, false otherwise.
299
     */
300 1
    public function isVendor()
301
    {
302 1
        return substr($this->getSubType(), 0, 4) == 'vnd.';
303
    }
304
305
    /**
306
     * Is this a wildcard type?
307
     *
308
     * @return boolean
309
     *   True if type is a wildcard, false otherwise.
310
     */
311 14
    public function isWildcard()
312
    {
313 14
        return ($this->getMedia() === '*' && $this->getSubtype() === '*') || strpos($this->getSubtype(), '*') !== false;
314
    }
315
316
    /**
317
     * Is this an alias?
318
     *
319
     * @return boolean
320
     *   True if type is an alias, false otherwise.
321
     */
322 15
    public function isAlias()
323
    {
324 15
        return $this->map->hasAlias($this->toString(static::SHORT_TEXT));
325
    }
326
327
    /**
328
     * Perform a wildcard match on a MIME type
329
     *
330
     * Example:
331
     * $type = new Type('image/png');
332
     * $type->wildcardMatch('image/*');
333
     *
334
     * @param string $wildcard
335
     *   Wildcard to check against.
336
     *
337
     * @return boolean
338
     *   True if there was a match, false otherwise.
339
     */
340 1
    public function wildcardMatch($wildcard)
341
    {
342 1
        $wildcard_type = new static($wildcard);
343
344 1
        if (!$wildcard_type->isWildcard()) {
345 1
            return false;
346
        }
347
348 1
        $wildcard_re = strtr($wildcard_type->toString(static::SHORT_TEXT), [
349 1
            '/' => '\\/',
350
            '*' => '.*',
351
        ]);
352 1
        $subject = $this->toString(static::SHORT_TEXT);
353
354 1
        return preg_match("/$wildcard_re/", $subject) === 1;
355
    }
356
357
    /**
358
     * Builds a list of MIME types existing in the map.
359
     *
360
     * If the current type is a wildcard, than all the types matching the
361
     * wildcard will be returned.
362
     *
363
     * @param bool $strict
364
     *   (Optional) if true a MappingException is thrown when no type is
365
     *   found, if false it returns an empty array as a default.
366
     *   Defaults to true.
367
     *
368
     * @throws MappingException if no mapping found and $strict is true.
369
     *
370
     * @return string[]
371
     */
372 8
    protected function buildTypesList($strict = true)
373
    {
374 8
        $subject = $this->toString(static::SHORT_TEXT);
375
376
        // Find all types.
377 8
        $types = [];
378 8
        if (!$this->isWildcard()) {
379 8
            if ($this->map->hasType($subject)) {
380 8
                $types[] = $subject;
381
            }
382
        } else {
383 1
            foreach ($this->map->listTypes($subject) as $t) {
384 1
                $types[] = $t;
385
            }
386
        }
387
388
        // No types found, throw exception or return emtpy array.
389 8
        if (empty($types)) {
390 5
            if ($strict) {
391 2
                throw new MappingException('No MIME type found for ' . $subject . ' in map');
392
            } else {
393 3
                return [];
394
            }
395
        }
396
397 6
        return $types;
398
    }
399
400
    /**
401
     * Returns the unaliased MIME type.
402
     *
403
     * @return Type
404
     *   $this if the current type is not an alias, the parent type if the
405
     *   current type is an alias.
406
     */
407 11
    protected function getUnaliasedType()
408
    {
409 11
        return $this->isAlias() ? new static($this->map->getAliasTypes($this->toString(static::SHORT_TEXT))[0]) : $this;
410
    }
411
412
    /**
413
     * Returns a description for the MIME type, if existing in the map.
414
     *
415
     * @param bool $include_acronym
416
     *   (Optional) if true and an acronym description exists for the type,
417
     *   the returned description will contain the acronym and its description,
418
     *   appended with a comma. Defaults to false.
419
     *
420
     * @return string|null
421
     */
422 1
    public function getDescription($include_acronym = false)
423
    {
424 1
        $descriptions = $this->map->getTypeDescriptions($this->getUnaliasedType()->toString(static::SHORT_TEXT));
425 1
        $res = null;
426 1
        if (isset($descriptions[0])) {
427 1
            $res = $descriptions[0];
428
        }
429 1
        if ($include_acronym && isset($descriptions[1])) {
430 1
            $res .= ', ' . $descriptions[1];
431
        }
432 1
        return $res;
433
    }
434
435
    /**
436
     * Returns all the aliases related to the MIME type(s).
437
     *
438
     * If the current type is a wildcard, than all aliases of all the
439
     * types matching the wildcard will be returned.
440
     *
441
     * @param bool $strict
442
     *   (Optional) if true a MappingException is thrown when no mapping is
443
     *   found, if false it returns an empty array as a default.
444
     *   Defaults to true.
445
     *
446
     * @throws MappingException if error and $strict is true.
447
     *
448
     * @return string[]
449
     */
450 4
    public function getAliases($strict = true)
451
    {
452
        // Fail if the current type is an alias already.
453 4
        if ($this->isAlias()) {
454 2
            if ($strict) {
455 1
                $subject = $this->toString(static::SHORT_TEXT);
456 1
                throw new MappingException("Cannot get aliases for '{$subject}', it is an alias itself");
457
            } else {
458 1
                return [];
459
            }
460
        }
461
462
        // Build the array of aliases.
463 3
        $aliases = [];
464 3
        foreach ($this->buildTypesList($strict) as $t) {
465 2
            foreach ($this->map->getTypeAliases($t) as $a) {
466 2
                $aliases[$a] = $a;
467
            }
468
        }
469
470 2
        return array_keys($aliases);
471
    }
472
473
    /**
474
     * Returns the MIME type's preferred file extension.
475
     *
476
     * @param bool $strict
477
     *   (Optional) if true a MappingException is thrown when no mapping is
478
     *   found, if false it returns null as a default.
479
     *   Defaults to true.
480
     *
481
     * @throws MappingException if no mapping found and $strict is true.
482
     *
483
     * @return string
484
     */
485 7
    public function getDefaultExtension($strict = true)
486
    {
487 7
        $unaliased_type = $this->getUnaliasedType();
488 7
        $subject = $unaliased_type->toString(static::SHORT_TEXT);
489
490 7
        if (!$unaliased_type->isWildcard()) {
491 4
            $proceed = $this->map->hasType($subject);
492
        } else {
493 3
            $proceed = count($this->map->listTypes($subject)) === 1;
494
        }
495
496 7
        if (!$proceed) {
497 5
            if ($strict) {
498 4
                throw new MappingException('Cannot determine default extension for type: ' . $unaliased_type->toString(static::SHORT_TEXT));
499
            } else {
500 1
                return null;
501
            }
502
        }
503
504 3
        return $unaliased_type->getExtensions()[0];
505
    }
506
507
    /**
508
     * Returns all the file extensions related to the MIME type(s).
509
     *
510
     * If the current type is a wildcard, than all extensions of all the
511
     * types matching the wildcard will be returned.
512
     *
513
     * @param bool $strict
514
     *   (Optional) if true a MappingException is thrown when no mapping is
515
     *   found, if false it returns an empty array as a default.
516
     *   Defaults to true.
517
     *
518
     * @throws MappingException if no mapping found and $strict is true.
519
     *
520
     * @return string[]
521
     */
522 6
    public function getExtensions($strict = true)
523
    {
524
        // Build the array of extensions.
525 6
        $extensions = [];
526 6
        foreach ($this->getUnaliasedType()->buildTypesList($strict) as $t) {
527 5
            foreach ($this->map->getTypeExtensions($t) as $e) {
528 5
                $extensions[$e] = $e;
529
            }
530
        }
531 5
        return array_keys($extensions);
532
    }
533
}
534