Test Failed
Pull Request — master (#18)
by mon
01:40
created

Type   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 513
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 95
dl 0
loc 513
ccs 110
cts 110
cp 1
rs 4.08
c 0
b 0
f 0
wmc 59

26 Methods

Rating   Name   Duplication   Size   Complexity  
A isAlias() 0 3 1
A getParameter() 0 3 2
A getDescription() 0 11 4
A getSubType() 0 3 1
A isWildcard() 0 3 3
A removeParameter() 0 3 1
A getMediaComment() 0 3 1
A buildTypesList() 0 26 6
A setSubTypeComment() 0 4 1
A setSubType() 0 4 1
A isVendor() 0 3 1
A setMediaComment() 0 4 1
A hasParameters() 0 3 1
A getParameters() 0 3 1
A getUnaliasedType() 0 3 2
A __construct() 0 4 1
A addParameter() 0 3 1
A setMedia() 0 4 1
A getSubTypeComment() 0 3 1
A getMedia() 0 3 1
A getAliases() 0 21 5
A wildcardMatch() 0 15 2
A isExperimental() 0 3 2
B toString() 0 16 8
A getExtensions() 0 10 3
B getDefaultExtension() 0 12 7

How to fix   Complexity   

Complex Class

Complex classes like Type often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Type, and based on these observations, apply Extract Interface, too.

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