cs-eliseev /
helpers-ip
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | namespace cse\helpers; |
||
| 6 | |||
| 7 | /** |
||
| 8 | * Class IP |
||
| 9 | * |
||
| 10 | * @package cse\helpers |
||
| 11 | */ |
||
| 12 | class IP |
||
| 13 | { |
||
| 14 | const IP_VERSION_4 = 4; |
||
| 15 | const IP_VERSION_6 = 6; |
||
| 16 | |||
| 17 | /** |
||
| 18 | * Get real IP |
||
| 19 | * |
||
| 20 | * @return null|string |
||
| 21 | */ |
||
| 22 | public static function getRealIP(): ?string |
||
| 23 | { |
||
| 24 | if (!empty($_SERVER['HTTP_X_REAL_IP'])) { |
||
| 25 | // Check ip from share internet |
||
| 26 | $ip = $_SERVER['HTTP_X_REAL_IP']; |
||
| 27 | } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) { |
||
| 28 | // Check ip from share internet |
||
| 29 | $ip = $_SERVER['HTTP_CLIENT_IP']; |
||
| 30 | } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { |
||
| 31 | // To check ip is pass from proxy |
||
| 32 | $ips = explode(',', str_replace(' ', '', $_SERVER['HTTP_X_FORWARDED_FOR'])); |
||
| 33 | $ips = array_filter($ips); |
||
| 34 | if (count($ips)) $ip = array_pop($ips); |
||
| 35 | } |
||
| 36 | |||
| 37 | return empty($ip) ? ($_SERVER['REMOTE_ADDR'] ?? null) : $ip; |
||
| 38 | } |
||
| 39 | |||
| 40 | /** |
||
| 41 | * Remove subnet mask to IPv6 |
||
| 42 | * |
||
| 43 | * @param string $ip |
||
| 44 | * |
||
| 45 | * @return null|string |
||
| 46 | */ |
||
| 47 | public static function removeSubnetMaskIPv6(string $ip): ?string |
||
| 48 | { |
||
| 49 | return preg_replace('/^(.*)(\/[\d]*)$/', '${1}', $ip); |
||
| 50 | } |
||
| 51 | |||
| 52 | /** |
||
| 53 | * Check is IPv4 address |
||
| 54 | * |
||
| 55 | * @param string $ip |
||
| 56 | * |
||
| 57 | * @return bool |
||
| 58 | */ |
||
| 59 | public static function isIPv4(string $ip): bool |
||
| 60 | { |
||
| 61 | return (bool) filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); |
||
| 62 | } |
||
| 63 | |||
| 64 | /** |
||
| 65 | * Check is IPv4 address |
||
| 66 | * |
||
| 67 | * @param string $ip |
||
| 68 | * |
||
| 69 | * @return bool |
||
| 70 | */ |
||
| 71 | public static function isIPv6(string $ip): bool |
||
| 72 | { |
||
| 73 | return (bool) filter_var(self::removeSubnetMaskIPv6($ip), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); |
||
| 74 | } |
||
| 75 | |||
| 76 | /** |
||
| 77 | * Get version IP address |
||
| 78 | * |
||
| 79 | * @param string $ip |
||
| 80 | * |
||
| 81 | * @return int|null |
||
| 82 | */ |
||
| 83 | public static function getVersionIP(string $ip): ?int |
||
| 84 | { |
||
| 85 | return self::isIPv4($ip) ? self::IP_VERSION_4 : (self::isIPv6($ip) ? self::IP_VERSION_6 : null); |
||
| 86 | } |
||
| 87 | |||
| 88 | /** |
||
| 89 | * Is IP address |
||
| 90 | * |
||
| 91 | * @param string $ip |
||
| 92 | * |
||
| 93 | * @return bool |
||
| 94 | */ |
||
| 95 | public static function isIP(string $ip): bool |
||
| 96 | { |
||
| 97 | return is_int(self::getVersionIP($ip)); |
||
| 98 | } |
||
| 99 | |||
| 100 | /** |
||
| 101 | * Get range IPv6 address |
||
| 102 | * |
||
| 103 | * @example 2a0a:2b40::4:60/124 => [2a0a:2b40::4:60, 2a0a:2b40::4:6f] |
||
| 104 | * |
||
| 105 | * @param $ip |
||
| 106 | * |
||
| 107 | * @return array |
||
| 108 | */ |
||
| 109 | public static function getRangeIPv6(string $ip): array |
||
| 110 | { |
||
| 111 | list($first_addr_str, $prefix_len) = explode('/', $ip); |
||
| 112 | |||
| 113 | // Parse the address into a binary string |
||
| 114 | $first_addr_bin = inet_pton($first_addr_str); |
||
| 115 | // Convert the binary string to a string with hexadecimal characters |
||
| 116 | $addr_hex = unpack('H*', $first_addr_bin); |
||
| 117 | $first_addr_hex = reset($addr_hex); |
||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 118 | // Overwriting first address string to make sure notation is optimal |
||
| 119 | $first_addr_str = inet_ntop($first_addr_bin); |
||
| 120 | // Calculate the number of 'flexible' bits |
||
| 121 | $flex_bits = 128 - $prefix_len; |
||
| 122 | // Build the hexadecimal string of the last address |
||
| 123 | $last_addr_hex = $first_addr_hex; |
||
| 124 | |||
| 125 | // We start at the end of the string (which is always 32 characters long) |
||
| 126 | $pos = 31; |
||
| 127 | while ($flex_bits > 0) { |
||
| 128 | // Get the character at this position |
||
| 129 | $orig = substr($last_addr_hex, $pos, 1); |
||
| 130 | // Convert it to an integer |
||
| 131 | $origval = hexdec($orig); |
||
| 132 | // OR it with (2^flexbits)-1, with flexbits limited to 4 at a time |
||
| 133 | $new_val = $origval | (pow(2, min(4, $flex_bits)) - 1); |
||
| 134 | // Convert it back to a hexadecimal character |
||
| 135 | $new = dechex($new_val); |
||
| 136 | // And put that character back in the string |
||
| 137 | $last_addr_hex = substr_replace($last_addr_hex, $new, $pos, 1); |
||
| 138 | // We processed one nibble, move to previous position |
||
| 139 | $flex_bits -= 4; |
||
| 140 | $pos -= 1; |
||
| 141 | } |
||
| 142 | |||
| 143 | // Convert the hexadecimal string to a binary string |
||
| 144 | $last_addr_bin = pack('H*', $last_addr_hex); |
||
| 145 | // And create an IPv6 address from the binary string |
||
| 146 | $last_addr_str = inet_ntop($last_addr_bin); |
||
| 147 | |||
| 148 | return [ |
||
| 149 | $first_addr_str, |
||
| 150 | $last_addr_str, |
||
| 151 | ]; |
||
| 152 | } |
||
| 153 | |||
| 154 | /** |
||
| 155 | * Filter IPs address |
||
| 156 | * |
||
| 157 | * @param array $ips |
||
| 158 | * @param int|null $version |
||
| 159 | * |
||
| 160 | * @return array |
||
| 161 | */ |
||
| 162 | public static function filterIPs(array $ips, ?int $version = null): array |
||
| 163 | { |
||
| 164 | $result = [self::IP_VERSION_4 => [], self::IP_VERSION_6 => []]; |
||
| 165 | |||
| 166 | foreach ($ips as $ip) { |
||
| 167 | $current_version = self::getVersionIP($ip); |
||
| 168 | if (is_int($current_version)) $result[$current_version][] = $ip; |
||
| 169 | } |
||
| 170 | |||
| 171 | return is_int($version) ? $result[$version] : $result; |
||
| 172 | } |
||
| 173 | |||
| 174 | /** |
||
| 175 | * Get first ip by version |
||
| 176 | * |
||
| 177 | * @param array $ips |
||
| 178 | * @param int|null $version |
||
| 179 | * |
||
| 180 | * @return null|string |
||
| 181 | */ |
||
| 182 | public static function getFirstIPByVersion (array $ips, int $version): ?string |
||
| 183 | { |
||
| 184 | foreach ($ips as $ip) { |
||
| 185 | $current_version = self::getVersionIP($ip); |
||
| 186 | if (is_int($current_version) && $version == $current_version) return $ip; |
||
| 187 | } |
||
| 188 | |||
| 189 | return null; |
||
| 190 | } |
||
| 191 | } |