Passed
Push — master ( 1b54b9...ff455f )
by Sam
10:10 queued 11s
created

Message   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 569
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 141
c 3
b 0
f 0
dl 0
loc 569
ccs 187
cts 187
cp 1
rs 4.5599
wmc 58

44 Methods

Rating   Name   Duplication   Size   Complexity  
A isAuthenticData() 0 3 1
A setRecursionDesired() 0 3 1
A setResponse() 0 3 1
A isRecursionDesired() 0 3 1
A addAuthoritative() 0 3 1
A getAdditionals() 0 3 1
A toWire() 0 30 2
A getBit9() 0 3 1
A setId() 0 3 1
A getOpcode() 0 3 1
A setAuthoritatives() 0 5 2
A getRcode() 0 3 1
A isResponse() 0 3 1
A isCheckingDisabled() 0 3 1
A isAuthoritative() 0 3 1
B decodeName() 0 37 6
A countQuestions() 0 3 1
A setAuthoritative() 0 3 1
A setBit9() 0 3 1
A countAuthoritatives() 0 3 1
A setQuery() 0 3 1
A setRcode() 0 3 1
A addAdditional() 0 3 1
A isQuery() 0 3 1
A addQuestion() 0 3 1
A isTruncated() 0 3 1
A setTruncated() 0 3 1
A setOpcode() 0 3 1
A addAnswer() 0 3 1
A getQuestions() 0 3 1
A setAnswers() 0 5 2
A getAnswers() 0 3 1
A encodeName() 0 14 3
A setCheckingDisabled() 0 3 1
A fromWire() 0 34 3
A setAdditionals() 0 5 2
A setQuestions() 0 5 2
A getId() 0 3 1
A countAdditionals() 0 3 1
A getAuthoritatives() 0 3 1
A setRecursionAvailable() 0 3 1
A isRecursionAvailable() 0 3 1
A countAnswers() 0 3 1
A setAuthenticData() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Message 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.

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 Message, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Badcow DNS Library.
7
 *
8
 * (c) Samuel Williams <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Badcow\DNS;
15
16
use Badcow\DNS\Rdata\UnsupportedTypeException;
17
18
class Message
19
{
20
    /**
21
     * ID.
22
     *
23
     * @var int
24
     */
25
    private $id;
26
27
    /**
28
     * QR.
29
     *
30
     * @var bool
31
     */
32
    private $isResponse;
33
34
    /**
35
     * OPCODE.
36
     *
37
     * @var int
38
     */
39
    private $opcode;
40
41
    /**
42
     * AA.
43
     *
44
     * @var bool
45
     */
46
    private $isAuthoritative;
47
48
    /**
49
     * TC.
50
     *
51
     * @var bool
52
     */
53
    private $isTruncated;
54
55
    /**
56
     * RD.
57
     *
58
     * @var bool
59
     */
60
    private $isRecursionDesired;
61
62
    /**
63
     * RA.
64
     *
65
     * @var bool
66
     */
67
    private $isRecursionAvailable;
68
69
    /**
70
     * Bit 9 of the header flags.
71
     *
72
     * @var int
73
     */
74
    private $bit9 = 0;
75
76
    /**
77
     * AD.
78
     *
79
     * {@link https://tools.ietf.org/html/rfc4035#section-3.2.3}
80
     *
81
     * @var bool
82
     */
83
    private $isAuthenticData;
84
85
    /**
86
     * CD.
87
     *
88
     * {@link https://tools.ietf.org/html/rfc4035#section-3.2.2}
89
     *
90
     * @var bool
91
     */
92
    private $isCheckingDisabled;
93
94
    /**
95
     * RCODE.
96
     *
97
     * @var int
98
     */
99
    private $rcode;
100
101
    /**
102
     * @var Question[]
103
     */
104
    private $questions = [];
105
106
    /**
107
     * @var ResourceRecord[]
108
     */
109
    private $answers = [];
110
111
    /**
112
     * @var ResourceRecord[]
113
     */
114
    private $authoritatives = [];
115
116
    /**
117
     * @var ResourceRecord[]
118
     */
119
    private $additionals = [];
120
121
    /**
122
     * Encode a domain name as a sequence of labels.
123
     *
124
     * @param string $name
125
     *
126
     * @return string
127
     */
128 38
    public static function encodeName(string $name): string
129
    {
130 38
        if ('.' === $name) {
131 3
            return chr(0);
132
        }
133
134 35
        $name = rtrim($name, '.').'.';
135 35
        $res = '';
136
137 35
        foreach (explode('.', $name) as $label) {
138 35
            $res .= chr(strlen($label)).$label;
139
        }
140
141 35
        return $res;
142
    }
143
144
    /**
145
     * @param string $string
146
     * @param int    $offset
147
     *
148
     * @return string
149
     */
150 30
    public static function decodeName(string $string, int &$offset = 0): string
151
    {
152 30
        $len = ord($string[$offset]);
153 30
        ++$offset;
154
155 30
        $isCompressed = (bool) (0b11000000 & $len);
156 30
        $_offset = 0;
157
158 30
        if ($isCompressed) {
159 7
            $_offset = $offset + 1;
160 7
            $offset = (0b00111111 & $len) * 256 + ord($string[$offset]);
161 7
            $len = ord($string[$offset]);
162 7
            ++$offset;
163
        }
164
165 30
        if (0 === $len) {
166 3
            return '.';
167
        }
168
169 27
        $name = '';
170 27
        while (0 !== $len) {
171 27
            $name .= substr($string, $offset, $len).'.';
172 27
            $offset += $len;
173 27
            $len = ord($string[$offset]);
174 27
            if ($len & 0b11000000) {
175 7
                $name .= self::decodeName($string, $offset);
176 7
                break;
177
            }
178
179 27
            ++$offset;
180
        }
181
182 27
        if ($isCompressed) {
183 7
            $offset = $_offset;
184
        }
185
186 27
        return $name;
187
    }
188
189
    /**
190
     * @return int
191
     */
192 2
    public function getId(): int
193
    {
194 2
        return $this->id;
195
    }
196
197
    /**
198
     * @param int $id
199
     */
200 8
    public function setId(int $id): void
201
    {
202 8
        $this->id = $id;
203 8
    }
204
205
    /**
206
     * @return bool
207
     */
208 2
    public function isResponse(): bool
209
    {
210 2
        return $this->isResponse;
211
    }
212
213
    /**
214
     * @param bool $isResponse
215
     */
216 8
    public function setResponse(bool $isResponse): void
217
    {
218 8
        $this->isResponse = $isResponse;
219 8
    }
220
221
    /**
222
     * @return bool
223
     */
224 1
    public function isQuery(): bool
225
    {
226 1
        return !$this->isResponse;
227
    }
228
229
    /**
230
     * @param bool $query
231
     */
232 1
    public function setQuery(bool $query): void
233
    {
234 1
        $this->setResponse(!$query);
235 1
    }
236
237
    /**
238
     * @return int
239
     */
240 2
    public function getOpcode(): int
241
    {
242 2
        return $this->opcode;
243
    }
244
245
    /**
246
     * @param int $opcode
247
     */
248 7
    public function setOpcode(int $opcode): void
249
    {
250 7
        $this->opcode = $opcode;
251 7
    }
252
253
    /**
254
     * @return bool
255
     */
256 2
    public function isAuthoritative(): bool
257
    {
258 2
        return $this->isAuthoritative;
259
    }
260
261
    /**
262
     * @param bool $isAuthoritative
263
     */
264 7
    public function setAuthoritative(bool $isAuthoritative): void
265
    {
266 7
        $this->isAuthoritative = $isAuthoritative;
267 7
    }
268
269
    /**
270
     * @return bool
271
     */
272 2
    public function isTruncated(): bool
273
    {
274 2
        return $this->isTruncated;
275
    }
276
277
    /**
278
     * @param bool $isTruncated
279
     */
280 7
    public function setTruncated(bool $isTruncated): void
281
    {
282 7
        $this->isTruncated = $isTruncated;
283 7
    }
284
285
    /**
286
     * @return bool
287
     */
288 2
    public function isRecursionDesired(): bool
289
    {
290 2
        return $this->isRecursionDesired;
291
    }
292
293
    /**
294
     * @param bool $isRecursionDesired
295
     */
296 7
    public function setRecursionDesired(bool $isRecursionDesired): void
297
    {
298 7
        $this->isRecursionDesired = $isRecursionDesired;
299 7
    }
300
301
    /**
302
     * @return bool
303
     */
304 2
    public function isRecursionAvailable(): bool
305
    {
306 2
        return $this->isRecursionAvailable;
307
    }
308
309
    /**
310
     * @param bool $isRecursionAvailable
311
     */
312 7
    public function setRecursionAvailable(bool $isRecursionAvailable): void
313
    {
314 7
        $this->isRecursionAvailable = $isRecursionAvailable;
315 7
    }
316
317
    /**
318
     * @return int
319
     */
320 2
    public function getBit9(): int
321
    {
322 2
        return $this->bit9;
323
    }
324
325
    /**
326
     * @param int $bit9
327
     */
328 7
    public function setBit9(int $bit9): void
329
    {
330 7
        $this->bit9 = $bit9;
331 7
    }
332
333
    /**
334
     * @return bool
335
     */
336 2
    public function isAuthenticData(): bool
337
    {
338 2
        return $this->isAuthenticData;
339
    }
340
341
    /**
342
     * @param bool $isAuthenticData
343
     */
344 7
    public function setAuthenticData(bool $isAuthenticData): void
345
    {
346 7
        $this->isAuthenticData = $isAuthenticData;
347 7
    }
348
349
    /**
350
     * @return bool
351
     */
352 2
    public function isCheckingDisabled(): bool
353
    {
354 2
        return $this->isCheckingDisabled;
355
    }
356
357
    /**
358
     * @param bool $isCheckingDisabled
359
     */
360 7
    public function setCheckingDisabled(bool $isCheckingDisabled): void
361
    {
362 7
        $this->isCheckingDisabled = $isCheckingDisabled;
363 7
    }
364
365
    /**
366
     * @return int
367
     */
368 2
    public function getRcode(): int
369
    {
370 2
        return $this->rcode;
371
    }
372
373
    /**
374
     * @param int $rcode
375
     */
376 7
    public function setRcode(int $rcode): void
377
    {
378 7
        $this->rcode = $rcode;
379 7
    }
380
381
    /**
382
     * @return Question[]
383
     */
384 3
    public function getQuestions(): array
385
    {
386 3
        return $this->questions;
387
    }
388
389
    /**
390
     * @param Question $question
391
     */
392 8
    public function addQuestion(Question $question): void
393
    {
394 8
        $this->questions[] = $question;
395 8
    }
396
397
    /**
398
     * @param Question[] $questions
399
     */
400 1
    public function setQuestions(array $questions): void
401
    {
402 1
        $this->questions = [];
403 1
        foreach ($questions as $question) {
404 1
            $this->addQuestion($question);
405
        }
406 1
    }
407
408
    /**
409
     * @return ResourceRecord[]
410
     */
411 3
    public function getAnswers(): array
412
    {
413 3
        return $this->answers;
414
    }
415
416
    /**
417
     * @param ResourceRecord $answer
418
     */
419 7
    public function addAnswer(ResourceRecord $answer): void
420
    {
421 7
        $this->answers[] = $answer;
422 7
    }
423
424
    /**
425
     * @param ResourceRecord[] $answers
426
     */
427 7
    public function setAnswers(array $answers): void
428
    {
429 7
        $this->answers = [];
430 7
        foreach ($answers as $answer) {
431 7
            $this->addAnswer($answer);
432
        }
433 7
    }
434
435
    /**
436
     * @return ResourceRecord[]
437
     */
438 2
    public function getAuthoritatives(): array
439
    {
440 2
        return $this->authoritatives;
441
    }
442
443
    /**
444
     * @param ResourceRecord $authoritative
445
     */
446 5
    public function addAuthoritative(ResourceRecord $authoritative): void
447
    {
448 5
        $this->authoritatives[] = $authoritative;
449 5
    }
450
451
    /**
452
     * @param ResourceRecord[] $authoritatives
453
     */
454 7
    public function setAuthoritatives(array $authoritatives): void
455
    {
456 7
        $this->authoritatives = [];
457 7
        foreach ($authoritatives as $authoritative) {
458 5
            $this->addAuthoritative($authoritative);
459
        }
460 7
    }
461
462
    /**
463
     * @return ResourceRecord[]
464
     */
465 3
    public function getAdditionals(): array
466
    {
467 3
        return $this->additionals;
468
    }
469
470
    /**
471
     * @param ResourceRecord $additional
472
     */
473 7
    public function addAdditional(ResourceRecord $additional): void
474
    {
475 7
        $this->additionals[] = $additional;
476 7
    }
477
478
    /**
479
     * @param ResourceRecord[] $additionals
480
     */
481 7
    public function setAdditionals(array $additionals): void
482
    {
483 7
        $this->additionals = [];
484 7
        foreach ($additionals as $additional) {
485 7
            $this->addAdditional($additional);
486
        }
487 7
    }
488
489 6
    public function countQuestions(): int
490
    {
491 6
        return count($this->questions);
492
    }
493
494 6
    public function countAnswers(): int
495
    {
496 6
        return count($this->answers);
497
    }
498
499 6
    public function countAuthoritatives(): int
500
    {
501 6
        return count($this->authoritatives);
502
    }
503
504 6
    public function countAdditionals(): int
505
    {
506 6
        return count($this->additionals);
507
    }
508
509
    /**
510
     * @return string
511
     *
512
     * @throws UnsetValueException
513
     */
514 3
    public function toWire(): string
515
    {
516
        $flags = 0x0 |
517 3
        ($this->isResponse & 0x1) << 15 |
518 3
        ($this->opcode & 0xf) << 11 |
519 3
        ($this->isAuthoritative & 0x1) << 10 |
520 3
        ($this->isTruncated & 0x1) << 9 |
521 3
        ($this->isRecursionDesired & 0x1) << 8 |
522 3
        ($this->isRecursionAvailable & 0x1) << 7 |
523 3
        ($this->bit9 & 0x1) << 6 |
524 3
        ($this->isAuthenticData & 0x1) << 5 |
525 3
        ($this->isCheckingDisabled & 0x1) << 4 |
526 3
        ($this->rcode & 0xf);
527
528 3
        $encoded = pack(
529 3
            'nnnnnn',
530 3
            $this->id,
531
            $flags,
532 3
            $this->countQuestions(),
533 3
            $this->countAnswers(),
534 3
            $this->countAuthoritatives(),
535 3
            $this->countAdditionals()
536
        );
537
538 3
        foreach (array_merge($this->questions, $this->answers, $this->authoritatives, $this->additionals) as $resource) {
539
            /* @var ResourceRecord|Question $resource */
540 3
            $encoded .= $resource->toWire();
541
        }
542
543 3
        return $encoded;
544
    }
545
546
    /**
547
     * @param string $encoded
548
     *
549
     * @return Message
550
     *
551
     * @throws UnsupportedTypeException
552
     */
553 7
    public static function fromWire(string $encoded): Message
554
    {
555 7
        $message = new self();
556 7
        $offset = 0;
557 7
        $header = unpack('nid/nflags/nqdcount/nancount/nnscount/narcount', $encoded, $offset);
558 7
        $offset += 12;
559 7
        $flags = $header['flags'];
560
561 7
        $message->setId($header['id']);
562 7
        $message->setResponse((bool) ($flags >> 15 & 0x1));
563 7
        $message->setOpcode($flags >> 11 & 0xf);
564 7
        $message->setAuthoritative((bool) ($flags >> 10 & 0x1));
565 7
        $message->setTruncated((bool) ($flags >> 9 & 0x1));
566 7
        $message->setRecursionDesired((bool) ($flags >> 8 & 0x1));
567 7
        $message->setRecursionAvailable((bool) ($flags >> 7 & 0x1));
568 7
        $message->setBit9($flags >> 6 & 0x1);
569 7
        $message->setAuthenticData((bool) ($flags >> 5 & 0x1));
570 7
        $message->setCheckingDisabled((bool) ($flags >> 4 & 0x1));
571 7
        $message->setRcode($flags & 0xf);
572
573 7
        for ($i = 0; $i < $header['qdcount']; ++$i) {
574 7
            $message->addQuestion(Question::fromWire($encoded, $offset));
575
        }
576
577 7
        $rrs = [];
578 7
        while ($offset < strlen($encoded)) {
579 7
            $rrs[] = ResourceRecord::fromWire($encoded, $offset);
580
        }
581
582 7
        $message->setAnswers(array_splice($rrs, 0, $header['ancount']));
583 7
        $message->setAuthoritatives(array_splice($rrs, 0, $header['nscount']));
584 7
        $message->setAdditionals(array_splice($rrs, 0, $header['arcount']));
585
586 7
        return $message;
587
    }
588
}
589