Passed
Push — master ( 7ed8f5...1c78cc )
by mon
01:54
created

Type::setMediaComment()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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