Test Failed
Pull Request — master (#15)
by mon
02:11
created

Type::getExtensions()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15

Duplication

Lines 15
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 15
loc 15
ccs 8
cts 8
cp 1
rs 9.7666
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
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 55
    public function __construct($type)
68
    {
69 55
        $this->parse($type);
70 48
    }
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 55
    protected function parse($type)
80
    {
81
        // Media and SubType are separated by a slash '/'.
82 55
        $media = TypeParser::parseStringPart($type, 0, '/');
83
84 54
        if (!$media['string']) {
85 3
            throw new MalformedTypeException('Media type not found');
86
        }
87 51
        if (!$media['delimiter_matched']) {
88 1
            throw new MalformedTypeException('Slash \'/\' to separate media type and subtype not found');
89
        }
90
91 50
        $this->media = strtolower($media['string']);
92 50
        $this->mediaComment = $media['comment'];
93
94
        // SubType and Parameters are separated by semicolons ';'.
95 50
        $sub = TypeParser::parseStringPart($type, $media['end_offset'] + 1, ';');
96
97 49
        if (!$sub['string']) {
98 1
            throw new MalformedTypeException('Media subtype not found');
99
        }
100
101 48
        $this->subType = strtolower($sub['string']);
102 48
        $this->subTypeComment = $sub['comment'];
103
104
        // Loops through the parameter.
105 48
        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 48
    }
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 41
    public function getMedia()
158
    {
159 41
        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 42
    public function getSubType()
191
    {
192 42
        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 42
    public function toString($format = Type::FULL_TEXT)
228
    {
229 42
        $type = strtolower($this->media);
230 42
        if ($format > Type::FULL_TEXT && isset($this->mediaComment)) {
231 3
            $type .= ' (' .  $this->mediaComment . ')';
232
        }
233 42
        $type .= '/' . strtolower($this->subType);
234 42
        if ($format > Type::FULL_TEXT && isset($this->subTypeComment)) {
235 6
            $type .= ' (' .  $this->subTypeComment . ')';
236
        }
237 42
        if ($format > Type::SHORT_TEXT && count($this->parameters)) {
238 23
            foreach ($this->parameters as $parameter) {
239 23
                $type .= '; ' . $parameter->toString($format);
240
            }
241
        }
242 42
        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 12
    public function isWildcard()
282
    {
283 12
        if (($this->getMedia() === '*' && $this->getSubtype() === '*') || strpos($this->getSubtype(), '*') !== false) {
284 6
            return true;
285
        }
286 9
        return false;
287
    }
288
289
    /**
290
     * Is this an alias?
291
     *
292
     * @return boolean true if type is an alias, false otherwise.
293
     */
294
    public function isAlias()
295
    {
296
        if ($this->isWildcard) {
0 ignored issues
show
Bug introduced by
The property isWildcard does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
297
            return false;
298
        }
299
300
        $map = MapHandler::map();
301
        $subject = $this->toString(static::SHORT_TEXT);
302
        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 all the aliases related to the MIME type(s).
405
     *
406
     * If the current type is a wildcard, than all aliases of all the
407
     * types matching the wildcard will be returned.
408
     *
409
     * @param bool $strict
410
     *   (Optional) if true a MappingException is thrown when no mapping is
411
     *   found, if false it returns an empty array as a default.
412
     *   Defaults to true.
413
     *
414
     * @throws MappingException if no mapping found and $strict is true.
415
     *
416
     * @return string[]
417
     */
418 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...
419
    {
420 1
        $map = MapHandler::map();
421 1
        $types = $this->buildTypesList($strict);
422
423
        // Build the array of aliases.
424 1
        $aliases = [];
425 1
        foreach ($types as $t) {
426 1
            foreach ($map->getTypeAliases($t) as $a) {
427 1
                $aliases[$a] = $a;
428
            }
429
        }
430
431 1
        return array_keys($aliases);
432
    }
433
434
    /**
435
     * Returns the MIME type's preferred file extension.
436
     *
437
     * @param bool $strict
438
     *   (Optional) if true a MappingException is thrown when no mapping is
439
     *   found, if false it returns null as a default.
440
     *   Defaults to true.
441
     *
442
     * @throws MappingException if no mapping found and $strict is true.
443
     *
444
     * @return string
445
     */
446 7
    public function getDefaultExtension($strict = true)
447
    {
448 7
        $map = MapHandler::map();
449 7
        $subject = $this->toString(static::SHORT_TEXT);
450
451 7
        if (!$this->isWildcard()) {
452 4
            $proceed = $map->hasType($subject);
453
        } else {
454 3
            $proceed = count($map->listTypes($subject)) === 1;
455
        }
456
457 7
        if (!$proceed) {
458 5
            if ($strict) {
459 4
                throw new MappingException('Cannot determine default extension for type: ' . $this->toString(static::SHORT_TEXT));
460
            } else {
461 1
                return null;
462
            }
463
        }
464
465 3
        return $this->getExtensions()[0];
466
    }
467
468
    /**
469
     * Returns all the file extensions related to the MIME type(s).
470
     *
471
     * If the current type is a wildcard, than all extensions of all the
472
     * types matching the wildcard will be returned.
473
     *
474
     * @param bool $strict
475
     *   (Optional) if true a MappingException is thrown when no mapping is
476
     *   found, if false it returns an empty array as a default.
477
     *   Defaults to true.
478
     *
479
     * @throws MappingException if no mapping found and $strict is true.
480
     *
481
     * @return string[]
482
     */
483 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...
484
    {
485 6
        $map = MapHandler::map();
486 6
        $types = $this->buildTypesList($strict);
487
488
        // Build the array of extensions.
489 5
        $extensions = [];
490 5
        foreach ($types as $t) {
491 5
            foreach ($map->getTypeExtensions($t) as $e) {
492 5
                $extensions[$e] = $e;
493
            }
494
        }
495
496 5
        return array_keys($extensions);
497
    }
498
}
499