Completed
Pull Request — master (#42)
by Frederik
01:59
created

MailboxName   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 103
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 13
lcom 0
cbo 0
dl 0
loc 103
ccs 35
cts 35
cp 1
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 3
A __toString() 0 4 1
D validateNonRfcName() 0 42 9
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Protocol\Imap;
5
6
/**
7
 *
8
 */
9
final class MailboxName
10
{
11
    /**
12
     *
13
     */
14
    private CONST RFC_3501_ASTRING = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x20\x1A\x1B\x1C\x1D\x1E\x1F\x22\x25\x28\x29\x2A\x5C\x7B\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF";
15
    /**
16
     *
17
     */
18
    private CONST RFC_3501_TEXT_CHAR = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF";
19
20
    /**
21
     *
22
     */
23
    private CONST RFC_3501_FIXED = [
24
        "INBOX" => true,
25
    ];
26
27
    /**
28
     *
29
     */
30
    private CONST PARSE_STATE_UNQUOTED = 1;
31
    /**
32
     *
33
     */
34
    private CONST PARSE_STATE_QUOTED = 2;
35
36
    /**
37
     * @var string
38
     */
39
    private $name;
40
41
    /**
42
     * Mailbox constructor.
43
     * @param string $name
44
     */
45 24
    public function __construct(string $name)
46
    {
47 24
        if ($name === '') {
48 1
            throw new \InvalidArgumentException('Mailbox name cannot be empty');
49
        }
50
51 23
        if (!isset(self::RFC_3501_FIXED[$name])) {
52 22
            $this->validateNonRfcName($name);
53
        }
54
55 18
        $this->name = $name;
56 18
    }
57
58
    /**
59
     * @return string
60
     */
61 15
    public function __toString(): string
62
    {
63 15
        return $this->name;
64
    }
65
66
    /**
67
     * @param string $name
68
     */
69 22
    private function validateNonRfcName(string $name): void
70
    {
71 22
        $index = 0;
72 22
        $state = self::PARSE_STATE_UNQUOTED;
73 22
        while (isset($name[$index])) {
74 22
            $char = $name[$index];
75
76
            switch ($state) {
77 22
                case self::PARSE_STATE_UNQUOTED:
78 22
                    if ($char === '"') {
79 5
                        $state = self::PARSE_STATE_QUOTED;
80 5
                        break;
81
                    }
82
83 20
                    if (strlen($char) !== strcspn($char, self::RFC_3501_ASTRING)) {
84 3
                        throw new \InvalidArgumentException(
85 3
                            \sprintf(
86 3
                                'Invalid mailbox character %s, use a quoted string for a broader range of allowed character',
87 3
                                $char
88
                            )
89
                        );
90
                    }
91 20
                    break;
92 5
                case self::PARSE_STATE_QUOTED:
93 5
                    if ($char === '"') {
94 3
                        $state = self::PARSE_STATE_UNQUOTED;
95 3
                        break;
96
                    }
97
98 5
                    if (strlen($char) !== strcspn($char, self::RFC_3501_TEXT_CHAR)) {
99 1
                        throw new \InvalidArgumentException('Invalid mailbox name');
100
                    }
101 4
                    break;
102
            }
103
104 22
            $index++;
105
        }
106
107 18
        if ($state === self::PARSE_STATE_QUOTED) {
108 1
            throw new \InvalidArgumentException('Unfinished quoted literal');
109
        }
110
    }
111
}