AddressList   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 169
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 1
dl 0
loc 169
ccs 68
cts 68
cp 1
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 3
A withAddress() 0 6 1
A withoutAddress() 0 12 3
A withList() 0 6 1
A first() 0 8 2
A count() 0 4 1
A getIterator() 0 4 1
A __toString() 0 12 1
B fromString() 0 56 9
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail;
5
6
/**
7
 * @implements \IteratorAggregate<int, Address>
8
 */
9
final class AddressList implements \Countable, \IteratorAggregate
10
{
11
    private const PARSE_START = 1;
12
    
13
    private const PARSE_QUOTE = 2;
14
15
    /**
16
     * @var array|Address[]
17
     */
18
    private $addresses = [];
19
20
    /**
21
     * @param array<int, mixed> $recipients
22
     */
23 56
    public function __construct(array $recipients = [])
24
    {
25 56
        foreach ($recipients as $recipient) {
26 54
            if ($recipient instanceof Address === false) {
27 1
                throw new \InvalidArgumentException('Recipient must be Address object');
28
            }
29
30 53
            $this->addresses[] = $recipient;
31
        }
32 55
    }
33
34
    /**
35
     * @param Address $address
36
     * @return AddressList
37
     */
38 2
    public function withAddress(Address $address): AddressList
39
    {
40 2
        $clone = clone $this;
41 2
        $clone->addresses[] = $address;
42 2
        return $clone;
43
    }
44
45
    /**
46
     * @param Address $address
47
     * @return AddressList
48
     */
49 2
    public function withoutAddress(Address $address): AddressList
50
    {
51 2
        $clone = clone $this;
52
53 2
        foreach ($this->addresses as $key => $mayRemoveAddress) {
54 2
            if ($mayRemoveAddress->equals($address)) {
55 2
                unset($clone->addresses[$key]);
56
            }
57
        }
58
59 2
        return $clone;
60
    }
61
62
    /**
63
     * @param AddressList $addressList
64
     * @return AddressList
65
     */
66 1
    public function withList(AddressList $addressList): AddressList
67
    {
68 1
        $clone = clone $this;
69 1
        $clone->addresses = \array_merge($this->addresses, $addressList->addresses);
70 1
        return $clone;
71
    }
72
73
    /**
74
     * @return Address
75
     */
76 11
    public function first(): Address
77
    {
78 11
        if (empty($this->addresses)) {
79 2
            throw new \OutOfRangeException();
80
        }
81
82 10
        return \reset($this->addresses);
83
    }
84
85
    /**
86
     * @return int
87
     */
88 13
    public function count(): int
89
    {
90 13
        return \count($this->addresses);
91
    }
92
93
    /**
94
     * @return \ArrayIterator<int, Address>
0 ignored issues
show
Documentation introduced by
The doc-type \ArrayIterator<int, could not be parsed: Expected "|" or "end of type", but got "<" at position 14. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
95
     */
96 1
    public function getIterator()
97
    {
98 1
        return new \ArrayIterator($this->addresses);
99
    }
100
101
    /**
102
     * @return string
103
     */
104 34
    public function __toString(): string
105
    {
106 34
        return \implode(
107 34
            ",\r\n ",
108 34
            \array_map(
109
                function (Address $addressAndName) {
110 33
                    return (string) $addressAndName;
111 34
                },
112 34
                $this->addresses
113
            )
114
        );
115
    }
116
117
    /**
118
     * @param string $addressListAsString
119
     * @return AddressList
120
     */
121 26
    public static function fromString(string $addressListAsString)
122
    {
123 26
        $addressListAsString = \trim($addressListAsString);
124 26
        if ($addressListAsString === '') {
125 2
            return new self([]);
126
        }
127
128 25
        $addresses = [];
129 25
        $length = \strlen($addressListAsString) - 1;
130 25
        $n = -1;
131 25
        $state = self::PARSE_START;
132 25
        $escapeNext = false;
133 25
        $sequence = '';
134
135 25
        while ($n < $length) {
136 25
            $n++;
137
138 25
            $char = $addressListAsString[$n];
139
140 25
            $sequence .= $char;
141
142 25
            if ($char === '\\') {
143 3
                $escapeNext = true;
144 3
                continue;
145
            }
146
147 25
            if ($escapeNext) {
148 3
                $escapeNext = false;
149 3
                continue;
150
            }
151
152
            switch ($state) {
153 25
                case self::PARSE_QUOTE:
154 9
                    if ($char === '"') {
155 8
                        $state = self::PARSE_START;
156
                    }
157
158 9
                    break;
159
                default:
160 25
                    if ($char === '"') {
161 9
                        $state = self::PARSE_QUOTE;
162
                    }
163
164 25
                    if ($char === ',') {
165 6
                        $addresses[] = Address::fromString(\substr($sequence, 0, -1));
166 6
                        $sequence = '';
167 6
                        $state = self::PARSE_START;
168
                    }
169 25
                    break;
170
            }
171
        }
172
173 25
        $addresses[] = Address::fromString($sequence);
174
175 24
        return new self($addresses);
176
    }
177
}
178