Completed
Push — master ( 57eb77...af9a70 )
by Sam
04:28 queued 02:46
created

Parser::handleAplRdata()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 16
ccs 10
cts 10
cp 1
rs 9.9666
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
namespace Badcow\DNS\Parser;
4
5
use Badcow\DNS\Classes;
6
use Badcow\DNS\ResourceRecord;
7
use Badcow\DNS\Zone;
8
use Badcow\DNS\Rdata;
9
10
class Parser
11
{
12
    /**
13
     * @var string
14
     */
15
    private $string;
16
17
    /**
18
     * @var string
19
     */
20
    private $previousName;
21
22
    /**
23
     * @var Zone
24
     */
25
    private $zone;
26
27
    /**
28
     * Array of methods that take an ArrayIterator and return an Rdata object. The array key is the Rdata type.
29
     *
30
     * @var array
31
     */
32
    private $rdataHandlers = [];
33
34
    /**
35
     * Parser constructor.
36
     * @param array $rdataHandlers
37
     */
38 7
    public function __construct(array $rdataHandlers = [])
39
    {
40 7
        $this->rdataHandlers = array_merge(RdataHandlers::getHandlers(), $rdataHandlers);
41 7
    }
42
43
    /**
44
     * @param string $name
45
     * @param string $zone
46
     *
47
     * @return Zone
48
     *
49
     * @throws ParseException
50
     */
51 7
    public static function parse(string $name, string $zone): Zone
52
    {
53 7
        return (new self())->makeZone($name, $zone);
54
    }
55
56
    /**
57
     * @param $name
58
     * @param $string
59
     *
60
     * @return Zone
61
     *
62
     * @throws ParseException
63
     */
64 7
    public function makeZone($name, $string): Zone
65
    {
66 7
        $this->zone = new Zone($name);
67 7
        $this->string = Normaliser::normalise($string);
68
69 7
        foreach (explode(Tokens::LINE_FEED, $this->string) as $line) {
70 7
            $this->processLine($line);
71
        }
72
73 5
        return $this->zone;
74
    }
75
76
    /**
77
     * @param string $line
78
     *
79
     * @throws ParseException
80
     */
81 7
    private function processLine(string $line): void
82
    {
83 7
        $iterator = new \ArrayIterator(explode(Tokens::SPACE, $line));
84
85 7
        if ($this->isControlEntry($iterator)) {
86 4
            $this->processControlEntry($iterator);
87
88 4
            return;
89
        }
90
91 7
        $resourceRecord = new ResourceRecord();
92
93 7
        $this->processResourceName($iterator, $resourceRecord);
94 7
        $this->processTtl($iterator, $resourceRecord);
95 7
        $this->processClass($iterator, $resourceRecord);
96 7
        $resourceRecord->setRdata($this->extractRdata($iterator));
97
98 5
        $this->zone->addResourceRecord($resourceRecord);
99 5
    }
100
101
    /**
102
     * Processes control entries at the top of a BIND record, i.e. $ORIGIN, $TTL, $INCLUDE, etc.
103
     *
104
     * @param \ArrayIterator $iterator
105
     */
106 4
    private function processControlEntry(\ArrayIterator $iterator): void
107
    {
108 4
        if ('$TTL' === strtoupper($iterator->current())) {
109 4
            $iterator->next();
110 4
            $this->zone->setDefaultTtl((int) $iterator->current());
111
        }
112 4
    }
113
114
    /**
115
     * Processes a ResourceRecord name.
116
     *
117
     * @param \ArrayIterator $iterator
118
     * @param ResourceRecord $resourceRecord
119
     */
120 7
    private function processResourceName(\ArrayIterator $iterator, ResourceRecord $resourceRecord): void
121
    {
122 7
        if ($this->isResourceName($iterator)) {
123 7
            $this->previousName = $iterator->current();
124 7
            $iterator->next();
125
        }
126
127 7
        $resourceRecord->setName($this->previousName);
128 7
    }
129
130
    /**
131
     * Set RR's TTL if there is one.
132
     *
133
     * @param \ArrayIterator $iterator
134
     * @param ResourceRecord $resourceRecord
135
     */
136 7
    private function processTtl(\ArrayIterator $iterator, ResourceRecord $resourceRecord): void
137
    {
138 7
        if ($this->isTTL($iterator)) {
139 4
            $resourceRecord->setTtl($iterator->current());
140 4
            $iterator->next();
141
        }
142 7
    }
143
144
    /**
145
     * Set RR's class if there is one.
146
     *
147
     * @param \ArrayIterator $iterator
148
     * @param ResourceRecord $resourceRecord
149
     */
150 7
    private function processClass(\ArrayIterator $iterator, ResourceRecord $resourceRecord): void
151
    {
152 7
        if (Classes::isValid(strtoupper($iterator->current()))) {
153 6
            $resourceRecord->setClass(strtoupper($iterator->current()));
154 6
            $iterator->next();
155
        }
156 7
    }
157
158
    /**
159
     * Determine if iterant is a resource name.
160
     *
161
     * @param \ArrayIterator $iterator
162
     *
163
     * @return bool
164
     */
165 7
    private function isResourceName(\ArrayIterator $iterator): bool
166
    {
167
        return !(
168 7
            $this->isTTL($iterator) ||
169 7
            Classes::isValid(strtoupper($iterator->current())) ||
170 7
            RDataTypes::isValid(strtoupper($iterator->current()))
171
        );
172
    }
173
174
    /**
175
     * Determine if iterant is a control entry such as $TTL, $ORIGIN, $INCLUDE, etcetera.
176
     *
177
     * @param \ArrayIterator $iterator
178
     *
179
     * @return bool
180
     */
181 7
    private function isControlEntry(\ArrayIterator $iterator): bool
182
    {
183 7
        return 1 === preg_match('/^\$[A-Z0-9]+/i', $iterator->current());
184
    }
185
186
    /**
187
     * Determine if the iterant is a TTL (i.e. it is an integer).
188
     *
189
     * @param \ArrayIterator $iterator
190
     *
191
     * @return bool
192
     */
193 7
    private function isTTL(\ArrayIterator $iterator): bool
194
    {
195 7
        return 1 === preg_match('/^\d+$/', $iterator->current());
196
    }
197
198
    /**
199
     * @param \ArrayIterator $iterator
200
     *
201
     * @return RData\RDataInterface
202
     *
203
     * @throws ParseException
204
     */
205 7
    private function extractRdata(\ArrayIterator $iterator): Rdata\RdataInterface
206
    {
207 7
        $type = strtoupper($iterator->current());
208 7
        $iterator->next();
209
210 7
        if (array_key_exists($type, $this->rdataHandlers)) {
211 6
            return call_user_func($this->rdataHandlers[$type], $iterator);
212
        }
213
214 5
        return RdataHandlers::catchAll($type, $iterator);
215
    }
216
}
217