Test Setup Failed
Pull Request — master (#20)
by
unknown
02:09
created

RData   F

Complexity

Total Complexity 78

Size/Duplication

Total Lines 328
Duplicated Lines 2.44 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 95.17%

Importance

Changes 0
Metric Value
wmc 78
lcom 1
cbo 2
dl 8
loc 328
ccs 138
cts 145
cp 0.9517
rs 2.16
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A ignoreComment() 8 8 3
A __construct() 0 9 2
A isKnownType() 0 4 1
A tokenize() 0 10 2
D defaultExtractor() 0 75 26
B endRecord() 0 32 11
D txtExtractor() 0 61 25
B extractCharSet() 0 23 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like RData often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RData, and based on these observations, apply Extract Interface, too.

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
     * RData constructor.
97
     *
98
     * @param StringStream $stream
99
     * @param string       $type
100
     */
101
    public function __construct(StringStream $stream, string $type)
102 6
    {
103
        if (! self::isKnownType($type)) {
104 6
            throw new SyntaxErrorException($stream);
105
        }
106
107
        $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...
108
        $this->type   = $type;
109
    }
110 6
111
    /**
112 6
     * @param string $type
113 6
     * @return bool
114
     */
115
    public static function isKnownType(string $type) : bool
116 6
    {
117
        return array_key_exists($type, self::$rdataFormats);
118 6
    }
119
120
    /**
121
     * @return array
122
     */
123
    public function tokenize() : array
124 6
    {
125
        foreach (self::$rdataFormats[$this->type] as $tokenName => $extractor) {
126 6
            $this->$extractor($tokenName);
127 5
        }
128
        
129 6
        $this->endRecord();
130
131
        return $this->tokens;
132 6
    }
133
    
134 6
    /**
135 6
     * @param string $tokenName
136
     */
137
    protected function defaultExtractor(string $tokenName)
138
    {
139
        if($this->multiLineOpened) {
140 6
            $this->stream->ignoreWhitespace();
141
        } else {
142 6
            $this->stream->ignoreHorizontalSpace();
143 4
        }
144
        
145
        $this->commentOpen = false;
146 6
        
147 5
        if (!array_key_exists($tokenName, $this->tokens)) {
148 5
            $this->tokens[$tokenName] = '';
149 5
        }
150 6
        
151 1
        start:
152 1
153 1
        $ord = $this->stream->ord();
154 6
155 4
        if($ord === AsciiChar::NULL || $this->stream->isEnd()) {
156 4
            return;
157 6
        }
158 4
159
        if($ord === AsciiChar::OPEN_BRACKET && !$this->commentOpen) {
160 6
            $this->multiLineOpened = true;
161 4
            $this->stream->next();
162 4
            goto start;
163
        } elseif($this->multiLineOpened && !$this->commentOpen && $ord === AsciiChar::CLOSE_BRACKET) {
164 3
            $this->multiLineOpened = false;
165 3
            $this->stream->next();
166 3
            goto start;
167
        } elseif($this->multiLineOpened && !$this->commentOpen && $ord === AsciiChar::LINE_FEED) {
168 1
            $this->stream->next();
169 1
            goto start;
170 1
        } elseif($ord === AsciiChar::LINE_FEED && !$this->commentOpen) {
171
            return;
172 4
        } else {
173 6
            if($ord === AsciiChar::SEMICOLON) {
174 3
                $this->stream->previous();
175 3
                if($this->stream->currentAscii()->isHorizontalSpace()) {
176 3
177 3
                    $this->commentOpen = true;
178 6
                    $this->stream->next();
179 3
                    $this->stream->next();
180 3
                } else {
181 3
                    $this->stream->next();
182 6
                    $this->tokens[$tokenName] .= $this->stream->current();
183 6
                    $this->stream->next();
184 4
                }
185 4
                goto start;
186 6
            } elseif(($this->stream->currentAscii()->isVerticalSpace() || $ord === AsciiChar::NULL) && $this->commentOpen) {
187 5
                $this->stream->next();
188
                $this->stream->ignoreHorizontalSpace();
189 6
                $this->commentOpen = false;
190 6
                goto start;
191 6
            } elseif($this->commentOpen) {
192 4
                $this->commentOpen = true;
193
                $this->ignoreComment();
194 6
                goto start;
195
            } elseif(!$this->commentOpen) {
196
                if($ord === AsciiChar::SPACE && $this->tokens[$tokenName] === '') {
197
                    $this->stream->next();
198
                    goto start;
199
                } elseif($this->stream->currentAscii()->isWhiteSpace()) {
200 3
                    return;
201
                } else {
202
                    $this->tokens[$tokenName] .= $this->stream->current();
203 3
                    $this->stream->next();
204 3
                    if($this->stream->isEnd()) {
205 3
                        $this->tokens[$tokenName] = trim($this->tokens[$tokenName]);
206
                    }
207 3
                    goto start;
208
                }
209 6
            }
210
        }
211
    }
212 6
213 6 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...
214 4
    {
215
        start:
216 5
        if (!$this->stream->currentAscii()->isVerticalSpace() && !$this->stream->isEnd()) {
217 2
            $this->stream->next();
218 2
            goto start;
219 2
        }
220 5
    }
221 2
    
222 2
    protected function endRecord()
223 2
    {
224
        start:
225 2
        $ord = $this->stream->ord();
226 2
        if($ord === AsciiChar::NULL) {
227 2
            return;
228
        }
229 5
        if($ord === AsciiChar::SEMICOLON) {
230 5
            $this->stream->next();
231 4
            $this->commentOpen = true;
232 4
            goto start;
233
        } elseif($this->commentOpen) {
234 4
            if($ord === AsciiChar::NULL() || $ord === AsciiChar::LINE_FEED) {
235 4
                $this->commentOpen = false;
236 5
                goto start;
237 5
            } else {
238
                $this->stream->next();
239
                $this->commentOpen = true;
240 3
                goto start;
241
            }
242
        } elseif(!$this->commentOpen)  {
243
            if($this->multiLineOpened) {
244
                if($ord === AsciiChar::CLOSE_BRACKET) {
245 1
                    $this->multiLineOpened = false;
246
                }
247 1
                $this->stream->next();
248 1
                goto start;
249
            } elseif($ord === AsciiChar::NULL() || $ord === AsciiChar::LINE_FEED) {
250
                return;
251
            }
252 1
        }
253 1
    }
254
255 1
    /**
256
     * @param string $tokenName
257
     */
258
    private function txtExtractor(string $tokenName)
259
    {
260 1
        if (!array_key_exists($tokenName, $this->tokens)) {
261 1
            $this->tokens[$tokenName] = '';
262 1
        }
263 1
264 1
        start:
265 1
        $ord = $this->stream->ord();
266 1
        $this->stream->next();
267 1
268 1
        if($ord === 0) { // if end of record
269 1
            return;
270
        }
271
272 1
        // comment starts
273 1
        if($ord === 59) {
274
            $this->commentOpen = true;
275
            goto start;
276
        } elseif($this->commentOpen === true && $ord !== 10) {
277 1
            $this->commentOpen = true;
278 1
            goto start;
279
        } elseif($this->commentOpen === true && ($ord === 10 || $ord === 0)) {
280
            $this->stream->previous();
281
            $this->commentOpen = false;
282 1
            goto start;
283 1
        } else {
284 1
            // ignore blanck line
285
            if($ord === 32) {
286
                goto start;
287
            }
288 1
289 1
            // Find starts of char set
290 1
            if($ord === 34 && !$this->commentOpen) { // "
291
                $this->extractCharSet($tokenName);
292
            }
293
294 1
            // multi line opened
295
            if($ord === 40 && !$this->commentOpen) {
296
                $this->multiLineOpened = true;
297
                goto start;
298
            }
299 1
300 1
            // multi line closed
301 1
            if($this->multiLineOpened && !$this->commentOpen && $ord === 41) {
302 1
                $this->multiLineOpened = false;
303
                goto start;
304
            }
305 1
306
            // comment end in multi line TXT record
307
            if($ord === 10 && $this->commentOpen && $this->multiLineOpened) {
308
                goto start;
309
            }
310 1
311
            // is record ends?
312 1
            if(!$this->multiLineOpened && ($ord === 10 || $ord === 0)) {
313
                return;
314 1
            } elseif($this->multiLineOpened && $ord === 10) {
315 1
                goto start;
316
            }
317 1
        }
318
    }
319
320
    /**
321 1
     * @param string $tokenName
322 1
     */
323
    private function extractCharSet(string $tokenName)
324 1
    {
325
        $escaping_open = false;
326
        start:
327
        $ord = $this->stream->ord();
328 1
        $this->stream->next();
329 1
330 1
        if($ord === AsciiChar::NULL) { // if end of record
331
            throw new SyntaxErrorException($this->stream);
332 1
        }
333
334
        if(!$escaping_open && $ord === 34) {
335
            $this->txtExtractor($tokenName);
336
        } else {
337
            if($ord === AsciiChar::LINE_FEED || $ord === AsciiChar::VERTICAL_TAB || $ord === AsciiChar::NULL) {
338
                $this->stream->previous();
339
                throw new SyntaxErrorException($this->stream);
340
            }
341
            $this->tokens[$tokenName] .= chr($ord);
342
            $escaping_open = ($ord === 92 && !$escaping_open);
343
            goto start;
344
        }
345
    }
346
}
347