(int), 'end' => (int)] representing * the according start and end addresses as integer * values. Returns false on malformed input. * * @param string $cidr 192.168.101/24, 1.2.3.4/16, ... * @return array{start: int, end: int}|null start and end address, false on error */ public static function parseCidr(string $cidr): ?array { $parts = explode('/', $cidr); if (count($parts) !== 2) { $ip = ip2long($cidr); if ($ip === false) return null; return ['start' => $ip, 'end' => $ip]; } $ip = $parts[0]; $bits = $parts[1]; if (!is_numeric($bits) || $bits < 0 || $bits > 32) return null; $dots = substr_count($ip, '.'); if ($dots < 3) { $ip .= str_repeat('.0', 3 - $dots); } $ip = ip2long($ip); if ($ip === false) return null; $bits = (int)((2 ** (32 - $bits)) - 1); return ['start' => $ip & ~$bits, 'end' => $ip | $bits]; } /** * Normalizes an IP address to the canonical form. * This supports all numeric IPv4, IPv6 and IPv4-mapped IPv6 addresses. * As well as odd formats like IPv4 with multiple octets collapsed into a single one. * Returns null if the address is invalid. */ public static function normalizeIp(string $ip): ?string { $ip = trim($ip); if ($ip === '') return null; // Check simple case first $addr = @inet_pton($ip); if ($addr !== false) { $ret = inet_ntop($addr); if (substr($ret, 0, 7) === '::ffff:') return substr($ret, 7); return $ret; } if (strpos($ip, ':') !== false) return null; // IPv4 handling // Support octal, hex and shorthand notations $parts = explode('.', $ip); $numParts = count($parts); if ($numParts > 4) return null; $values = []; foreach ($parts as $part) { $part = trim($part); if ($part === '') return null; // Check for hex (0x...) if (strncasecmp($part, '0x', 2) === 0) { if (!ctype_xdigit(substr($part, 2))) return null; $values[] = hexdec($part); } elseif ($part[0] === '0' && strlen($part) > 1) { // Octal (starts with 0, but not just 0) if (!preg_match('/^[0-7]+$/', $part)) return null; $values[] = octdec($part); } else { // Decimal if (!preg_match('/^[0-9]+$/', $part)) return null; $values[] = (int)$part; } } if (empty($values)) return null; // Validate values and handle shorthands // Rules: // 1 part: a (32 bits) // 2 parts: a.b (a: 8 bits, b: 24 bits) // 3 parts: a.b.c (a: 8 bits, b: 8 bits, c: 16 bits) // 4 parts: a.b.c.d (a: 8 bits, b: 8 bits, c: 8 bits, d: 8 bits) $ipv4int = 0; for ($i = 0; $i < $numParts; $i++) { $val = $values[$i]; if ($i === $numParts - 1) { // Last part $max = (2 ** (8 * (4 - $i))) - 1; if ($val < 0 || $val > $max) return null; $ipv4int = ($ipv4int << (8 * (4 - $i))) | (int)$val; } else { if ($val < 0 || $val > 255) return null; $ipv4int = ($ipv4int << 8) | (int)$val; } } return long2ip($ipv4int); } /** * Check if given string is a valid IP address. * Supports all kinds of IP address formats that PHP supports, * including IPv6, IPv4-mapped IPv6, IPv4-in-IPv6, etc. */ public static function isValidIp(string $ip): bool { return self::normalizeIp($ip) !== null; } }