Completed
Pull Request — master (#42)
by Frederik
02:56
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 Item $item
52
     * @return ItemList
53
     */
54
    public function withItem(Item $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 Item
94
     */
95
    public function getName($name): Item
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 Item
108
     */
109
    public function last(): Item
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 (Item $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
                    $list = $list->withItem(new Item(substr($sequence, 0, -1)));
163
                    $sequence = '[';
164
                    $state = self::STATE_SECTION;
165
                    break;
166
                case ']':
167
                    if ($state !== self::STATE_SECTION) {
168
                        throw new \InvalidArgumentException('Invalid character ] found');
169
                    }
170
171
                    $list = $list->withItem(
172
                        $list
173
                            ->last()
174
                            ->withSections(SectionList::fromString($sequence))
175
                    );
176
177
                    $sequence = '';
178
                    $state = self::STATE_NAME;
179
                    break;
180
                case '<':
181
                    if ($state !== self::STATE_NAME) {
182
                        throw new \InvalidArgumentException('Invalid character < found');
183
                    }
184
185
                    $state = self::STATE_PARTIAL;
186
                    break;
187
                case '>':
188
                    if ($state !== self::STATE_PARTIAL) {
189
                        throw new \InvalidArgumentException('Invalid character > found');
190
                    }
191
192
                    $list = $list->withItem(
193
                        $list
194
                            ->last()
195
                            ->withPartial(Partial::fromString($sequence))
196
                    );
197
198
                    $sequence = '';
199
                    $state = self::STATE_NAME;
200
                    break;
201
                case '{':
202
                    if ($state !== self::STATE_NONE) {
203
                        throw new \InvalidArgumentException('Invalid character { found');
204
                    }
205
206
207
                    $state = self::STATE_OCTET;
208
                    break;
209
                case '}':
210
                    if ($state !== self::STATE_OCTET) {
211
                        throw new \InvalidArgumentException('Invalid characters } found');
212
                    }
213
214
                    $list = $list->withOctet((int)substr($sequence, 1, -1));
215
                    $sequence = '';
216
217
                    $state = self::STATE_NAME;
218
                    break;
219
                case ' ':
220
                    if ($sequence === ' ') {
221
                        $state = self::STATE_NONE;
222
                    }
223
224
                    if ($state === self::STATE_NONE) {
225
                        $sequence = '';
226
                    }
227
228
                    if ($state === self::STATE_NAME) {
229
                        $list = $list->withItem(new Item(substr($sequence, 0, -1)));
230
                        $sequence = '';
231
                        $state = self::STATE_NONE;
232
                    }
233
234
                    break;
235
                case "\n":
236
                    $list = $list->withBody(substr($serializedList, $index + 1));
237
                    $sequence = '';
238
                    break 2;
239
            }
240
241
            $index++;
242
        }
243
244
        if ($sequence) {
245
            $list = $list->withItem(new Item($sequence));
246
        }
247
248
        return $list;
249
    }
250
}