Completed
Push — master ( 7c761d...bd1c1c )
by Frederik
02:03
created

HeaderValue::fromString()   C

Complexity

Conditions 9
Paths 18

Size

Total Lines 63
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 9.1023

Importance

Changes 0
Metric Value
dl 0
loc 63
ccs 33
cts 37
cp 0.8919
rs 6.6149
c 0
b 0
f 0
cc 9
eloc 39
nc 18
nop 1
crap 9.1023

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Header;
5
6
/**
7
 * Class HeaderValue
8
 * @package Genkgo\Mail\Header
9
 */
10
final class HeaderValue
11
{
12
    /**
13
     *
14
     */
15
    private CONST PARSE_START = 1;
16
    /**
17
     *
18
     */
19
    private CONST PARSE_QUOTE = 2;
20
    /**
21
     * @var string
22
     */
23
    private $value;
24
    /**
25
     * @var array
26
     */
27
    private $parameters = [];
28
    /**
29
     * @var bool
30
     */
31
    private $needsEncoding = true;
32
33
    /**
34
     * HeaderValue constructor.
35
     * @param string $value
36
     */
37 89
    public function __construct(string $value)
38
    {
39 89
        $this->assertValid($value);
40
41 86
        $this->value = $value;
42 86
    }
43
44
    /**
45
     * @param HeaderValueParameter $parameter
46
     * @return HeaderValue
47
     */
48 24
    public function withParameter(HeaderValueParameter $parameter): HeaderValue
49
    {
50 24
        $clone = clone $this;
51 24
        $clone->parameters[$parameter->getName()] = $parameter;
52 24
        return $clone;
53
    }
54
55
    /**
56
     * @return string
57
     */
58 13
    public function getRaw(): string
59
    {
60 13
        return $this->value;
61
    }
62
63
    /**
64
     * @return string
65
     */
66 69
    public function __toString(): string
67
    {
68 69
        $value = implode('; ', array_merge([$this->value], $this->parameters));
69
70 69
        if ($this->needsEncoding) {
71 62
            return (string) (new OptimalEncodedHeaderValue($value));
72
        }
73
74 16
        return $value;
75
    }
76
77
    /**
78
     * @param string $value
79
     */
80 89
    private function assertValid(string $value): void
81
    {
82 89
        if ($this->validate($value) === false) {
83 3
            throw new \InvalidArgumentException('Invalid header value ' . $value);
84
        }
85 86
    }
86
87
    /**
88
     * @param $value
89
     * @return bool
90
     */
91 89
    private function validate(string $value): bool
92
    {
93 89
        $total = strlen($value);
94
95 89
        for ($i = 0; $i < $total; $i += 1) {
96 89
            $ord = ord($value[$i]);
97 89
            if ($ord === 10) {
98 1
                return false;
99
            }
100 89
            if ($ord === 13) {
101 5
                if ($i + 2 >= $total) {
102
                    return false;
103
                }
104 5
                $lf = ord($value[$i + 1]);
105 5
                $sp = ord($value[$i + 2]);
106 5
                if ($lf !== 10 || ! in_array($sp, [9, 32], true)) {
107 2
                    return false;
108
                }
109
                // skip over the LF following this
110 3
                $i += 2;
111
            }
112
        }
113
114 86
        return true;
115
    }
116
117
    /**
118
     * @param string $name
119
     * @return HeaderValueParameter
120
     */
121 7
    public function getParameter(string $name): HeaderValueParameter
122
    {
123 7
        if (isset($this->parameters[$name])) {
124 7
            return $this->parameters[$name];
125
        }
126
127 2
        throw new \UnexpectedValueException('No parameter with name ' . $name);
128
    }
129
130
    /**
131
     * @param string $value
132
     * @return HeaderValue
133
     */
134 16
    public static function fromEncodedString(string $value): HeaderValue
135
    {
136 16
        $headerValue = new self($value);
137 16
        $headerValue->needsEncoding = false;
138 16
        return $headerValue;
139
    }
140
141
    /**
142
     * @param string $headerValueAsString
143
     * @return HeaderValue
144
     */
145 3
    public static function fromString(string $headerValueAsString): HeaderValue
146
    {
147 3
        $values = [];
148
149 3
        $headerValueAsString = trim($headerValueAsString);
150
151 3
        $length = strlen($headerValueAsString) - 1;
152 3
        $n = -1;
153 3
        $state = self::PARSE_START;
154 3
        $escapeNext = false;
155 3
        $sequence = '';
156
157 3
        while ($n < $length) {
158 3
            $n++;
159
160 3
            $char = $headerValueAsString[$n];
161
162 3
            $sequence .= $char;
163
164 3
            if ($char === '\\') {
165
                $escapeNext = true;
166
                continue;
167
            }
168
169 3
            if ($escapeNext) {
170
                $escapeNext = false;
171
                continue;
172
            }
173
174
            switch ($state) {
175 3
                case self::PARSE_QUOTE:
176 2
                    if ($char === '"') {
177 2
                        $state = self::PARSE_START;
178
                    }
179
180 2
                    break;
181
                default:
182 3
                    if ($char === '"') {
183 2
                        $state = self::PARSE_QUOTE;
184
                    }
185
186 3
                    if ($char === ';') {
187 3
                        $values[] = trim(substr($sequence, 0, -1));
188 3
                        $sequence = '';
189 3
                        $state = self::PARSE_START;
190
                    }
191 3
                    break;
192
            }
193
        }
194
195 3
        $values[] = trim($sequence);
196
197 3
        $headerValue = new self($values[0]);
198
199 3
        $parameters = [];
200 3
        foreach (array_slice($values, 1) as $parameterString) {
201 3
            $parameter = HeaderValueParameter::fromString($parameterString);
202 3
            $parameters[$parameter->getName()] = $parameter;
203
        }
204
205 3
        $headerValue->parameters = $parameters;
206 3
        return $headerValue;
207
    }
208
}