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

Type::getExtensions()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15

Duplication

Lines 15
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 15
loc 15
ccs 0
cts 0
cp 0
rs 9.7666
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 12
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 54
    public function __construct($type)
68
    {
69 54
        $this->parse($type);
70 47
    }
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 54
    protected function parse($type)
80
    {
81
        // Media and SubType are separated by a slash '/'.
82 54
        $media = TypeParser::parseStringPart($type, 0, '/');
83
84 53
        if (!$media['string']) {
85 3
            throw new MalformedTypeException('Media type not found');
86
        }
87 50
        if (!$media['delimiter_matched']) {
88 1
            throw new MalformedTypeException('Slash \'/\' to separate media type and subtype not found');
89
        }
90
91 49
        $this->media = strtolower($media['string']);
92 49
        $this->mediaComment = $media['comment'];
93
94
        // SubType and Parameters are separated by semicolons ';'.
95 49
        $sub = TypeParser::parseStringPart($type, $media['end_offset'] + 1, ';');
96
97 48
        if (!$sub['string']) {
98 1
            throw new MalformedTypeException('Media subtype not found');
99
        }
100
101 47
        $this->subType = strtolower($sub['string']);
102 47
        $this->subTypeComment = $sub['comment'];
103
104
        // Loops through the parameter.
105 47
        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 47
    }
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 42
     * @return $this
178
     */
179 42
    public function setMediaComment($comment)
180
    {
181
        $this->mediaComment = $comment;
182
        return $this;
183
    }
184
185
    /**
186
     * Gets a MIME type's subtype.
187 28
     *
188
     * @return string Type's subtype, null if invalid mime type.
189 28
     */
190
    public function getSubType()
191
    {
192
        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
    public function getSubTypeComment()
201 41
    {
202
        return $this->subTypeComment;
203 41
    }
204 41
205 2
    /**
206
     * Sets a MIME type's subtype comment.
207 41
     *
208 41
     * @param string Type's subtype comment.
209 5
     *
210
     * @return $this
211 41
     */
212 23
    public function getSubTypeComment($comment)
213 23
    {
214
        $this->subTypeComment = $comment;
215
        return $this;
216 41
    }
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 1
    public function toString($format = Type::FULL_TEXT)
228
    {
229 1
        $type = strtolower($this->media);
230 1
        if ($format > Type::FULL_TEXT && isset($this->mediaComment)) {
231
            $type .= ' (' .  $this->mediaComment . ')';
232 1
        }
233
        $type .= '/' . strtolower($this->subType);
234
        if ($format > Type::FULL_TEXT && isset($this->subTypeComment)) {
235
            $type .= ' (' .  $this->subTypeComment . ')';
236
        }
237
        if ($format > Type::SHORT_TEXT && count($this->parameters)) {
238
            foreach ($this->parameters as $parameter) {
239
                $type .= '; ' . $parameter->toString($format);
240
            }
241
        }
242 1
        return $type;
243
    }
244 1
245 1
    /**
246
     * Is this type experimental?
247 1
     *
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
    public function isExperimental()
254
    {
255 12
        if (substr($this->getMedia(), 0, 2) == 'x-' || substr($this->getSubType(), 0, 2) == 'x-') {
256
            return true;
257 12
        }
258 6
        return false;
259
    }
260 9
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
    public function isVendor()
269
    {
270
        if (substr($this->getSubType(), 0, 4) == 'vnd.') {
271
            return true;
272
        }
273
        return false;
274 1
    }
275
276 1
    /**
277
     * Is this a wildcard type?
278 1
     *
279 1
     * @return boolean true if type is a wildcard, false otherwise.
280
     */
281
    public function isWildcard()
282 1
    {
283 1
        if (($this->getMedia() === '*' && $this->getSubtype() === '*') || strpos($this->getSubtype(), '*') !== false) {
284
            return true;
285
        }
286 1
        return false;
287
    }
288 1
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 26
        $map = MapHandler::map();
301
        $subject = $this->toString(static::SHORT_TEXT);
302 26
        return $map->hasAlias($subject);
303 26
    }
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 1
     * @param string $wildcard Wildcard to check against
313
     *
314 1
     * @return boolean true if there was a match, false otherwise
315 1
     */
316
    public function wildcardMatch($wildcard)
317
    {
318
        $wildcard_type = new static($wildcard);
319
320
        if (!$wildcard_type->isWildcard()) {
321
            return false;
322
        }
323
324
        $wildcard_re = strtr($wildcard_type->toString(static::SHORT_TEXT), [
325
            '/' => '\\/',
326
            '*' => '.*',
327
        ]);
328
        $subject = $this->toString(static::SHORT_TEXT);
329 7
330
        return preg_match("/$wildcard_re/", $subject) === 1;
331 7
    }
332 7
333
    /**
334 7
     * Add a parameter to this type
335 4
     *
336
     * @param string $name    Parameter name
337 3
     * @param string $value   Parameter value
338
     * @param string $comment Comment for this parameter
339
     *
340 7
     * @return void
341 5
     */
342 4
    public function addParameter($name, $value, $comment = null)
343
    {
344 1
        $this->parameters[$name] = new TypeParameter($name, $value, $comment);
345
    }
346
347
    /**
348 3
     * Remove a parameter from this type
349
     *
350
     * @param string $name Parameter name
351
     *
352
     * @return void
353
     */
354
    public function removeParameter($name)
355
    {
356
        unset($this->parameters[$name]);
357
    }
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 6
     *   (Optional) if true a MappingException is thrown when no type is
367
     *   found, if false it returns an empty array as a default.
368 6
     *   Defaults to true.
369 6
     *
370
     * @throws MappingException if no mapping found and $strict is true.
371
     *
372 6
     * @return string[]
373 6
     */
374 6
    public function buildTypesList($strict = true)
375 6
    {
376
        $map = MapHandler::map();
377
        $subject = $this->toString(static::SHORT_TEXT);
378 1
379 1
        // Find all types.
380
        $types = [];
381
        if (!$this->isWildcard()) {
382
            if ($map->hasType($subject)) {
383
                $types[] = $subject;
384 6
            }
385 3
        } else {
386 1
            foreach ($map->listTypes($subject) as $t) {
387
                $types[] = $t;
388 2
            }
389
        }
390
391
        // No types found, throw exception or return emtpy array.
392
        if (empty($types)) {
393 5
            if ($strict) {
394 5
                throw new MappingException('No MIME type found for ' . $subject . ' in map');
395 5
            } else {
396 5
                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 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
        $map = MapHandler::map();
421
        $types = $this->buildTypesList($strict);
422
423
        // Build the array of aliases.
424
        $aliases = [];
425
        foreach ($types as $t) {
426
            foreach ($map->getTypeAliases($t) as $a) {
427
                $aliases[$a] = $a;
428
            }
429
        }
430
431
        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
    public function getDefaultExtension($strict = true)
447
    {
448
        $map = MapHandler::map();
449
        $subject = $this->toString(static::SHORT_TEXT);
450
451
        if (!$this->isWildcard()) {
452
            $proceed = $map->hasType($subject);
453
        } else {
454
            $proceed = count($map->listTypes($subject)) === 1;
455
        }
456
457
        if (!$proceed) {
458
            if ($strict) {
459
                throw new MappingException('Cannot determine default extension for type: ' . $this->toString(static::SHORT_TEXT));
460
            } else {
461
                return null;
462
            }
463
        }
464
465
        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 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
        $map = MapHandler::map();
486
        $types = $this->buildTypesList($strict);
487
488
        // Build the array of extensions.
489
        $extensions = [];
490
        foreach ($types as $t) {
491
            foreach ($map->getTypeExtensions($t) as $e) {
492
                $extensions[$e] = $e;
493
            }
494
        }
495
496
        return array_keys($extensions);
497
    }
498
}
499