/** * Return the maximum prefix length that would fit the IP address given. * * This is useful to determine how my bit would be needed to store the IP * address when you don't already have a prefix for the IP. * * @example 216.240.32.0 would return 27 * * @param string $ip IP address without prefix * @param integer $bits Maximum bits to check; defaults to 32 for IPv4 and 128 for IPv6 */ public static function max_prefix($ip, $bits = null) { static $mask = array(); $ver = false === filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? 6 : 4; $max = $ver == 6 ? 128 : 32; if ($bits === null) { $bits = $max; } $int = IP::inet_ptod($ip); while ($bits > 0) { // micro-optimization; calculate mask once ... if (!isset($mask[$ver][$bits - 1])) { // 2^$max - 2^($max - $bits); if ($ver == 4) { $mask[$ver][$bits - 1] = pow(2, $max) - pow(2, $max - ($bits - 1)); } else { $mask[$ver][$bits - 1] = bcsub(bcpow(2, $max), bcpow(2, $max - ($bits - 1))); } } $m = $mask[$ver][$bits - 1]; //printf("%s/%d: %s & %s == %s\n", $ip, $bits-1, BC::bcdecbin($m, 32), BC::bcdecbin($int, 32), BC::bcdecbin(BC::bcand($int, $m))); //echo "$ip/", $bits-1, ": ", IP::inet_dtop($m), " ($m) & $int == ", BC::bcand($int, $m), "\n"; if (bccomp(BC::bcand($int, $m), $int) != 0) { return $bits; } $bits--; } return $bits; }