Address::fromString()   F
last analyzed

Complexity

Conditions 24
Paths 223

Size

Total Lines 102

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 57
CRAP Score 24.0029

Importance

Changes 0
Metric Value
dl 0
loc 102
ccs 57
cts 58
cp 0.9828
rs 2.4366
c 0
b 0
f 0
cc 24
nc 223
nop 1
crap 24.0029

How to fix   Long Method    Complexity   

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;
5
6
use Genkgo\Mail\Header\OptimalEncodedHeaderValue;
7
8
final class Address
9
{
10
    private const PARSE_POSITION_START = 1;
11
    
12
    private const PARSE_POSITION_QUOTE = 2;
13
    
14
    private const PARSE_STATE_EMAIL = 1;
15
    
16
    private const PARSE_STATE_TAGGED_EMAIL = 2;
17
18
    /**
19
     * @var EmailAddress
20
     */
21
    private $address;
22
23
    /**
24
     * @var string
25
     */
26
    private $name;
27
28
    /**
29
     * @param EmailAddress $address
30
     * @param string $name
31
     */
32 94
    public function __construct(EmailAddress $address, string $name = '')
33
    {
34 94
        if (\preg_match('/\v/u', $name) !== 0) {
35 1
            throw new \InvalidArgumentException('Cannot use vertical white space within name of email address');
36
        }
37
38 93
        $this->address = $address;
39 93
        $this->name = $name;
40 93
    }
41
42
    /**
43
     * @return EmailAddress
44
     */
45 28
    public function getAddress(): EmailAddress
46
    {
47 28
        return $this->address;
48
    }
49
50
    /**
51
     * @return string
52
     */
53 36
    public function getName(): string
54
    {
55 36
        return $this->name;
56
    }
57
58
    /**
59
     * @param Address $address
60
     * @return bool
61
     */
62 3
    public function equals(Address $address): bool
63
    {
64 3
        return $this->address->equals($address->address) && $this->name === $address->name;
65
    }
66
67
    /**
68
     * @return string
69
     */
70 54
    public function __toString(): string
71
    {
72 54
        if ($this->name === '') {
73 15
            return (string)$this->address;
74
        }
75
76 41
        $encodedPhrase = OptimalEncodedHeaderValue::forPhrase($this->name);
77 41
        if ($encodedPhrase->getEncoding() === '7bit' || $encodedPhrase->getEncoding() === '8bit') {
78 38
            $encodedName = \addslashes((string)$encodedPhrase);
79
80 38
            if (\preg_match('/[^A-Za-z0-9\s!#$%&\'*+\/=?^_`{|}~\-]/', $this->name) === 1) {
81 38
                $encodedName = \sprintf('"%s"', $encodedName);
82
            }
83
        } else {
84 4
            $encodedName = (string)$encodedPhrase;
85
        }
86
87 41
        return \sprintf('%s <%s>', $encodedName, $this->address->getPunyCode());
88
    }
89
90
    /**
91
     * @return string
92
     */
93 1
    public function toReadableString(): string
94
    {
95 1
        if ($this->name === '') {
96 1
            return (string)$this->address;
97
        }
98
99 1
        $encodedName = $this->name;
100 1
        if ($encodedName !== $this->name || \preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $this->name) === 1) {
101 1
            $encodedName = \sprintf('"%s"', $encodedName);
102
        }
103
104 1
        return \sprintf('%s <%s>', $encodedName, $this->address->getAddress());
105
    }
106
107
    /**
108
     * @param string $addressAsString
109
     * @return Address
110
     */
111 52
    public static function fromString(string $addressAsString)
112
    {
113 52
        $addressAsString = \trim($addressAsString);
114
115 52
        if ($addressAsString === '') {
116 1
            throw new \InvalidArgumentException('Address cannot be empty');
117
        }
118
119 51
        $sequence = '';
120 51
        $length = \strlen($addressAsString) - 1;
121 51
        $n = -1;
122 51
        $state = self::PARSE_STATE_EMAIL;
123 51
        $position = self::PARSE_POSITION_START;
124 51
        $escapeNext = false;
125 51
        $name = '';
126 51
        $email = '';
127 51
        $nameQuoted = false;
128
129 51
        while ($n < $length) {
130 51
            $n++;
131
132 51
            $char = $addressAsString[$n];
133
134 51
            if ($char === '\\') {
135 5
                $escapeNext = true;
136 5
                continue;
137
            }
138
139 51
            $sequence .= $char;
140
141 51
            if ($escapeNext) {
142 5
                $escapeNext = false;
143 5
                continue;
144
            }
145
146
            switch ($position) {
147 51
                case self::PARSE_POSITION_QUOTE:
148 19
                    if ($char === '"') {
149 17
                        $position = self::PARSE_POSITION_START;
150
                    }
151
152 19
                    break;
153
                default:
154 51
                    if ($char === '"') {
155 19
                        $position = self::PARSE_POSITION_QUOTE;
156
                    }
157
158 51
                    if ($char === '"' && $state === self::PARSE_STATE_EMAIL) {
159 18
                        $nameQuoted = true;
160
                    }
161 51
                    break;
162
            }
163
164
            switch ($state) {
165 51
                case self::PARSE_STATE_TAGGED_EMAIL:
166 38
                    if ($position !== self::PARSE_POSITION_QUOTE && $char === '>') {
167 37
                        $state = self::PARSE_STATE_EMAIL;
168 37
                        $email = \substr($sequence, 0, -1);
169
                    }
170
171 38
                    break;
172
                default:
173 51
                    if ($email !== '') {
174 1
                        throw new \InvalidArgumentException('Invalid characters used after <>');
175
                    }
176
177 51
                    if ($position !== self::PARSE_POSITION_QUOTE && $char === '<') {
178 38
                        $state = self::PARSE_STATE_TAGGED_EMAIL;
179 38
                        $name = \trim(\substr($sequence, 0, -1));
180 38
                        $sequence = '';
181
                    }
182 51
                    break;
183
            }
184
        }
185
186 50
        if ($position === self::PARSE_POSITION_QUOTE) {
187 2
            throw new \InvalidArgumentException('Address uses starting quotes but no ending quotes');
188
        }
189
190 48
        if ($state === self::PARSE_STATE_TAGGED_EMAIL) {
191 1
            throw new \InvalidArgumentException('Address uses starting tag (<) but no ending tag (>)');
192
        }
193
194 47
        if ($name === '' && $email === '') {
195 15
            return new self(new EmailAddress($sequence));
196
        }
197
198 36
        if ($nameQuoted && $name[0] !== '"') {
199 1
            throw new \InvalidArgumentException('Invalid characters before "');
200
        }
201
202 35
        if ($nameQuoted) {
203 10
            $name = \substr($name, 1, -1);
204
        }
205
206 35
        $name = @\iconv_mime_decode($name);
207 35
        if ($name === false) {
208
            throw new \InvalidArgumentException('Failed to mime decode the name');
209
        }
210
211 35
        return new self(new EmailAddress($email), $name);
212
    }
213
}
214