Passed
Pull Request — master (#17)
by mon
01:53
created

TypeParser::parse()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 19
nc 5
nop 2
dl 0
loc 33
ccs 20
cts 20
cp 1
crap 5
rs 9.3222
c 0
b 0
f 0
1
<?php
2
3
namespace FileEye\MimeMap;
4
5
/**
6
 * Class for parsing RFC 2045 Content-Type Header Fields.
7
 */
8
class TypeParser
9
{
10
    /**
11
     * Parse a mime-type and set the class variables.
12
     *
13
     * @param string $type_string
14
     *   MIME type string to parse.
15
     * @param Type $type
16
     *   The Type object to receive the components.
17
     *
18
     * @return void
19
     */
20 61
    public static function parse($type_string, Type $type)
21
    {
22
        // Media and SubType are separated by a slash '/'.
23 61
        $media = static::parseStringPart($type_string, 0, '/');
24
25 60
        if (!$media['string']) {
26 3
            throw new MalformedTypeException('Media type not found');
27
        }
28 57
        if (!$media['delimiter_matched']) {
29 1
            throw new MalformedTypeException('Slash \'/\' to separate media type and subtype not found');
30
        }
31
32 56
        $type->setMedia(strtolower($media['string']));
0 ignored issues
show
Bug introduced by
$media['string'] of type void is incompatible with the type string expected by parameter $str of strtolower(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

32
        $type->setMedia(strtolower(/** @scrutinizer ignore-type */ $media['string']));
Loading history...
33 56
        $type->setMediaComment($media['comment']);
34
35
        // SubType and Parameters are separated by semicolons ';'.
36 56
        $sub = static::parseStringPart($type_string, $media['end_offset'] + 1, ';');
37
38 55
        if (!$sub['string']) {
39 1
            throw new MalformedTypeException('Media subtype not found');
40
        }
41
42 54
        $type->setSubType(strtolower($sub['string']));
43 54
        $type->setSubTypeComment($sub['comment']);
44
45
        // Loops through the parameter.
46 54
        while ($sub['delimiter_matched']) {
47 27
            $sub = static::parseStringPart($type_string, $sub['end_offset'] + 1, ';');
48 27
            $tmp = explode('=', $sub['string'], 2);
49 27
            $p_name = trim($tmp[0]);
50 27
            $p_val = trim($tmp[1]);
51 27
            $p_val = str_replace('\\"', '"', $p_val);
52 27
            $type->addParameter($p_name, $p_val, $sub['comment']);
53
        }
54 54
    }
55
56
    /**
57
     * Parses a part of the content MIME type string.
58
     *
59
     * Splits string and comment until a delimiter is found.
60
     *
61
     * @param string $string     Input string.
62
     * @param int $offset        Offset to start parsing from.
63
     * @param string $delimiter  Stop parsing when delimiter found.
64
     *
65
     * @return array An array with the following keys:
66
     *   'string' - the uncommented part of $string
67
     *   'comment' - the comment part of $string
68
     *   'delimiter_matched' - true if a $delimiter stopped the parsing, false
69
     *                         otherwise
70
     *   'end_offset' - the last position parsed in $string.
71
     */
72 61
    public static function parseStringPart($string, $offset, $delimiter)
73
    {
74 61
        $inquote   = false;
75 61
        $escaped   = false;
76 61
        $incomment = 0;
77 61
        $newstring = '';
78 61
        $comment = '';
79
80 61
        for ($n = $offset; $n < strlen($string); $n++) {
81 59
            if ($string[$n] === $delimiter && !$escaped && !$inquote && $incomment === 0) {
82 57
                break;
83
            }
84
85 58
            if ($escaped) {
86 4
                if ($incomment == 0) {
87 2
                    $newstring .= $string[$n];
88
                } else {
89 2
                    $comment .= $string[$n];
90
                }
91 4
                $escaped = false;
92 4
                continue;
93
            }
94
95 58
            if ($string[$n] == '\\') {
96 4
                if ($incomment > 0) {
97 2
                    $comment .= $string[$n];
98
                }
99 4
                $escaped = true;
100 4
                continue;
101
            }
102
103 58
            if (!$inquote && $incomment > 0 && $string[$n] == ')') {
104 21
                $incomment--;
105 21
                if ($incomment == 0) {
106 19
                    $comment .= ' ';
107
                }
108 21
                continue;
109
            }
110
111 58
            if (!$inquote && $string[$n] == '(') {
112 21
                $incomment++;
113 21
                continue;
114
            }
115
116 58
            if ($string[$n] == '"') {
117 10
                if ($incomment > 0) {
118 1
                    $comment .= $string[$n];
119
                } else {
120 9
                    if ($inquote) {
121 9
                        $inquote = false;
122
                    } else {
123 9
                        $inquote = true;
124
                    }
125
                }
126 10
                continue;
127
            }
128
129 58
            if ($incomment == 0) {
130 58
                $newstring .= $string[$n];
131 58
                continue;
132
            }
133
134 21
            $comment .= $string[$n];
135
        }
136
137 61
        if ($incomment > 0) {
138 2
            throw new MalformedTypeException('Comment closing bracket missing: ' . $comment);
139
        }
140
141
        return [
142 60
          'string' => empty($newstring) ? null : trim($newstring),
0 ignored issues
show
introduced by
The condition empty($newstring) is always true.
Loading history...
143 60
          'comment' => empty($comment) ? null : trim($comment),
0 ignored issues
show
introduced by
The condition empty($comment) is always true.
Loading history...
144 60
          'delimiter_matched' => isset($string[$n]) ? ($string[$n] === $delimiter) : false,
145 60
          'end_offset' => $n,
146
        ];
147
    }
148
}
149