Completed
Push — master ( 2c4ff8...24d0cf )
by Sam
01:41
created

Toolbox   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 97.92%

Importance

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

6 Methods

Rating   Name   Duplication   Size   Complexity  
A expandIpv6() 0 10 2
A expandIncompleteIpv6() 0 8 1
B contractIpv6() 0 32 6
A reverseIpv4() 0 6 1
A reverseIpv6() 0 15 3
A hextetsToIp() 0 12 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\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
        $ip = self::expandIpv6($ip);
79
        $hextets = array_map('hexdec', explode(':', $ip));
80
81
        //Find the largest streak of zeroes
82 1
        $streak = $longestStreak = 0;
83 1
        $streak_i = $longestStreak_i = -1;
84
85
        foreach ($hextets as $i => $hextet) {
86 1
            if (0 !== $hextet) {
87 1
                $streak_i = -1;
88
                $streak = 0;
89 1
90 1
                continue;
91 1
            }
92 1
93
            $streak_i = (-1 === $streak_i) ? $i : $streak_i;
94 1
            ++$streak;
95
96
            if ($streak >= $longestStreak) {
97 1
                $longestStreak = $streak;
98 1
                $longestStreak_i = $streak_i;
99
            }
100 1
        }
101 1
102 1
        if (0 < $longestStreak) {
103
            array_splice($hextets, $longestStreak_i, $longestStreak, '::');
104
        }
105
106 1
        return self::hextetsToIp($hextets);
107
    }
108 1
109 1
    /**
110 1
     * Creates a reverse IPv4 address.
111
     *
112
     * E.g. 192.168.1.213 -> 213.1.168.192.in-addr.arpa.
113 1
     *
114 1
     * @param string $ip Valid IPv4 address
115 1
     *
116
     * @return string Reversed IP address appended with ".in-addr.arpa."
117
     */
118 1
    public static function reverseIpv4(string $ip): string
119 1
    {
120
        $octets = array_reverse(explode('.', $ip));
121
122 1
        return implode('.', $octets).'.in-addr.arpa.';
123
    }
124
125
    /**
126
     * Creates a reverse IPv6 address.
127
     *
128
     * 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.
129
     *
130
     * @param string $ip           A full or partial IPv6 address
131
     * @param bool   $appendSuffix Whether or not to append ".ip6.arpa.' suffix.
132
     *
133
     * @return string The reversed address appended with ".ip6.arpa."
134 2
     */
135
    public static function reverseIpv6(string $ip, bool $appendSuffix = true): string
136 2
    {
137
        try {
138 2
            $ip = self::expandIpv6($ip);
139
        } catch (\InvalidArgumentException $e) {
140
            $ip = self::expandIncompleteIpv6($ip);
141
        }
142
143
        $ip = str_replace(':', '', $ip);
144
        $ip = strrev($ip);
145
        $ip = implode('.', str_split($ip));
146
        $ip .= $appendSuffix ? '.ip6.arpa.' : '';
147
148
        return $ip;
149
    }
150
151 2
    /**
152
     * @param array $hextets
153
     *
154 2
     * @return string Contracted IPv6 address.
155 2
     */
156 2
    private static function hextetsToIp(array $hextets): string
157
    {
158
        $hextets = array_map(function($hex) {
159 2
            if ('::' === $hex) {
160 2
                return '::';
161 2
            }
162 2
163
            return (string) dechex($hex);
164 2
        }, $hextets);
165
166
        return preg_replace('/\:{2,}/', '::', implode(':', $hextets), 1);
167
    }
168
}
169