Test Setup Failed
Pull Request — master (#23)
by
unknown
01:48
created

RData::extractCharSet()   C

Complexity

Conditions 12
Paths 0

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 0
Metric Value
dl 0
loc 38
ccs 0
cts 0
cp 0
rs 6.9666
c 0
b 0
f 0
cc 12
nc 0
nop 1
crap 156

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author: Viskov Sergey
4
 * @date  : 14.04.16
5
 * @time  : 4:50
6
 */
7
8
namespace LTDBeget\dns\record;
9
10
use LTDBeget\ascii\AsciiChar;
11
use LTDBeget\dns\SyntaxErrorException;
12
use LTDBeget\stringstream\StringStream;
13
14
/**
15
 * Class RData
16
 *
17
 * @package LTDBeget\dns\record
18
 */
19
class RData
20
{
21
    /**
22
     * @var array
23
     */
24
    private static $rdataFormats = [
25
        'SOA'   => [
26
            'MNAME'   => 'defaultExtractor',
27
            'RNAME'   => 'defaultExtractor',
28
            'SERIAL'  => 'defaultExtractor',
29
            'REFRESH' => 'defaultExtractor',
30
            'RETRY'   => 'defaultExtractor',
31
            'EXPIRE'  => 'defaultExtractor',
32
            'MINIMUM' => 'defaultExtractor',
33
        ],
34
        'A'     => [
35
            'ADDRESS' => 'defaultExtractor'
36
        ],
37
        'AAAA'  => [
38
            'ADDRESS' => 'defaultExtractor'
39
        ],
40
        'CNAME' => [
41
            'CNAME' => 'defaultExtractor'
42
        ],
43
        'MX'    => [
44
            'PREFERENCE' => 'defaultExtractor',
45
            'EXCHANGE'   => 'defaultExtractor'
46
        ],
47
        'NS'    => [
48
            'NSDNAME' => 'defaultExtractor'
49
        ],
50
        'PTR'   => [
51
            'PTRDNAME' => 'defaultExtractor'
52
        ],
53
        'SRV'   => [
54
            'PRIORITY' => 'defaultExtractor',
55
            'WEIGHT'   => 'defaultExtractor',
56
            'PORT'     => 'defaultExtractor',
57
            'TARGET'   => 'defaultExtractor'
58
        ],
59
        'TXT'   => [
60
            'TXTDATA' => 'txtExtractor'
61
        ],
62
        'CAA' => [
63
            'FLAGS' => 'defaultExtractor',
64
            'TAG'   => 'defaultExtractor',
65
            'VALUE' => 'defaultExtractor'
66
        ],
67
        'NAPTR' => [
68
            'ORDER'       => 'defaultExtractor',
69
            'PREFERENCE'  => 'defaultExtractor',
70
            'FLAGS'       => 'defaultExtractor',
71
            'SERVICES'    => 'defaultExtractor',
72
            'REGEXP'      => 'defaultExtractor',
73
            'REPLACEMENT' => 'defaultExtractor'
74
        ]
75
    ];
76
    /**
77
     * @var string
78
     */
79
    private $type;
80
    /**
81
     * @var array
82
     */
83
    private $tokens = [];
84
85
    /**
86
     * @var bool
87
     */
88 6
    private $commentOpen = false;
89
90 6
    /**
91
     * @var bool
92
     */
93
    private $multiLineOpened = false;
94 6
95 6
    /**
96 6
     * Is the txt record surrounded by quotes
97
     * @var bool
98
     */
99
    private $txtRecordHasQuotes = false;
100
101
    /**
102 6
     * RData constructor.
103
     *
104 6
     * @param StringStream $stream
105
     * @param string       $type
106
     */
107
    public function __construct(StringStream $stream, string $type)
108
    {
109
        if (! self::isKnownType($type)) {
110 6
            throw new SyntaxErrorException($stream);
111
        }
112 6
113 6
        $this->stream = $stream;
0 ignored issues
show
Bug introduced by
The property stream does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
114
        $this->type   = $type;
115
    }
116 6
117
    /**
118 6
     * @param string $type
119
     * @return bool
120
     */
121
    public static function isKnownType(string $type) : bool
122
    {
123
        return array_key_exists($type, self::$rdataFormats);
124 6
    }
125
126 6
    /**
127 5
     * @return array
128
     */
129 6
    public function tokenize() : array
130
    {
131
        foreach (self::$rdataFormats[$this->type] as $tokenName => $extractor) {
132 6
            $this->$extractor($tokenName);
133
        }
134 6
135 6
        $this->endRecord();
136
137
        return $this->tokens;
138
    }
139
140 6
    /**
141
     * @param string $tokenName
142 6
     */
143 4
    protected function defaultExtractor(string $tokenName)
144
    {
145
        if($this->multiLineOpened) {
146 6
            $this->stream->ignoreWhitespace();
147 5
        } else {
148 5
            $this->stream->ignoreHorizontalSpace();
149 5
        }
150 6
151 1
        $this->commentOpen = false;
152 1
153 1
        if (!array_key_exists($tokenName, $this->tokens)) {
154 6
            $this->tokens[$tokenName] = '';
155 4
        }
156 4
157 6
        start:
158 4
159
        if ($this->stream->isEnd()){
160 6
            return;
161 4
        }
162 4
        $ord = $this->stream->ord();
163
164 3
        if($ord === AsciiChar::OPEN_BRACKET && !$this->commentOpen) {
165 3
            $this->multiLineOpened = true;
166 3
            $this->stream->next();
167
            goto start;
168 1
        } elseif($this->multiLineOpened && !$this->commentOpen && $ord === AsciiChar::CLOSE_BRACKET) {
169 1
            $this->multiLineOpened = false;
170 1
            $this->stream->next();
171
            goto start;
172 4
        } elseif($this->multiLineOpened && !$this->commentOpen && $ord === AsciiChar::LINE_FEED) {
173 6
            $this->stream->next();
174 3
            goto start;
175 3
        } elseif($ord === AsciiChar::LINE_FEED && !$this->commentOpen) {
176 3
            return;
177 3
        } else {
178 6
            if($ord === AsciiChar::SEMICOLON) {
179 3
                $this->stream->previous();
180 3
                if($this->stream->currentAscii()->isHorizontalSpace()) {
181 3
182 6
                    $this->commentOpen = true;
183 6
                    $this->stream->next();
184 4
                    $this->stream->next();
185 4
                } else {
186 6
                    $this->stream->next();
187 5
                    $this->tokens[$tokenName] .= $this->stream->current();
188
                    $this->stream->next();
189 6
                }
190 6
                goto start;
191 6
            } elseif(($this->stream->currentAscii()->isVerticalSpace() || $ord === AsciiChar::NULL) && $this->commentOpen) {
192 4
                $this->stream->next();
193
                $this->stream->ignoreHorizontalSpace();
194 6
                $this->commentOpen = false;
195
                goto start;
196
            } elseif($this->commentOpen) {
197
                $this->commentOpen = true;
198
                $this->ignoreComment();
199
                goto start;
200 3
            } elseif(!$this->commentOpen) {
201
                if($ord === AsciiChar::SPACE && $this->tokens[$tokenName] === '') {
202
                    $this->stream->next();
203 3
                    goto start;
204 3
                } elseif($this->stream->currentAscii()->isWhiteSpace()) {
205 3
                    return;
206
                } else {
207 3
                    $this->tokens[$tokenName] .= $this->stream->current();
208
                    $this->stream->next();
209 6
                    if($this->stream->isEnd()) {
210
                        $this->tokens[$tokenName] = trim($this->tokens[$tokenName]);
211
                    }
212 6
                    goto start;
213 6
                }
214 4
            }
215
        }
216 5
    }
217 2
218 2 View Code Duplication
    private function ignoreComment()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
219 2
    {
220 5
        start:
221 2
        if (!$this->stream->currentAscii()->isVerticalSpace() && !$this->stream->isEnd()) {
222 2
            $this->stream->next();
223 2
            goto start;
224
        }
225 2
    }
226 2
227 2
    protected function endRecord()
228
    {
229 5
        start:
230 5
        if ($this->stream->isEnd()) {
231 4
            return;
232 4
        }
233
        $ord = $this->stream->ord();
234 4
        if($ord === AsciiChar::SEMICOLON) {
235 4
            $this->stream->next();
236 5
            $this->commentOpen = true;
237 5
            goto start;
238 View Code Duplication
        } elseif($this->commentOpen) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239
            if($ord === AsciiChar::NULL() || $ord === AsciiChar::LINE_FEED) {
240 3
                $this->commentOpen = false;
241
                goto start;
242
            } else {
243
                $this->stream->next();
244
                $this->commentOpen = true;
245 1
                goto start;
246
            }
247 1
        } elseif(!$this->commentOpen)  {
248 1
            if($this->multiLineOpened) {
249
                if($ord === AsciiChar::CLOSE_BRACKET) {
250
                    $this->multiLineOpened = false;
251
                }
252 1
                $this->stream->next();
253 1
                goto start;
254
            } elseif($ord === AsciiChar::NULL() || $ord === AsciiChar::LINE_FEED) {
255 1
                return;
256
            }
257
        }
258
    }
259
260 1
    /**
261 1
     * @param string $tokenName
262 1
     */
263 1
    private function txtExtractor(string $tokenName)
264 1
    {
265 1
        if (!array_key_exists($tokenName, $this->tokens)) {
266 1
            $this->tokens[$tokenName] = '';
267 1
        }
268 1
269 1
        start:
270
        if ($this->stream->isEnd()) {
271
            return;
272 1
        }
273 1
        $ord = $this->stream->ord();
274
        $this->stream->next();
275
276
        // comment starts
277 1
        if($ord === AsciiChar::SEMICOLON) {
278 1
            $this->commentOpen = true;
279
            goto start;
280
        } elseif($this->commentOpen === true && $ord !== AsciiChar::LINE_FEED) {
281
            $this->commentOpen = true;
282 1
            goto start;
283 1 View Code Duplication
        } elseif($this->commentOpen === true && ($ord === AsciiChar::LINE_FEED || $ord === AsciiChar::NULL)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
284 1
            $this->stream->previous();
285
            $this->commentOpen = false;
286
            goto start;
287
        } else {
288 1
            // ignore whitespace
289 1
            if($ord === AsciiChar::SPACE || $ord === AsciiChar::HORIZONTAL_TAB) {
290 1
                goto start;
291
            }
292
293
            // multi line opened
294 1
            if($ord === AsciiChar::OPEN_BRACKET && !$this->commentOpen) {
295
                $this->multiLineOpened = true;
296
                goto start;
297
            }
298
            // multi line closed
299 1
            elseif($this->multiLineOpened && !$this->commentOpen && $ord === AsciiChar::CLOSE_BRACKET) {
300 1
                $this->multiLineOpened = false;
301 1
                goto start;
302 1
            }
303
            // comment end in multi line TXT record
304
            elseif($ord === AsciiChar::LINE_FEED && $this->commentOpen && $this->multiLineOpened) {
305 1
                goto start;
306
            }
307
            // is record ends?
308
            elseif(!$this->multiLineOpened && ($ord === AsciiChar::LINE_FEED || $ord === AsciiChar::NULL)) {
309
                return;
310 1
            } elseif($this->multiLineOpened && $ord === AsciiChar::LINE_FEED) {
311
                goto start;
312 1
            }
313
            elseif(!$this->commentOpen) {
314 1
                // Double quotes aren't required to start a string, but if they start the string then they must also end the string
315 1
                if($ord !== AsciiChar::DOUBLE_QUOTES) {
316
                    $this->stream->previous();
317 1
                    $this->txtRecordHasQuotes = false;
318
                } else {
319
                    $this->txtRecordHasQuotes = true;
320
                }
321 1
                $this->extractCharSet($tokenName);
322 1
            }
323
        }
324 1
        
325
        // multi line should no longer be open
326
        if($this->multiLineOpened) {
327
            throw new SyntaxErrorException($this->stream);
328 1
        }
329 1
    }
330 1
331
    /**
332 1
     * @param string $tokenName
333
     */
334
    private function extractCharSet(string $tokenName)
335
    {
336
        $escaping_open = false;
337
        start:
338
        if ($this->stream->isEnd()) {
339
            throw new SyntaxErrorException($this->stream);
340
        }
341
        $ord = $this->stream->ord();
342
        $this->stream->next();
343
344
        if(!$escaping_open && $this->txtRecordHasQuotes && $ord === AsciiChar::DOUBLE_QUOTES) {
345
            $this->txtExtractor($tokenName);
346
        } elseif(!$this->txtRecordHasQuotes && $ord === AsciiChar::SPACE) {
347
            // If there aren't quotes around a character string, a space terminates the string
348
            return;
349
        } else {
350
            if($ord === AsciiChar::LINE_FEED || $ord === AsciiChar::VERTICAL_TAB || $ord === AsciiChar::NULL) {
351
                if($this->txtRecordHasQuotes) {
352
                    $this->stream->previous();
353
                    throw new SyntaxErrorException($this->stream);
354
                }
355
                else {
356
                    return;
357
                }
358
            }
359
360
            $this->tokens[$tokenName] .= chr($ord);
361
362
            // Escaping open is only set for one iteration so that it doesn't break on double slashes ie. \\
363
            if($ord === AsciiChar::BACKSLASH) {
364
                $escaping_open = true;
365
            } else {
366
                $escaping_open = false;
367
            }
368
369
            goto start;
370
        }
371
    }
372
}
373