Passed
Pull Request — master (#17)
by mon
01:53
created

Type::parse()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 19
nc 5
nop 1
dl 0
loc 33
ccs 20
cts 20
cp 1
crap 5
rs 9.3222
c 0
b 0
f 0

3 Methods

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