Completed
Push — master ( 40e61a...4da634 )
by Frederik
02:29
created

Address::getAddress()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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