Passed
Push — master ( 42d6ea...19d510 )
by Sergey
02:33
created

RData::ignoreComment()   A

Complexity

Conditions 3
Paths 0

Size

Total Lines 8
Code Lines 5

Duplication

Lines 8
Ratio 100 %

Code Coverage

Tests 5
CRAP Score 3
Metric Value
dl 8
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
cc 3
eloc 5
nc 0
nop 0
crap 3
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
    ];
63
    /**
64
     * @var string
65
     */
66
    private $type;
67
    /**
68
     * @var array
69
     */
70
    private $tokens = [];
71
72
    /**
73
     * @var bool
74
     */
75
    private $commentOpen = false;
76
77
    /**
78
     * @var bool
79
     */
80
    private $multiLineOpened = false;
81
82
    /**
83
     * RData constructor.
84
     *
85
     * @param StringStream $stream
86
     * @param string       $type
87
     */
88 5
    public function __construct(StringStream $stream, string $type)
89
    {
90 5
        if (! self::isKnownType($type)) {
91
            throw new SyntaxErrorException($stream);
92
        }
93
94 5
        $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...
95 5
        $this->type   = $type;
96 5
    }
97
98
    /**
99
     * @param string $type
100
     * @return bool
101
     */
102 5
    public static function isKnownType(string $type) : bool
103
    {
104 5
        return array_key_exists($type, self::$rdataFormats);
105
    }
106
107
    /**
108
     * @return array
109
     */
110 5
    public function tokenize() : array
111
    {
112 5
        foreach (self::$rdataFormats[$this->type] as $tokenName => $extractor) {
113 5
            $this->$extractor($tokenName);
114
        }
115
        
116 5
        $this->endRecord();
117
118 5
        return $this->tokens;
119
    }
120
    
121
    /**
122
     * @param string $tokenName
123
     */
124 5
    protected function defaultExtractor(string $tokenName)
125
    {
126 5
        if($this->multiLineOpened) {
127 5
            $this->stream->ignoreWhitespace();
128
        } else {
129 5
            $this->stream->ignoreHorizontalSpace();
130
        }
131
        
132 5
        $this->commentOpen = false;
133
        
134 5
        if (!array_key_exists($tokenName, $this->tokens)) {
135 5
            $this->tokens[$tokenName] = '';
136
        }
137
        
138
        start:
139
140 5
        $ord = $this->stream->ord();
141
142 5
        if($ord == AsciiChar::NULL) {
143 3
            return;
144
        }
145
146 5
        if($ord === AsciiChar::OPEN_BRACKET && !$this->commentOpen) {
147 5
            $this->multiLineOpened = true;
148 5
            $this->stream->next();
149 5
            goto start;
150 5
        } elseif($this->multiLineOpened && !$this->commentOpen && $ord === AsciiChar::CLOSE_BRACKET) {
151 1
            $this->multiLineOpened = false;
152 1
            $this->stream->next();
153 1
            goto start;
154 5
        } elseif($this->multiLineOpened && !$this->commentOpen && $ord === AsciiChar::LINE_FEED) {
155 4
            $this->stream->next();
156 4
            goto start;
157 5
        } elseif($ord === AsciiChar::LINE_FEED && !$this->commentOpen) {
158 4
            return;
159
        } else {
160 5
            if($ord === AsciiChar::SEMICOLON) {
161 4
                $this->stream->previous();
162 4
                if($this->stream->currentAscii()->isHorizontalSpace()) {
163
164 3
                    $this->commentOpen = true;
165 3
                    $this->stream->next();
166 3
                    $this->stream->next();
167
                } else {
168 1
                    $this->stream->next();
169 1
                    $this->tokens[$tokenName] .= $this->stream->current();
170 1
                    $this->stream->next();
171
                }
172 4
                goto start;
173 5
            } elseif(($this->stream->currentAscii()->isVerticalSpace() || $ord === AsciiChar::NULL) && $this->commentOpen) {
174 3
                $this->stream->next();
175 3
                $this->stream->ignoreHorizontalSpace();
176 3
                $this->commentOpen = false;
177 3
                goto start;
178 5
            } elseif($this->commentOpen) {
179 3
                $this->commentOpen = true;
180 3
                $this->ignoreComment();
181 3
                goto start;
182 5
            } elseif(!$this->commentOpen) {
183 5
                if($ord === AsciiChar::SPACE && $this->tokens[$tokenName] === "") {
184 4
                    $this->stream->next();
185 4
                    goto start;
186 5
                } elseif($this->stream->currentAscii()->isHorizontalSpace()) {
187 5
                    return;
188
                } else {
189 5
                    $this->tokens[$tokenName] .= $this->stream->current();
190 5
                    $this->stream->next();
191 5
                    goto start;
192
                }
193
            }
194
        }
195
    }
196
197 3 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...
198
    {
199
        start:
200 3
        if (!$this->stream->currentAscii()->isVerticalSpace() && !$this->stream->isEnd()) {
201 3
            $this->stream->next();
202 3
            goto start;
203
        }
204 3
    }
205
    
206 5
    protected function endRecord()
207
    {
208
        start:
209 5
        $ord = $this->stream->ord();
210 5
        if($ord == AsciiChar::NULL) {
211 3
            return;
212
        }
213 5
        if($ord === AsciiChar::SEMICOLON) {
214 2
            $this->stream->next();
215 2
            $this->commentOpen = true;
216 2
            goto start;
217 5
        } elseif($this->commentOpen) {
218 2
            if($ord === AsciiChar::NULL() || $ord === AsciiChar::LINE_FEED) {
219 2
                $this->commentOpen = false;
220 2
                goto start;
221
            } else {
222 2
                $this->stream->next();
223 2
                $this->commentOpen = true;
224 2
                goto start;
225
            }
226 5
        } elseif(!$this->commentOpen)  {
227 5
            if($this->multiLineOpened) {
228 4
                if($ord === AsciiChar::CLOSE_BRACKET) {
229 4
                    $this->multiLineOpened = false;
230
                }
231 4
                $this->stream->next();
232 4
                goto start;
233 5
            } elseif($ord === AsciiChar::NULL() || $ord === AsciiChar::LINE_FEED) {
234 5
                return;
235
            }
236
        }
237 3
    }
238
239
    /**
240
     * @param string $tokenName
241
     */
242 1
    private function txtExtractor(string $tokenName)
243
    {
244 1
        if (!array_key_exists($tokenName, $this->tokens)) {
245 1
            $this->tokens[$tokenName] = '';
246
        }
247
248
        start:
249 1
        $ord = $this->stream->ord();
250 1
        $this->stream->next();
251
252 1
        if($ord == 0) { // if end of record
253
            return;
254
        }
255
256
        // comment starts
257 1
        if($ord === 59) {
258 1
            $this->commentOpen = true;
259 1
            goto start;
260 1
        } elseif($this->commentOpen == true && $ord !== 10) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
261 1
            $this->commentOpen = true;
262 1
            goto start;
263 1
        } elseif($this->commentOpen == true && ($ord === 10 || $ord === 0)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
264 1
            $this->stream->previous();
265 1
            $this->commentOpen = false;
266 1
            goto start;
267
        } else {
268
            // ignore blanck line
269 1
            if($ord === 32) {
270 1
                goto start;
271
            }
272
273
            // Find starts of char set
274 1
            if($ord === 34 && !$this->commentOpen) { // "
275 1
                $this->extractCharSet($tokenName);
276
            }
277
278
            // multi line opened
279 1
            if($ord === 40 && !$this->commentOpen) {
280 1
                $this->multiLineOpened = true;
281 1
                goto start;
282
            }
283
284
            // multi line closed
285 1
            if($this->multiLineOpened && !$this->commentOpen && $ord === 41) {
286 1
                $this->multiLineOpened = false;
287 1
                goto start;
288
            }
289
290
            // comment end in multi line TXT record
291 1
            if($ord === 10 && $this->commentOpen && $this->multiLineOpened) {
292
                goto start;
293
            }
294
295
            // is record ends?
296 1
            if(!$this->multiLineOpened && ($ord === 10 || $ord === 0)) {
297 1
                return;
298 1
            } elseif($this->multiLineOpened && $ord === 10) {
299 1
                goto start;
300
            }
301
        }
302 1
    }
303
304
    /**
305
     * @param string $tokenName
306
     */
307 1
    private function extractCharSet(string $tokenName)
308
    {
309 1
        $escaping_open = false;
310
        start:
311 1
        $ord = $this->stream->ord();
312 1
        $this->stream->next();
313
314 1
        if($ord == 0) { // if end of record
315
            return;
316
        }
317
318 1
        if(!$escaping_open && $ord === 34) {
319 1
            $this->txtExtractor($tokenName);
320
        } else {
321 1
            $this->tokens[$tokenName] .= chr($ord);
322 1
            $escaping_open = ($ord === 92 && !$escaping_open);
323 1
            goto start;
324
        }
325
    }
326
}