Passed
Push — master ( 523cb4...302393 )
by Sam
02:03
created

Normaliser::process()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 5

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 21
ccs 15
cts 15
cp 1
rs 9.4888
c 0
b 0
f 0
cc 5
nc 5
nop 0
crap 5
1
<?php
2
3
namespace Badcow\DNS\Parser;
4
5
use LTDBeget\ascii\AsciiChar;
6
7
class Normaliser
8
{
9
    /**
10
     * @var StringIterator
11
     */
12
    private $string;
13
14
    /**
15
     * @var string
16
     */
17
    private $normalisedString = '';
18
19
    /**
20
     * Normaliser constructor.
21
     *
22
     * @param string $zone
23
     */
24 8
    public function __construct(string $zone)
25
    {
26
        //Remove Windows line feeds and tabs
27 8
        $zone = str_replace(["\r\n", "\t"], ["\n", ' '], $zone);
28
29 8
        $this->string = new StringIterator($zone);
30 8
    }
31
32
    /**
33
     * @param string $zone
34
     *
35
     * @return string
36
     *
37
     * @throws ParseException
38
     */
39 8
    public static function normalise(string $zone): string
40
    {
41 8
        $normaliser = new self($zone);
42
43 8
        return $normaliser->process();
44
    }
45
46
    /**
47
     * @return string
48
     *
49
     * @throws ParseException
50
     */
51 8
    public function process(): string
52
    {
53 8
        while ($this->string->valid()) {
54 8
            switch ($this->string->ord()) {
55 8
                case AsciiChar::DOUBLE_QUOTES:
56 5
                    $this->handleTxt();
57 3
                    break;
58 8
                case AsciiChar::SEMICOLON:
59 5
                    $this->handleComment();
60 5
                    break;
61 8
                case AsciiChar::OPEN_BRACKET:
62 4
                    $this->handleMultiline();
63 3
                    break;
64
            }
65
66 8
            $this->append();
67
        }
68
69 5
        $this->removeWhitespace();
70
71 5
        return $this->normalisedString;
72
    }
73
74
    /**
75
     * Ignores the comment section.
76
     *
77
     * @throws ParseException
78
     */
79 6
    private function handleComment(): void
80
    {
81 6
        if (AsciiChar::SEMICOLON !== $this->string->ord()) {
82
            throw new ParseException(sprintf('Semicolon (;) expected as current entry, character "%s" instead.',
83
                $this->string->current()),
84
                $this->string
85
            );
86
        }
87
88 6
        while (AsciiChar::LINE_FEED !== $this->string->ord() && $this->string->valid()) {
89 6
            $this->string->next();
90
        }
91 6
    }
92
93
    /**
94
     * Handle text inside of double quotations. When this function is called, the String pointer MUST be at the
95
     * double quotation mark.
96
     *
97
     * @throws ParseException
98
     */
99 5
    private function handleTxt(): void
100
    {
101 5
        if (AsciiChar::DOUBLE_QUOTES !== $this->string->ord()) {
102
            throw new ParseException(sprintf('Double Quotes (") expected as current entry, character "%s" instead.',
103
                $this->string->current()),
104
                $this->string
105
            );
106
        }
107
108 5
        $this->append();
109
110 5
        while (AsciiChar::DOUBLE_QUOTES !== $this->string->ord()) {
111 5
            if (!$this->string->valid()) {
112 1
                throw new ParseException('Unbalanced double quotation marks. End of file reached.');
113
            }
114
115
            //If escape character
116 5
            if (AsciiChar::BACKSLASH === $this->string->ord()) {
117 3
                $this->append();
118
            }
119
120 5
            if (AsciiChar::LINE_FEED === $this->string->ord()) {
121 1
                throw new ParseException('Line Feed found within double quotation marks context.', $this->string);
122
            }
123
124 5
            $this->append();
125
        }
126 3
    }
127
128
    /**
129
     * Move multi-line records onto single line.
130
     *
131
     * @throws ParseException
132
     */
133 4
    private function handleMultiline(): void
134
    {
135 4
        if (AsciiChar::OPEN_BRACKET !== $this->string->ord()) {
136
            throw new ParseException(sprintf('Open bracket "(" expected as current entry, character "%s" instead.',
137
                $this->string->current()),
138
                $this->string
139
            );
140
        }
141
142 4
        $openBracket = true;
143 4
        $this->string->next();
144 4
        while ($openBracket) {
145 4
            switch ($this->string->ord()) {
146 4
                case AsciiChar::DOUBLE_QUOTES:
147 1
                    $this->handleTxt();
148 1
                    $this->append();
149 1
                    break;
150 4
                case AsciiChar::SEMICOLON:
151 4
                    $this->handleComment();
152 4
                    break;
153 4
                case AsciiChar::LINE_FEED:
154 4
                    $this->string->next();
155 4
                    break;
156 4
                case AsciiChar::CLOSE_BRACKET:
157 3
                    $openBracket = false;
158 3
                    $this->string->next();
159 3
                    break;
160 4
                case AsciiChar::NULL:
161 1
                    throw new ParseException('End of file reached. Unclosed bracket.');
162
                default:
163 4
                    $this->append();
164
            }
165
        }
166 3
    }
167
168
    /**
169
     * Remove superfluous whitespace characters from string.
170
     */
171 5
    private function removeWhitespace(): void
172
    {
173 5
        $this->normalisedString = preg_replace('/ {2,}/', ' ', $this->normalisedString);
174 5
        $lines = explode("\n", $this->normalisedString);
175 5
        $this->normalisedString = '';
176 5
        foreach ($lines as $line) {
177 5
            $line = trim($line);
178 5
            if ('' !== $line) {
179 5
                $this->normalisedString .= $line."\n";
180
            }
181
        }
182 5
        $this->normalisedString = rtrim($this->normalisedString);
183 5
    }
184
185
    /**
186
     * Add current entry to nomalisedString and moves to next entry.
187
     */
188 8
    private function append()
189
    {
190 8
        $this->normalisedString .= $this->string->current();
191 8
        $this->string->next();
192 8
    }
193
}
194