Passed
Pull Request — master (#15)
by mon
02:14
created

Type::getAliases()   B

Complexity

Conditions 8
Paths 15

Size

Total Lines 36

Duplication

Lines 36
Ratio 100 %

Code Coverage

Tests 13
CRAP Score 9.372

Importance

Changes 0
Metric Value
dl 36
loc 36
ccs 13
cts 18
cp 0.7221
rs 8.0995
c 0
b 0
f 0
cc 8
nc 15
nop 1
crap 9.372
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
     * Get 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
     * Get 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
     * Get a MIME type's subtype.
174
     *
175
     * @return string Type's subtype, null if invalid mime type.
176
     */
177 42
    public function getSubType()
178
    {
179 42
        return $this->subType;
180
    }
181
182
    /**
183
     * Get a MIME type's subtype comment.
184
     *
185
     * @return string Type's subtype comment, null if invalid mime type.
186
     */
187 28
    public function getSubTypeComment()
188
    {
189 28
        return $this->subTypeComment;
190
    }
191
192
    /**
193
     * Create a textual MIME type from object values
194
     *
195
     * This function performs the opposite function of parse().
196
     *
197
     * @param int $format The format of the output string.
198
     *
199
     * @return string MIME type string
200
     */
201 41
    public function toString($format = Type::FULL_TEXT)
202
    {
203 41
        $type = strtolower($this->media);
204 41
        if ($format > Type::FULL_TEXT && isset($this->mediaComment)) {
205 2
            $type .= ' (' .  $this->mediaComment . ')';
206
        }
207 41
        $type .= '/' . strtolower($this->subType);
208 41
        if ($format > Type::FULL_TEXT && isset($this->subTypeComment)) {
209 5
            $type .= ' (' .  $this->subTypeComment . ')';
210
        }
211 41
        if ($format > Type::SHORT_TEXT && count($this->parameters)) {
212 23
            foreach ($this->parameters as $parameter) {
213 23
                $type .= '; ' . $parameter->toString($format);
214
            }
215
        }
216 41
        return $type;
217
    }
218
219
    /**
220
     * Is this type experimental?
221
     *
222
     * Note: Experimental types are denoted by a leading 'x-' in the media or
223
     *       subtype, e.g. text/x-vcard or x-world/x-vrml.
224
     *
225
     * @return boolean true if type is experimental, false otherwise
226
     */
227 1
    public function isExperimental()
228
    {
229 1
        if (substr($this->getMedia(), 0, 2) == 'x-' || substr($this->getSubType(), 0, 2) == 'x-') {
230 1
            return true;
231
        }
232 1
        return false;
233
    }
234
235
    /**
236
     * Is this a vendor MIME type?
237
     *
238
     * Note: Vendor types are denoted with a leading 'vnd. in the subtype.
239
     *
240
     * @return boolean true if type is a vendor type, false otherwise
241
     */
242 1
    public function isVendor()
243
    {
244 1
        if (substr($this->getSubType(), 0, 4) == 'vnd.') {
245 1
            return true;
246
        }
247 1
        return false;
248
    }
249
250
    /**
251
     * Is this a wildcard type?
252
     *
253
     * @return boolean true if type is a wildcard, false otherwise
254
     */
255 12
    public function isWildcard()
256
    {
257 12
        if (($this->getMedia() === '*' && $this->getSubtype() === '*') || strpos($this->getSubtype(), '*') !== false) {
258 6
            return true;
259
        }
260 9
        return false;
261
    }
262
263
    /**
264
     * Perform a wildcard match on a MIME type
265
     *
266
     * Example:
267
     * $type = new Type('image/png');
268
     * $type->wildcardMatch('image/*');
269
     *
270
     * @param string $wildcard Wildcard to check against
271
     *
272
     * @return boolean true if there was a match, false otherwise
273
     */
274 1
    public function wildcardMatch($wildcard)
275
    {
276 1
        $wildcard_type = new static($wildcard);
277
278 1
        if (!$wildcard_type->isWildcard()) {
279 1
            return false;
280
        }
281
282 1
        $wildcard_re = strtr($wildcard_type->toString(static::SHORT_TEXT), [
283 1
            '/' => '\\/',
284
            '*' => '.*',
285
        ]);
286 1
        $subject = $this->toString(static::SHORT_TEXT);
287
288 1
        return preg_match("/$wildcard_re/", $subject) === 1;
289
    }
290
291
    /**
292
     * Add a parameter to this type
293
     *
294
     * @param string $name    Parameter name
295
     * @param string $value   Parameter value
296
     * @param string $comment Comment for this parameter
297
     *
298
     * @return void
299
     */
300 26
    public function addParameter($name, $value, $comment = null)
301
    {
302 26
        $this->parameters[$name] = new TypeParameter($name, $value, $comment);
303 26
    }
304
305
    /**
306
     * Remove a parameter from this type
307
     *
308
     * @param string $name Parameter name
309
     *
310
     * @return void
311
     */
312 1
    public function removeParameter($name)
313
    {
314 1
        unset($this->parameters[$name]);
315 1
    }
316
317
    /**
318
     * Returns all the aliases related to the MIME type(s).
319
     *
320
     * If the current type is a wildcard, than all aliases of all the
321
     * types matching the wildcard will be returned.
322
     *
323
     * @param bool $strict
324
     *   (Optional) if true a MappingException is thrown when no mapping is
325
     *   found, if false it returns an empty array as a default.
326
     *   Defaults to true.
327
     *
328
     * @throws MappingException if no mapping found and $strict is true.
329
     *
330
     * @return string[]
331
     */
332 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...
333
    {
334 1
        $map = MapHandler::map();
335 1
        $subject = $this->toString(static::SHORT_TEXT);
336
337
        // Find all types.
338 1
        $types = [];
339 1
        if (!$this->isWildcard()) {
340 1
            if ($map->hasType($subject)) {
341 1
                $types[] = $subject;
342
            }
343
        } else {
344
            foreach ($map->listTypes($subject) as $t) {
345
                $types[] = $t;
346
            }
347
        }
348
349
        // No types found, throw exception or return emtpy array.
350 1
        if (empty($types)) {
351
            if ($strict) {
352
                throw new MappingException('No MIME type found for ' . $subject . ' in map');
353
            } else {
354
                return [];
355
            }
356
        }
357
358
        // Build the array of extensions.
359 1
        $aliases = [];
360 1
        foreach ($types as $t) {
361 1
            foreach ($map->getTypeAliases($t) as $a) {
362 1
                $aliases[$a] = $a;
363
            }
364
        }
365
366 1
        return array_keys($aliases);
367
    }
368
369
    /**
370
     * Returns the MIME type's preferred file extension.
371
     *
372
     * @param bool $strict
373
     *   (Optional) if true a MappingException is thrown when no mapping is
374
     *   found, if false it returns null as a default.
375
     *   Defaults to true.
376
     *
377
     * @throws MappingException if no mapping found and $strict is true.
378
     *
379
     * @return string
380
     */
381 7
    public function getDefaultExtension($strict = true)
382
    {
383 7
        $map = MapHandler::map();
384 7
        $subject = $this->toString(static::SHORT_TEXT);
385
386 7
        if (!$this->isWildcard()) {
387 4
            $proceed = $map->hasType($subject);
388
        } else {
389 3
            $proceed = count($map->listTypes($subject)) === 1;
390
        }
391
392 7
        if (!$proceed) {
393 5
            if ($strict) {
394 4
                throw new MappingException('Cannot determine default extension for type: ' . $this->toString(static::SHORT_TEXT));
395
            } else {
396 1
                return null;
397
            }
398
        }
399
400 3
        return $this->getExtensions()[0];
401
    }
402
403
    /**
404
     * Returns all the file extensions related to the MIME type(s).
405
     *
406
     * If the current type is a wildcard, than all extensions 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 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...
419
    {
420 6
        $map = MapHandler::map();
421 6
        $subject = $this->toString(static::SHORT_TEXT);
422
423
        // Find all types.
424 6
        $types = [];
425 6
        if (!$this->isWildcard()) {
426 6
            if ($map->hasType($subject)) {
427 6
                $types[] = $subject;
428
            }
429
        } else {
430 1
            foreach ($map->listTypes($subject) as $t) {
431 1
                $types[] = $t;
432
            }
433
        }
434
435
        // No types found, throw exception or return emtpy array.
436 6
        if (empty($types)) {
437 3
            if ($strict) {
438 1
                throw new MappingException('No MIME type found for ' . $subject . ' in map');
439
            } else {
440 2
                return [];
441
            }
442
        }
443
444
        // Build the array of extensions.
445 5
        $extensions = [];
446 5
        foreach ($types as $t) {
447 5
            foreach ($map->getTypeExtensions($t) as $e) {
448 5
                $extensions[$e] = $e;
449
            }
450
        }
451
452 5
        return array_keys($extensions);
453
    }
454
}
455