Completed
Push — master ( f48d1d...faf583 )
by Sam
01:47
created

Toolbox   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 97.92%

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 1
dl 0
loc 151
ccs 47
cts 48
cp 0.9792
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A expandIpv6() 0 10 2
A expandIncompleteIpv6() 0 8 1
B contractIpv6() 0 48 11
A reverseIpv4() 0 6 1
A reverseIpv6() 0 15 3
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\Ip;
13
14
use Badcow\DNS\Validator;
15
16
class Toolbox
17
{
18
    /**
19
     * Expands an IPv6 address to its full, non-shorthand representation.
20
     *
21
     * E.g. 2001:db8:9a::42 -> 2001:0db8:009a:0000:0000:0000:0000:0042
22
     *
23
     * @param string $ip IPv6 address
24
     *
25
     * @throws \InvalidArgumentException
26
     *
27
     * @return string
28
     */
29 5
    public static function expandIpv6(string $ip): string
30
    {
31 5
        if (!Validator::ipv6($ip)) {
32 2
            throw new \InvalidArgumentException(sprintf('"%s" is not a valid IPv6 address.', $ip));
33
        }
34
35 4
        $hex = unpack('H*hex', inet_pton($ip));
36
37 4
        return substr(preg_replace('/([A-f0-9]{4})/', '$1:', $hex['hex']), 0, -1);
38
    }
39
40
    /**
41
     * This function will expand in incomplete IPv6 address.
42
     * An incomplete IPv6 address is of the form `2001:db8:ff:abcd`
43
     * i.e. one where there is less than eight hextets.
44
     *
45
     * @param string $ip IPv6 address
46
     *
47
     * @return string Expanded incomplete IPv6 address
48
     */
49 2
    public static function expandIncompleteIpv6(string $ip): string
50
    {
51
        $hextets = array_map(function ($hextet) {
52 2
            return str_pad($hextet, 4, '0', STR_PAD_LEFT);
53 2
        }, explode(':', $ip));
54
55 2
        return implode(':', $hextets);
56
    }
57
58
    /**
59
     * Takes a valid IPv6 address and contracts it
60
     * to its shorter version.
61
     *
62
     * E.g.: 2001:0000:0000:acad:0000:0000:0000:0001 -> 2001:0:0:acad::1
63
     *
64
     * Note: If there is more than one set of consecutive hextets, the function
65
     * will favour the larger of the sets. If both sets of zeroes are the same
66
     * the second will be favoured in the omission of zeroes.
67
     *
68
     * E.g.: 2001:0000:0000:ab80:2390:0000:0000:000a -> 2001:0:0:ab80:2390::a
69
     *
70
     * @param string $ip IPv6 address
71
     *
72
     * @throws \InvalidArgumentException
73
     *
74
     * @return string Contracted IPv6 address
75
     */
76 1
    public static function contractIpv6(string $ip): string
77
    {
78 1
        if (!Validator::ipv6($ip)) {
79
            throw new \InvalidArgumentException(sprintf('"%s" is not a valid IPv6 address.', $ip));
80
        }
81
82 1
        $ip = self::expandIpv6($ip);
83 1
        $decimals = array_map('hexdec', explode(':', $ip));
84
85
        //Find the largest streak of zeroes
86 1
        $streak = $longestStreak = 0;
87 1
        $streak_i = $longestStreak_i = -1;
88
89 1
        foreach ($decimals as $i => $decimal) {
90 1
            if (0 !== $decimal) {
91 1
                $streak_i = -1;
92 1
                $streak = 0;
93
94 1
                continue;
95
            }
96
97 1
            $streak_i = (-1 === $streak_i) ? $i : $streak_i;
98 1
            ++$streak;
99
100 1
            if ($streak >= $longestStreak) {
101 1
                $longestStreak = $streak;
102 1
                $longestStreak_i = $streak_i;
103
            }
104
        }
105
106 1
        $ip = '';
107
108 1
        foreach ($decimals as $i => $decimal) {
109 1
            if ($i > $longestStreak_i && $i < $longestStreak_i + $longestStreak) {
110 1
                continue;
111
            }
112
113 1
            if ($i === $longestStreak_i) {
114 1
                $ip .= '::';
115 1
                continue;
116
            }
117
118 1
            $ip .= (string) dechex($decimal);
119 1
            $ip .= ($i < 7) ? ':' : '';
120
        }
121
122 1
        return preg_replace('/\:{3}/', '::', $ip);
123
    }
124
125
    /**
126
     * Creates a reverse IPv4 address.
127
     *
128
     * E.g. 192.168.1.213 -> 213.1.168.192.in-addr.arpa.
129
     *
130
     * @param string $ip Valid IPv4 address
131
     *
132
     * @return string Reversed IP address appended with ".in-addr.arpa."
133
     */
134 2
    public static function reverseIpv4(string $ip): string
135
    {
136 2
        $octets = array_reverse(explode('.', $ip));
137
138 2
        return implode('.', $octets).'.in-addr.arpa.';
139
    }
140
141
    /**
142
     * Creates a reverse IPv6 address.
143
     *
144
     * E.g. 2001:db8::567:89ab -> b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
145
     *
146
     * @param string $ip           A full or partial IPv6 address
147
     * @param bool   $appendSuffix Whether or not to append ".ip6.arpa.' suffix.
148
     *
149
     * @return string The reversed address appended with ".ip6.arpa."
150
     */
151 2
    public static function reverseIpv6(string $ip, bool $appendSuffix = true): string
152
    {
153
        try {
154 2
            $ip = self::expandIpv6($ip);
155 2
        } catch (\InvalidArgumentException $e) {
156 2
            $ip = self::expandIncompleteIpv6($ip);
157
        }
158
159 2
        $ip = str_replace(':', '', $ip);
160 2
        $ip = strrev($ip);
161 2
        $ip = implode('.', str_split($ip));
162 2
        $ip .= $appendSuffix ? '.ip6.arpa.' : '';
163
164 2
        return $ip;
165
    }
166
}
167