Completed
Pull Request — master (#15)
by mon
02:43
created

Type::removeParameter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
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
     * Constructor.
62
     *
63
     * The type string will be parsed and the appropriate class vars set.
64
     *
65
     * @param string $type MIME type
66
     */
67 57
    public function __construct($type)
68
    {
69 57
        $this->parse($type);
70 50
    }
71
72
    /**
73
     * Parse a mime-type and set the class variables.
74
     *
75
     * @param string $type MIME type to parse
76
     *
77
     * @return void
78
     */
79 57
    protected function parse($type)
80
    {
81
        // Media and SubType are separated by a slash '/'.
82 57
        $media = TypeParser::parseStringPart($type, 0, '/');
83
84 56
        if (!$media['string']) {
85 3
            throw new MalformedTypeException('Media type not found');
86
        }
87 53
        if (!$media['delimiter_matched']) {
88 1
            throw new MalformedTypeException('Slash \'/\' to separate media type and subtype not found');
89
        }
90
91 52
        $this->media = strtolower($media['string']);
92 52
        $this->mediaComment = $media['comment'];
93
94
        // SubType and Parameters are separated by semicolons ';'.
95 52
        $sub = TypeParser::parseStringPart($type, $media['end_offset'] + 1, ';');
96
97 51
        if (!$sub['string']) {
98 1
            throw new MalformedTypeException('Media subtype not found');
99
        }
100
101 50
        $this->subType = strtolower($sub['string']);
102 50
        $this->subTypeComment = $sub['comment'];
103
104
        // Loops through the parameter.
105 50
        while ($sub['delimiter_matched']) {
106 26
            $sub = TypeParser::parseStringPart($type, $sub['end_offset'] + 1, ';');
107 26
            $tmp = explode('=', $sub['string'], 2);
108 26
            $p_name = trim($tmp[0]);
109 26
            $p_val = trim($tmp[1]);
110 26
            if ($p_val[0] == '"' && $p_val[strlen($p_val) - 1] == '"') {
111
                $p_val = substr($p_val, 1, -1);
112
            }
113 26
            $p_val = str_replace('\\"', '"', $p_val);
114 26
            $this->addParameter($p_name, $p_val, $sub['comment']);
115
        }
116 50
    }
117
118
    /**
119
     * Does this type have any parameters?
120
     *
121
     * @return boolean true if type has parameters, false otherwise
122
     */
123 28
    public function hasParameters()
124
    {
125 28
        return (bool) $this->parameters;
126
    }
127
128
    /**
129
     * Get a MIME type's parameters
130
     *
131
     * @return TypeParameter[] Type's parameters
132
     */
133 29
    public function getParameters()
134
    {
135 29
        return $this->parameters;
136
    }
137
138
    /**
139
     * Get a MIME type's parameter
140
     *
141
     * @param string $name Parameter name
142
     *
143
     * @return TypeParameter|null
144
     */
145 23
    public function getParameter($name)
146
    {
147 23
        return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
148
    }
149
150
    /**
151
     * Gets a MIME type's media.
152
     *
153
     * Note: 'media' refers to the portion before the first slash.
154
     *
155
     * @return string Type's media.
156
     */
157 43
    public function getMedia()
158
    {
159 43
        return $this->media;
160
    }
161
162
    /**
163
     * Gets a MIME type's media comment.
164
     *
165
     * @return string Type's media comment.
166
     */
167 28
    public function getMediaComment()
168
    {
169 28
        return $this->mediaComment;
170
    }
171
172
    /**
173
     * Sets a MIME type's media comment.
174
     *
175
     * @param string Type's media comment.
176
     *
177
     * @return $this
178
     */
179 1
    public function setMediaComment($comment)
180
    {
181 1
        $this->mediaComment = $comment;
182 1
        return $this;
183
    }
184
185
    /**
186
     * Gets a MIME type's subtype.
187
     *
188
     * @return string Type's subtype, null if invalid mime type.
189
     */
190 44
    public function getSubType()
191
    {
192 44
        return $this->subType;
193
    }
194
195
    /**
196
     * Gets a MIME type's subtype comment.
197
     *
198
     * @return string Type's subtype comment, null if invalid mime type.
199
     */
200 28
    public function getSubTypeComment()
201
    {
202 28
        return $this->subTypeComment;
203
    }
204
205
    /**
206
     * Sets a MIME type's subtype comment.
207
     *
208
     * @param string Type's subtype comment.
209
     *
210
     * @return $this
211
     */
212 1
    public function setSubTypeComment($comment)
213
    {
214 1
        $this->subTypeComment = $comment;
215 1
        return $this;
216
    }
217
218
    /**
219
     * Create a textual MIME type from object values
220
     *
221
     * This function performs the opposite function of parse().
222
     *
223
     * @param int $format The format of the output string.
224
     *
225
     * @return string MIME type string
226
     */
227 44
    public function toString($format = Type::FULL_TEXT)
228
    {
229 44
        $type = strtolower($this->media);
230 44
        if ($format > Type::FULL_TEXT && isset($this->mediaComment)) {
231 3
            $type .= ' (' .  $this->mediaComment . ')';
232
        }
233 44
        $type .= '/' . strtolower($this->subType);
234 44
        if ($format > Type::FULL_TEXT && isset($this->subTypeComment)) {
235 6
            $type .= ' (' .  $this->subTypeComment . ')';
236
        }
237 44
        if ($format > Type::SHORT_TEXT && count($this->parameters)) {
238 23
            foreach ($this->parameters as $parameter) {
239 23
                $type .= '; ' . $parameter->toString($format);
240
            }
241
        }
242 44
        return $type;
243
    }
244
245
    /**
246
     * Is this type experimental?
247
     *
248
     * Note: Experimental types are denoted by a leading 'x-' in the media or
249
     *       subtype, e.g. text/x-vcard or x-world/x-vrml.
250
     *
251
     * @return boolean true if type is experimental, false otherwise
252
     */
253 1
    public function isExperimental()
254
    {
255 1
        if (substr($this->getMedia(), 0, 2) == 'x-' || substr($this->getSubType(), 0, 2) == 'x-') {
256 1
            return true;
257
        }
258 1
        return false;
259
    }
260
261
    /**
262
     * Is this a vendor MIME type?
263
     *
264
     * Note: Vendor types are denoted with a leading 'vnd. in the subtype.
265
     *
266
     * @return boolean true if type is a vendor type, false otherwise
267
     */
268 1
    public function isVendor()
269
    {
270 1
        if (substr($this->getSubType(), 0, 4) == 'vnd.') {
271 1
            return true;
272
        }
273 1
        return false;
274
    }
275
276
    /**
277
     * Is this a wildcard type?
278
     *
279
     * @return boolean true if type is a wildcard, false otherwise.
280
     */
281 14
    public function isWildcard()
282
    {
283 14
        if (($this->getMedia() === '*' && $this->getSubtype() === '*') || strpos($this->getSubtype(), '*') !== false) {
284 8
            return true;
285
        }
286 11
        return false;
287
    }
288
289
    /**
290
     * Is this an alias?
291
     *
292
     * @return boolean true if type is an alias, false otherwise.
293
     */
294 1
    public function isAlias()
295
    {
296 1
        if ($this->isWildcard()) {
297 1
            return false;
298
        }
299
300 1
        $map = MapHandler::map();
301 1
        $subject = $this->toString(static::SHORT_TEXT);
302 1
        return $map->hasAlias($subject);
303
    }
304
305
    /**
306
     * Perform a wildcard match on a MIME type
307
     *
308
     * Example:
309
     * $type = new Type('image/png');
310
     * $type->wildcardMatch('image/*');
311
     *
312
     * @param string $wildcard Wildcard to check against
313
     *
314
     * @return boolean true if there was a match, false otherwise
315
     */
316 1
    public function wildcardMatch($wildcard)
317
    {
318 1
        $wildcard_type = new static($wildcard);
319
320 1
        if (!$wildcard_type->isWildcard()) {
321 1
            return false;
322
        }
323
324 1
        $wildcard_re = strtr($wildcard_type->toString(static::SHORT_TEXT), [
325 1
            '/' => '\\/',
326
            '*' => '.*',
327
        ]);
328 1
        $subject = $this->toString(static::SHORT_TEXT);
329
330 1
        return preg_match("/$wildcard_re/", $subject) === 1;
331
    }
332
333
    /**
334
     * Add a parameter to this type
335
     *
336
     * @param string $name    Parameter name
337
     * @param string $value   Parameter value
338
     * @param string $comment Comment for this parameter
339
     *
340
     * @return void
341
     */
342 26
    public function addParameter($name, $value, $comment = null)
343
    {
344 26
        $this->parameters[$name] = new TypeParameter($name, $value, $comment);
345 26
    }
346
347
    /**
348
     * Remove a parameter from this type
349
     *
350
     * @param string $name Parameter name
351
     *
352
     * @return void
353
     */
354 1
    public function removeParameter($name)
355
    {
356 1
        unset($this->parameters[$name]);
357 1
    }
358
359
    /**
360
     * Builds a list of MIME types.
361
     *
362
     * If the current type is a wildcard, than all the types matching the
363
     * wildcard will be returned.
364
     *
365
     * @param bool $strict
366
     *   (Optional) if true a MappingException is thrown when no type is
367
     *   found, if false it returns an empty array as a default.
368
     *   Defaults to true.
369
     *
370
     * @throws MappingException if no mapping found and $strict is true.
371
     *
372
     * @return string[]
373
     */
374 6
    public function buildTypesList($strict = true)
375
    {
376 6
        $map = MapHandler::map();
377 6
        $subject = $this->toString(static::SHORT_TEXT);
378
379
        // Find all types.
380 6
        $types = [];
381 6
        if (!$this->isWildcard()) {
382 6
            if ($map->hasType($subject)) {
383 6
                $types[] = $subject;
384
            }
385
        } else {
386 1
            foreach ($map->listTypes($subject) as $t) {
387 1
                $types[] = $t;
388
            }
389
        }
390
391
        // No types found, throw exception or return emtpy array.
392 6
        if (empty($types)) {
393 3
            if ($strict) {
394 1
                throw new MappingException('No MIME type found for ' . $subject . ' in map');
395
            } else {
396 2
                return [];
397
            }
398
        }
399
400 5
        return $types;
401
    }
402
403
    /**
404
     * Returns a description for the MIME type, if existing in the map.
405
     *
406
     * @param bool $include_acronym
407
     *   (Optional) if true and an acronym description exists for the type,
408
     *   the returned description will contain the acronym and its description,
409
     *   appended with a comma. Defaults to false.
410
     *
411
     * @return string|null
412
     */
413 1
    public function getDescription($include_acronym = false)
414
    {
415 1
        if ($this->isWildcard()) {
416 1
            return null;
417
        }
418
419 1
        $map = MapHandler::map();
420 1
        $subject = $this->toString(static::SHORT_TEXT);
421 1
        $descriptions = $map->getTypeDescriptions($subject);
422 1
        $res = null;
423 1
        if (isset($descriptions[0])) {
424 1
            $res = $descriptions[0];
425
        }
426 1
        if ($include_acronym && isset($descriptions[1])) {
427 1
            $res .= ', ' . $descriptions[1];
428
        }
429 1
        return $res;
430
    }
431
432
    /**
433
     * Returns all the aliases related to the MIME type(s).
434
     *
435
     * If the current type is a wildcard, than all aliases of all the
436
     * types matching the wildcard will be returned.
437
     *
438
     * @param bool $strict
439
     *   (Optional) if true a MappingException is thrown when no mapping is
440
     *   found, if false it returns an empty array as a default.
441
     *   Defaults to true.
442
     *
443
     * @throws MappingException if no mapping found and $strict is true.
444
     *
445
     * @return string[]
446
     */
447 1 View Code Duplication
    public function getAliases($strict = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
448
    {
449 1
        $map = MapHandler::map();
450 1
        $types = $this->buildTypesList($strict);
451
452
        // Build the array of aliases.
453 1
        $aliases = [];
454 1
        foreach ($types as $t) {
455 1
            foreach ($map->getTypeAliases($t) as $a) {
456 1
                $aliases[$a] = $a;
457
            }
458
        }
459
460 1
        return array_keys($aliases);
461
    }
462
463
    /**
464
     * Returns the MIME type's preferred file extension.
465
     *
466
     * @param bool $strict
467
     *   (Optional) if true a MappingException is thrown when no mapping is
468
     *   found, if false it returns null as a default.
469
     *   Defaults to true.
470
     *
471
     * @throws MappingException if no mapping found and $strict is true.
472
     *
473
     * @return string
474
     */
475 7
    public function getDefaultExtension($strict = true)
476
    {
477 7
        $map = MapHandler::map();
478 7
        $subject = $this->toString(static::SHORT_TEXT);
479
480 7
        if (!$this->isWildcard()) {
481 4
            $proceed = $map->hasType($subject);
482
        } else {
483 3
            $proceed = count($map->listTypes($subject)) === 1;
484
        }
485
486 7
        if (!$proceed) {
487 5
            if ($strict) {
488 4
                throw new MappingException('Cannot determine default extension for type: ' . $this->toString(static::SHORT_TEXT));
489
            } else {
490 1
                return null;
491
            }
492
        }
493
494 3
        return $this->getExtensions()[0];
495
    }
496
497
    /**
498
     * Returns all the file extensions related to the MIME type(s).
499
     *
500
     * If the current type is a wildcard, than all extensions of all the
501
     * types matching the wildcard will be returned.
502
     *
503
     * @param bool $strict
504
     *   (Optional) if true a MappingException is thrown when no mapping is
505
     *   found, if false it returns an empty array as a default.
506
     *   Defaults to true.
507
     *
508
     * @throws MappingException if no mapping found and $strict is true.
509
     *
510
     * @return string[]
511
     */
512 6 View Code Duplication
    public function getExtensions($strict = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
513
    {
514 6
        $map = MapHandler::map();
515 6
        $types = $this->buildTypesList($strict);
516
517
        // Build the array of extensions.
518 5
        $extensions = [];
519 5
        foreach ($types as $t) {
520 5
            foreach ($map->getTypeExtensions($t) as $e) {
521 5
                $extensions[$e] = $e;
522
            }
523
        }
524
525 5
        return array_keys($extensions);
526
    }
527
}
528