Completed
Branch v2 (ec0e33)
by Sam
02:09
created

AlignedBuilder::outputLoc()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 9.568
c 0
b 0
f 0
cc 1
nc 1
nop 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\AAAA;
15
use Badcow\DNS\Rdata\A;
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\RdataInterface;
25
use Badcow\DNS\Rdata\SOA;
26
use Badcow\DNS\Rdata\SRV;
27
use Badcow\DNS\Rdata\TXT;
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
    public static function build(Zone $zone): string
63
    {
64
        $master = '$ORIGIN '.$zone->getName().PHP_EOL.
65
            '$TTL '.$zone->getDefaultTtl().PHP_EOL;
66
67
        $rrs = $zone->getResourceRecords();
68
        $current = SOA::TYPE;
69
        $namePadding = $ttlPadding = $typePadding = 0;
70
        usort($rrs, 'self::compareResourceRecords');
71
72
        foreach ($zone as $resourceRecord) {
73
            $namePadding = (strlen($resourceRecord->getName()) > $namePadding) ? strlen($resourceRecord->getName()) : $namePadding;
74
            $ttlPadding = (strlen($resourceRecord->getTtl()) > $ttlPadding) ? strlen($resourceRecord->getTtl()) : $ttlPadding;
75
            $typePadding = (strlen($resourceRecord->getType()) > $typePadding) ? strlen($resourceRecord->getType()) : $typePadding;
76
        }
77
78
        foreach ($rrs as $resourceRecord) {
79
            if (null == $resourceRecord->getRdata()) {
80
                continue;
81
            }
82
83
            if ($resourceRecord->getType() !== $current) {
84
                $master .= PHP_EOL.self::COMMENT_DELIMINATOR.$resourceRecord->getType().' RECORDS'.PHP_EOL;
85
                $current = $resourceRecord->getType();
86
            }
87
88
            $rdataPadding = $namePadding + $ttlPadding + $typePadding + 6;
89
90
            $master .= sprintf('%s %s %s %s %s',
91
                str_pad($resourceRecord->getName(), $namePadding, ' ', STR_PAD_RIGHT),
92
                str_pad($resourceRecord->getTtl(), $ttlPadding, ' ', STR_PAD_RIGHT),
93
                str_pad($resourceRecord->getClass(), 2, ' ', STR_PAD_RIGHT),
94
                str_pad($resourceRecord->getType(), $typePadding, ' ', STR_PAD_RIGHT),
95
                self::generateRdataOutput($resourceRecord->getRdata(), $rdataPadding)
96
            );
97
98
            if (null != $resourceRecord->getComment()) {
99
                $master .= self::COMMENT_DELIMINATOR.$resourceRecord->getComment();
100
            }
101
102
            $master .= PHP_EOL;
103
        }
104
105
        return $master;
106
    }
107
108
    /**
109
     * Compares two ResourceRecords to determine which is the higher order. Used with the usort() function.
110
     *
111
     * @param ResourceRecord $a
112
     * @param ResourceRecord $b
113
     *
114
     * @return int
115
     */
116
    public static function compareResourceRecords(ResourceRecord $a, ResourceRecord $b): int
117
    {
118
        if ($a->getType() === $b->getType()) {
119
            return strcmp($a->getName().$a->getRdata()->output(), $b->getName().$b->getRdata()->output());
120
        }
121
122
        $_a = array_search($a->getType(), self::$order);
123
        $_b = array_search($b->getType(), self::$order);
124
125
        if (false !== $_a && false !== $_b) {
126
            return $_a - $_b;
127
        }
128
129
        if (false === $_a) {
130
            return 1;
131
        }
132
133
        return -1;
134
    }
135
136
    /**
137
     * @param RdataInterface $rdata
138
     * @param int            $padding
139
     *
140
     * @return string
141
     */
142
    private static function generateRdataOutput(RdataInterface $rdata, int $padding): string
143
    {
144
        if ($rdata instanceof SOA) {
145
            return self::outputSoa($rdata, $padding);
146
        }
147
148
        if ($rdata instanceof APL) {
149
            return self::outputApl($rdata, $padding);
150
        }
151
152
        if ($rdata instanceof LOC) {
153
            return self::outputLoc($rdata, $padding);
154
        }
155
156
        return $rdata->output();
157
    }
158
159
    /**
160
     * @param SOA $rdata
161
     * @param int $padding
162
     *
163
     * @return string
164
     */
165
    private static function outputSoa(SOA $rdata, int $padding): string
166
    {
167
        $vars = [
168
            $rdata->getMname(),
169
            $rdata->getRname(),
170
            $rdata->getSerial(),
171
            $rdata->getRefresh(),
172
            $rdata->getRetry(),
173
            $rdata->getExpire(),
174
            $rdata->getMinimum(),
175
        ];
176
177
        $longestVarLength = max(array_map('strlen', $vars));
178
179
        return self::MULTILINE_BEGIN.PHP_EOL.
180
        self::makeLine($rdata->getMname(), 'MNAME', $longestVarLength, $padding).
181
        self::makeLine($rdata->getRname(), 'RNAME', $longestVarLength, $padding).
182
        self::makeLine($rdata->getSerial(), 'SERIAL', $longestVarLength, $padding).
183
        self::makeLine($rdata->getRefresh(), 'REFRESH', $longestVarLength, $padding).
184
        self::makeLine($rdata->getRetry(), 'RETRY', $longestVarLength, $padding).
185
        self::makeLine($rdata->getExpire(), 'EXPIRE', $longestVarLength, $padding).
186
        self::makeLine($rdata->getMinimum(), 'MINIMUM', $longestVarLength, $padding).
187
        str_repeat(' ', $padding).self::MULTILINE_END;
188
    }
189
190
    /**
191
     * @param APL $rdata
192
     * @param int $padding
193
     *
194
     * @return string
195
     */
196
    private static function outputApl(APL $rdata, int $padding): string
197
    {
198
        $blocks = explode(' ', $rdata->output());
199
        $longestVarLength = max(array_map('strlen', $blocks));
200
        $string = self::MULTILINE_BEGIN.PHP_EOL;
201
202
        foreach ($blocks as $block) {
203
            $string .= self::makeLine($block, null, $longestVarLength, $padding);
204
        }
205
206
        return $string.str_repeat(' ', $padding).self::MULTILINE_END;
207
    }
208
209
    /**
210
     * @param LOC $rdata
211
     * @param int $padding
212
     *
213
     * @return string
214
     */
215
    private static function outputLoc(LOC $rdata, int $padding): string
216
    {
217
        $parts = [
218
            $rdata->getLatitude(LOC::FORMAT_DMS),
219
            $rdata->getLongitude(LOC::FORMAT_DMS),
220
            sprintf('%.2fm', $rdata->getAltitude()),
221
            sprintf('%.2fm', $rdata->getSize()),
222
            sprintf('%.2fm', $rdata->getHorizontalPrecision()),
223
            sprintf('%.2fm', $rdata->getVerticalPrecision()),
224
        ];
225
226
        $longestVarLength = max(array_map('strlen', $parts));
227
228
        return self::MULTILINE_BEGIN.PHP_EOL.
229
            self::makeLine($rdata->getLatitude(LOC::FORMAT_DMS), 'LATITUDE', $longestVarLength, $padding).
230
            self::makeLine($rdata->getLongitude(LOC::FORMAT_DMS), 'LONGITUDE', $longestVarLength, $padding).
231
            self::makeLine(sprintf('%.2fm', $rdata->getAltitude()), 'ALTITUDE', $longestVarLength, $padding).
232
            self::makeLine(sprintf('%.2fm', $rdata->getSize()), 'SIZE', $longestVarLength, $padding).
233
            self::makeLine(sprintf('%.2fm', $rdata->getHorizontalPrecision()), 'HORIZONTAL PRECISION', $longestVarLength, $padding).
234
            self::makeLine(sprintf('%.2fm', $rdata->getVerticalPrecision()), 'VERTICAL PRECISION', $longestVarLength, $padding).
235
            str_repeat(' ', $padding).self::MULTILINE_END;
236
    }
237
238
    /**
239
     * Returns a padded line with comment.
240
     *
241
     * @param string $text
242
     * @param string $comment
243
     * @param int    $longestVarLength
244
     * @param int    $padding
245
     *
246
     * @return string
247
     */
248
    private static function makeLine(string $text, string $comment, int $longestVarLength, int $padding): string
249
    {
250
        $output = str_repeat(' ', $padding).str_pad($text, $longestVarLength);
251
252
        if (null !== $comment) {
253
            $output .= ' '.self::COMMENT_DELIMINATOR.$comment;
254
        }
255
256
        return $output.PHP_EOL;
257
    }
258
}
259