Completed
Push — remove-intl-polyfills ( 129df4...a6e727 )
by Alexander
16:22 queued 12:49
created

BaseIpHelper   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 11
lcom 0
cbo 0
dl 0
loc 104
rs 10
c 0
b 0
f 0
ccs 28
cts 28
cp 1

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getIpVersion() 0 4 2
A expandIPv6() 0 5 1
A ip2bin() 0 15 4
A inRange() 0 19 4
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\helpers;
9
10
/**
11
 * Class BaseIpHelper provides concrete implementation for [[IpHelper]]
12
 *
13
 * Do not use BaseIpHelper, use [[IpHelper]] instead.
14
 *
15
 * @author Dmytro Naumenko <[email protected]>
16
 * @since 2.0.14
17
 */
18
class BaseIpHelper
19
{
20
    const IPV4 = 4;
21
    const IPV6 = 6;
22
    /**
23
     * The length of IPv6 address in bits
24
     */
25
    const IPV6_ADDRESS_LENGTH = 128;
26
    /**
27
     * The length of IPv4 address in bits
28
     */
29
    const IPV4_ADDRESS_LENGTH = 32;
30
31
32
    /**
33
     * Gets the IP version. Does not perform IP address validation.
34
     *
35
     * @param string $ip the valid IPv4 or IPv6 address.
36
     * @return int [[IPV4]] or [[IPV6]]
37
     */
38 41
    public static function getIpVersion($ip)
39
    {
40 41
        return strpos($ip, ':') === false ? self::IPV4 : self::IPV6;
41
    }
42
43
    /**
44
     * Checks whether IP address or subnet $subnet is contained by $subnet.
45
     *
46
     * For example, the following code checks whether subnet `192.168.1.0/24` is in subnet `192.168.0.0/22`:
47
     *
48
     * ```php
49
     * IpHelper::inRange('192.168.1.0/24', '192.168.0.0/22'); // true
50
     * ```
51
     *
52
     * In case you need to check whether a single IP address `192.168.1.21` is in the subnet `192.168.1.0/24`,
53
     * you can use any of theses examples:
54
     *
55
     * ```php
56
     * IpHelper::inRange('192.168.1.21', '192.168.1.0/24'); // true
57
     * IpHelper::inRange('192.168.1.21/32', '192.168.1.0/24'); // true
58
     * ```
59
     *
60
     * @param string $subnet the valid IPv4 or IPv6 address or CIDR range, e.g.: `10.0.0.0/8` or `2001:af::/64`
61
     * @param string $range the valid IPv4 or IPv6 CIDR range, e.g. `10.0.0.0/8` or `2001:af::/64`
62
     * @return bool whether $subnet is contained by $range
63
     *
64
     * @see https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
65
     */
66 23
    public static function inRange($subnet, $range)
67
    {
68 23
        [$ip, $mask] = array_pad(explode('/', $subnet), 2, null);
0 ignored issues
show
Bug introduced by
The variable $ip does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $mask seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
69 23
        [$net, $netMask] = array_pad(explode('/', $range), 2, null);
0 ignored issues
show
Bug introduced by
The variable $net does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $netMask seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
70
71 23
        $ipVersion = static::getIpVersion($ip);
72 23
        $netVersion = static::getIpVersion($net);
73 23
        if ($ipVersion !== $netVersion) {
74 2
            return false;
75
        }
76
77 23
        $maxMask = $ipVersion === self::IPV4 ? self::IPV4_ADDRESS_LENGTH : self::IPV6_ADDRESS_LENGTH;
78 23
        $mask = $mask ?? $maxMask;
79 23
        $netMask = $netMask ?? $maxMask;
80
81 23
        $binIp = static::ip2bin($ip);
82 23
        $binNet = static::ip2bin($net);
83 23
        return substr($binIp, 0, $netMask) === substr($binNet, 0, $netMask) && $mask >= $netMask;
84
    }
85
86
    /**
87
     * Expands an IPv6 address to it's full notation.
88
     *
89
     * For example `2001:db8::1` will be expanded to `2001:0db8:0000:0000:0000:0000:0000:0001`
90
     *
91
     * @param string $ip the original valid IPv6 address
92
     * @return string the expanded IPv6 address
93
     */
94 4
    public static function expandIPv6($ip)
95
    {
96 4
        $hex = unpack('H*hex', inet_pton($ip));
97 3
        return substr(preg_replace('/([a-f0-9]{4})/i', '$1:', $hex['hex']), 0, -1);
98
    }
99
100
    /**
101
     * Converts IP address to bits representation.
102
     *
103
     * @param string $ip the valid IPv4 or IPv6 address
104
     * @return string bits as a string
105
     */
106 27
    public static function ip2bin($ip)
107
    {
108 27
        if (static::getIpVersion($ip) === self::IPV4) {
109 21
            return str_pad(base_convert(ip2long($ip), 10, 2), self::IPV4_ADDRESS_LENGTH, '0', STR_PAD_LEFT);
110
        }
111
112 8
        $unpack = unpack('A16', inet_pton($ip));
113 8
        $binStr = array_shift($unpack);
114 8
        $bytes = self::IPV6_ADDRESS_LENGTH / 8; // 128 bit / 8 = 16 bytes
115 8
        $result = '';
116 8
        while ($bytes-- > 0) {
117 8
            $result = sprintf('%08b', isset($binStr[$bytes]) ? ord($binStr[$bytes]) : '0') . $result;
118
        }
119 8
        return $result;
120
    }
121
}
122