Completed
Pull Request — master (#42)
by Frederik
03:39
created

ItemList::withItem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
ccs 0
cts 6
cp 0
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
crap 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Protocol\Imap\MessageData;
5
6
final class ItemList
7
{
8
    /**
9
     *
10
     */
11
    private CONST STATE_NONE = 0;
12
    /**
13
     *
14
     */
15
    private CONST STATE_NAME = 1;
16
    /**
17
     *
18
     */
19
    private CONST STATE_SECTION = 2;
20
    /**
21
     *
22
     */
23
    private CONST STATE_PARTIAL = 3;
24
    /**
25
     *
26
     */
27
    private CONST STATE_OCTET = 4;
28
    /**
29
     * @var array
30
     */
31
    private $list = [];
32
    /**
33
     * @var int
34
     */
35
    private $size;
36
    /**
37
     * @var string
38
     */
39
    private $body;
40
41
    /**
42
     * ItemList constructor.
43
     * @param array $list
44
     */
45
    public function __construct(array $list = [])
46
    {
47
        $this->list = $list;
48
    }
49
50
    /**
51
     * @param ItemInterface $item
52
     * @return ItemList
53
     */
54
    public function withItem(ItemInterface $item): self
55
    {
56
        $clone = clone $this;
57
        $clone->list[$item->getName()] = $item;
58
        return $clone;
59
    }
60
61
    /**
62
     * @param int $size
63
     * @return ItemList
64
     */
65
    public function withOctet(int $size): self
66
    {
67
        $clone = clone $this;
68
        $clone->size = $size;
69
        return $clone;
70
    }
71
72
    /**
73
     * @param string $body
74
     * @return ItemList
75
     */
76
    public function withBody(string $body): self
77
    {
78
        $clone = clone $this;
79
        $clone->body = $body;
80
        return $clone;
81
    }
82
83
    /**
84
     * @return string
85
     */
86
    public function getBody(): string
87
    {
88
        return $this->body;
89
    }
90
91
    /**
92
     * @param $name
93
     * @return ItemInterface
94
     */
95
    public function getName($name): ItemInterface
96
    {
97
        if (!isset($this->list[$name])) {
98
            throw new \UnexpectedValueException(
99
                sprintf('Unknown name %s', $name)
100
            );
101
        }
102
103
        return $this->list[$name];
104
    }
105
106
    /**
107
     * @return ItemInterface
108
     */
109
    public function last(): ItemInterface
110
    {
111
        if (empty($this->list)) {
112
            throw new \OutOfBoundsException('Cannot return last item from empty list');
113
        }
114
115
        return end($this->list);
116
    }
117
118
    /**
119
     * @return string
120
     */
121
    public function __toString(): string
122
    {
123
        $string = implode(
124
            ' ',
125
            array_map(
126
                function (ItemInterface $item) {
127
                    return (string)$item;
128
                },
129
                $this->list
130
            )
131
        );
132
133
        if (count($this->list) > 1) {
134
            return '(' . $string . ')';
135
        }
136
137
        return $string;
138
    }
139
140
    /**
141
     * @param string $serializedList
142
     * @return ItemList
143
     */
144
    public static function fromString(string $serializedList): self
145
    {
146
        $list = new self();
147
148
        $index = 0;
149
        $state = self::STATE_NAME;
150
        $sequence = '';
151
152
        while (isset($serializedList[$index])) {
153
            $char = $serializedList[$index];
154
            $sequence .= $char;
155
156
            switch ($char) {
157
                case '[':
158
                    if ($state !== self::STATE_NAME) {
159
                        throw new \InvalidArgumentException('Invalid character [ found');
160
                    }
161
162
                    $state = self::STATE_SECTION;
163
                    break;
164
                case ']':
165
                    if ($state !== self::STATE_SECTION) {
166
                        throw new \InvalidArgumentException('Invalid character ] found');
167
                    }
168
169
                    $list = $list->withItem(NameSectionItem::fromString($sequence));
170
171
                    $sequence = '';
172
                    $state = self::STATE_NAME;
173
                    break;
174
                case '<':
175
                    if ($state !== self::STATE_NAME) {
176
                        throw new \InvalidArgumentException('Invalid character < found');
177
                    }
178
179
                    $state = self::STATE_PARTIAL;
180
                    break;
181
                case '>':
182
                    if ($state !== self::STATE_PARTIAL) {
183
                        throw new \InvalidArgumentException('Invalid character > found');
184
                    }
185
186
                    $list = $list->withItem(
187
                        PartialItem::fromString(
188
                            $list->last(),
189
                            $sequence
190
                        )
191
                    );
192
193
                    $sequence = '';
194
                    $state = self::STATE_NAME;
195
                    break;
196
                case '{':
197
                    if ($state !== self::STATE_NONE) {
198
                        throw new \InvalidArgumentException('Invalid character { found');
199
                    }
200
201
202
                    $state = self::STATE_OCTET;
203
                    break;
204
                case '}':
205
                    if ($state !== self::STATE_OCTET) {
206
                        throw new \InvalidArgumentException('Invalid characters } found');
207
                    }
208
209
                    $list = $list->withOctet((int)substr($sequence, 1, -1));
210
                    $sequence = '';
211
212
                    $state = self::STATE_NAME;
213
                    break;
214
                case ' ':
215
                    if ($sequence === ' ') {
216
                        $state = self::STATE_NONE;
217
                    }
218
219
                    if ($state === self::STATE_NONE) {
220
                        $sequence = '';
221
                    }
222
223
                    if ($state === self::STATE_NAME) {
224
                        $list = $list->withItem(new NameItem(substr($sequence, 0, -1)));
225
                        $sequence = '';
226
                        $state = self::STATE_NONE;
227
                    }
228
229
                    break;
230
                case "\n":
231
                    $list = $list->withBody(substr($serializedList, $index + 1));
232
                    $sequence = '';
233
                    break 2;
234
            }
235
236
            $index++;
237
        }
238
239
        if ($sequence) {
240
            $list = $list->withItem(new NameItem($sequence));
241
        }
242
243
        return $list;
244
    }
245
}