Completed
Push — master ( 5836e1...99cc50 )
by Sam
15s queued 10s
created

AlignedBuilder::makeLine()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 4
crap 2
1
<?php
2
3
/*
4
 * This file is part of Badcow DNS Library.
5
 *
6
 * (c) Samuel Williams <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Badcow\DNS;
13
14
use Badcow\DNS\Rdata\A;
15
use Badcow\DNS\Rdata\AAAA;
16
use Badcow\DNS\Rdata\APL;
17
use Badcow\DNS\Rdata\CNAME;
18
use Badcow\DNS\Rdata\DNAME;
19
use Badcow\DNS\Rdata\HINFO;
20
use Badcow\DNS\Rdata\LOC;
21
use Badcow\DNS\Rdata\MX;
22
use Badcow\DNS\Rdata\NS;
23
use Badcow\DNS\Rdata\PTR;
24
use Badcow\DNS\Rdata\SOA;
25
use Badcow\DNS\Rdata\SRV;
26
use Badcow\DNS\Rdata\TXT;
27
use Badcow\DNS\Rdata\RdataInterface;
28
29
class AlignedBuilder
30
{
31
    const COMMENT_DELIMINATOR = '; ';
32
33
    const MULTILINE_BEGIN = '(';
34
35
    const MULTILINE_END = ')';
36
37
    /**
38
     * The order in which Resource Records should appear in a zone.
39
     *
40
     * @var array
41
     */
42
    private static $order = [
43
        SOA::TYPE,
44
        NS::TYPE,
45
        A::TYPE,
46
        AAAA::TYPE,
47
        CNAME::TYPE,
48
        DNAME::TYPE,
49
        MX::TYPE,
50
        LOC::TYPE,
51
        HINFO::TYPE,
52
        TXT::TYPE,
53
        PTR::TYPE,
54
        SRV::TYPE,
55
    ];
56
57
    /**
58
     * @param Zone $zone
59
     *
60
     * @return string
61
     */
62 1
    public static function build(Zone $zone): string
63
    {
64 1
        $master = '$ORIGIN '.$zone->getName().PHP_EOL;
65 1
        if (null !== $zone->getDefaultTtl()) {
66 1
            $master .= '$TTL '.$zone->getDefaultTtl().PHP_EOL;
67
        }
68
69 1
        $rrs = $zone->getResourceRecords();
70 1
        $current = SOA::TYPE;
71 1
        usort($rrs, 'self::compareResourceRecords');
72
73 1
        list($namePadding, $ttlPadding, $typePadding, $rdataPadding) = self::getPadding($zone);
74
75 1
        foreach ($rrs as $resourceRecord) {
76 1
            if (null == $resourceRecord->getRdata()) {
77 1
                continue;
78
            }
79
80 1
            if ($resourceRecord->getType() !== $current) {
81 1
                $master .= PHP_EOL.self::COMMENT_DELIMINATOR.$resourceRecord->getType().' RECORDS'.PHP_EOL;
82 1
                $current = $resourceRecord->getType();
83
            }
84
85 1
            $master .= sprintf('%s %s %s %s %s',
86 1
                str_pad($resourceRecord->getName(), $namePadding, ' ', STR_PAD_RIGHT),
87 1
                str_pad($resourceRecord->getTtl(), $ttlPadding, ' ', STR_PAD_RIGHT),
88 1
                str_pad($resourceRecord->getClass(), 2, ' ', STR_PAD_RIGHT),
89 1
                str_pad($resourceRecord->getType(), $typePadding, ' ', STR_PAD_RIGHT),
90 1
                self::generateRdataOutput($resourceRecord->getRdata(), $rdataPadding)
91
            );
92
93 1
            if (null != $resourceRecord->getComment()) {
94 1
                $master .= self::COMMENT_DELIMINATOR.$resourceRecord->getComment();
95
            }
96
97 1
            $master .= PHP_EOL;
98
        }
99
100 1
        return $master;
101
    }
102
103
    /**
104
     * Compares two ResourceRecords to determine which is the higher order. Used with the usort() function.
105
     *
106
     * @param ResourceRecord $a
107
     * @param ResourceRecord $b
108
     *
109
     * @return int
110
     */
111 2
    public static function compareResourceRecords(ResourceRecord $a, ResourceRecord $b): int
112
    {
113 2
        if ($a->getType() === $b->getType()) {
114 2
            return strcmp($a->getName().$a->getRdata()->output(), $b->getName().$b->getRdata()->output());
115
        }
116
117 2
        $_a = array_search($a->getType(), self::$order);
118 2
        $_b = array_search($b->getType(), self::$order);
119
120 2
        if (false !== $_a && false !== $_b) {
121 2
            return $_a - $_b;
122
        }
123
124 2
        if (false === $_a) {
125 1
            return 1;
126
        }
127
128 2
        return -1;
129
    }
130
131
    /**
132
     * @param RdataInterface $rdata
133
     * @param int            $padding
134
     *
135
     * @return string
136
     */
137 1
    private static function generateRdataOutput(RdataInterface $rdata, int $padding): string
138
    {
139 1
        if ($rdata instanceof SOA) {
140 1
            return self::outputSoa($rdata, $padding);
141
        }
142
143 1
        if ($rdata instanceof APL) {
144
            return self::outputApl($rdata, $padding);
145
        }
146
147 1
        if ($rdata instanceof LOC) {
148 1
            return self::outputLoc($rdata, $padding);
149
        }
150
151 1
        return $rdata->output();
152
    }
153
154
    /**
155
     * @param SOA $rdata
156
     * @param int $padding
157
     *
158
     * @return string
159
     */
160 1
    private static function outputSoa(SOA $rdata, int $padding): string
161
    {
162
        $vars = [
163 1
            $rdata->getMname(),
164 1
            $rdata->getRname(),
165 1
            $rdata->getSerial(),
166 1
            $rdata->getRefresh(),
167 1
            $rdata->getRetry(),
168 1
            $rdata->getExpire(),
169 1
            $rdata->getMinimum(),
170
        ];
171
172 1
        $longestVarLength = max(array_map('strlen', $vars));
173
174 1
        return self::MULTILINE_BEGIN.PHP_EOL.
175 1
        self::makeLine($rdata->getMname(), 'MNAME', $longestVarLength, $padding).
176 1
        self::makeLine($rdata->getRname(), 'RNAME', $longestVarLength, $padding).
177 1
        self::makeLine($rdata->getSerial(), 'SERIAL', $longestVarLength, $padding).
178 1
        self::makeLine($rdata->getRefresh(), 'REFRESH', $longestVarLength, $padding).
179 1
        self::makeLine($rdata->getRetry(), 'RETRY', $longestVarLength, $padding).
180 1
        self::makeLine($rdata->getExpire(), 'EXPIRE', $longestVarLength, $padding).
181 1
        self::makeLine($rdata->getMinimum(), 'MINIMUM', $longestVarLength, $padding).
182 1
        str_repeat(' ', $padding).self::MULTILINE_END;
183
    }
184
185
    /**
186
     * @param APL $rdata
187
     * @param int $padding
188
     *
189
     * @return string
190
     */
191
    private static function outputApl(APL $rdata, int $padding): string
192
    {
193
        $blocks = explode(' ', $rdata->output());
194
        $longestVarLength = max(array_map('strlen', $blocks));
195
        $string = self::MULTILINE_BEGIN.PHP_EOL;
196
197
        foreach ($blocks as $block) {
198
            $string .= self::makeLine($block, null, $longestVarLength, $padding);
199
        }
200
201
        return $string.str_repeat(' ', $padding).self::MULTILINE_END;
202
    }
203
204
    /**
205
     * @param LOC $rdata
206
     * @param int $padding
207
     *
208
     * @return string
209
     */
210 1
    private static function outputLoc(LOC $rdata, int $padding): string
211
    {
212
        $parts = [
213 1
            $rdata->getLatitude(LOC::FORMAT_DMS),
214 1
            $rdata->getLongitude(LOC::FORMAT_DMS),
215 1
            sprintf('%.2fm', $rdata->getAltitude()),
216 1
            sprintf('%.2fm', $rdata->getSize()),
217 1
            sprintf('%.2fm', $rdata->getHorizontalPrecision()),
218 1
            sprintf('%.2fm', $rdata->getVerticalPrecision()),
219
        ];
220
221 1
        $longestVarLength = max(array_map('strlen', $parts));
222
223 1
        return self::MULTILINE_BEGIN.PHP_EOL.
224 1
            self::makeLine($rdata->getLatitude(LOC::FORMAT_DMS), 'LATITUDE', $longestVarLength, $padding).
225 1
            self::makeLine($rdata->getLongitude(LOC::FORMAT_DMS), 'LONGITUDE', $longestVarLength, $padding).
226 1
            self::makeLine(sprintf('%.2fm', $rdata->getAltitude()), 'ALTITUDE', $longestVarLength, $padding).
227 1
            self::makeLine(sprintf('%.2fm', $rdata->getSize()), 'SIZE', $longestVarLength, $padding).
228 1
            self::makeLine(sprintf('%.2fm', $rdata->getHorizontalPrecision()), 'HORIZONTAL PRECISION', $longestVarLength, $padding).
229 1
            self::makeLine(sprintf('%.2fm', $rdata->getVerticalPrecision()), 'VERTICAL PRECISION', $longestVarLength, $padding).
230 1
            str_repeat(' ', $padding).self::MULTILINE_END;
231
    }
232
233
    /**
234
     * Returns a padded line with comment.
235
     *
236
     * @param string $text
237
     * @param string $comment
238
     * @param int    $longestVarLength
239
     * @param int    $padding
240
     *
241
     * @return string
242
     */
243 1
    private static function makeLine(string $text, ?string $comment, int $longestVarLength, int $padding): string
244
    {
245 1
        $output = str_repeat(' ', $padding).str_pad($text, $longestVarLength);
246
247 1
        if (null !== $comment) {
248 1
            $output .= ' '.self::COMMENT_DELIMINATOR.$comment;
249
        }
250
251 1
        return $output.PHP_EOL;
252
    }
253
254
    /**
255
     * Get the padding required for a zone.
256
     *
257
     * @param Zone $zone
258
     *
259
     * @return array Array order: name, ttl, type, rdata
260
     */
261 1
    private static function getPadding(Zone $zone)
262
    {
263 1
        $name = $ttl = $type = 0;
264
265 1
        foreach ($zone as $resourceRecord) {
266 1
            $name = max($name, strlen($resourceRecord->getName()));
267 1
            $ttl = max($ttl, strlen($resourceRecord->getTtl()));
268 1
            $type = max($type, strlen($resourceRecord->getType()));
269
        }
270
271
        return [
272 1
            $name,
273 1
            $ttl,
274 1
            $type,
275 1
            $name + $ttl + $type + 6,
276
        ];
277
    }
278
}
279