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

Type   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 526
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 102
dl 0
loc 526
ccs 115
cts 115
cp 1
rs 5.04
c 0
b 0
f 0
wmc 57

26 Methods

Rating   Name   Duplication   Size   Complexity  
A wildcardMatch() 0 15 2
A isAlias() 0 3 1
A getParameter() 0 3 2
A getDescription() 0 11 4
A getSubType() 0 3 1
A getExtensions() 0 10 3
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 getDefaultExtension() 0 20 4
A setMediaComment() 0 4 1
A hasParameters() 0 3 1
A getParameters() 0 3 1
A getUnaliasedType() 0 3 2
A __construct() 0 6 2
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 isExperimental() 0 3 2
B toString() 0 16 8

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