'Merge IP Ranges with PHP

I am stuck at a point and cant think beyond it :

I have a list of Ip ranges, I need to merge them making them as a range. For example

192.168.1.1

192.168.1.2

192.168.1.3

192.168.1.5

192.168.1.6

I need to club them into IP ranges. For this case it will be 192.168.1.1-192.168.1.3 and 192.168.1.5-192.168.1.6

I was thinking of converting these into integer by using ip2long function and sorting it. But can't think beyond that.

Any help would be appreciated.

Thanks In advance :)



Solution 1:[1]

Here is my answer :

$ip_list = array('192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.5', '192.168.1.6');
$ip_long_list = array();
foreach($ip_list as $ip) {
  $ip_long_list[] = ip2long($ip);
}
unset($ip_list); // no longer in use 
sort($ip_long_list); // sort it

$result = array();
$cnt_group = 0;
for($i = 0; $i < count($ip_long_list)-1; $i++) {
  if($i == 0) {
    $result[$cnt_group] = array($ip_long_list[0]); // initialize 1st group
    continue;
  }
  if(abs($ip_long_list[$i] - $ip_long_list[$i+1]) == 1) {
    // they are in series, group it.
    $tmp_array = $result[$cnt_group]; // get back the array
    $tmp_array[] = $ip_long_list[$i]; // push item into array
    $result[$cnt_group] = $tmp_array;
    unset($tmp_array); // remove to save memory
  } else {
    // they are separate. next group.
    $cnt_group++;
    $result[$cnt_group] = array($ip_long_list[1]);
  }
}

After all the above, convert back to IP from Long.

foreach($result as $ip_long_group) {
  $first_ip = long2ip($ip_long_group[0]);
  $last_ip = long2ip($ip_long_group[count($ip_long_group)-1];

  $common_parts = longest_common_substring(array($first_ip, $last_ip));

  echo $common_parts . str_replace($common_parts, '', $first_ip) . '-' . str_replace($common_parts, '', $last_ip) . PHP_EOL;
}

Note: I used a 3rd party function (listed in Reference) to get the common parts of 2 strings.

Reference: longest_common_substring()

Solution 2:[2]

Manual method to get range (without using ip2long):

<?php  
 $ip_addressess=array(
   '192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.5', '192.168.1.6', '192.168.1.7', '192.168.1.9', '192.168.1.10',   '192.168.1.11', '192.168.1.15', '192.168.1.16', '192.168.1.17');

foreach($ip_addressess as $ip_address){
    $break_ip[] = explode('.', $ip_address); //break ip address
 }
foreach($break_ip as $break){
    $numbers[] = $break[3]; //get last number of ip address
}
$commaList_ipaddress = implode(',', $numbers); //generate comma separated list of last digits of ip address
$missing = array();
$a =0 ;
for ($i = 1; $i <= max($numbers); $i++) {
    if (!in_array($i, $numbers))
    {       
        $missing[] = $i;
    $a++;

    } else{
    $new_ip_ranges[$a][] = $i;    
    }

}
$new_ip_ranges = array_values($new_ip_ranges); //rearrange the array
foreach($new_ip_ranges as $new_range){
    $min = min($new_range);
    $max  = max($new_range);    
    $min_range = $break_ip[0][0].'.'.$break_ip[0][1].'.'.$break_ip[0][2].'.'.$min;
    $max_range = $break_ip[0][0].'.'.$break_ip[0][1].'.'.$break_ip[0][2].'.'.$max;       
    $range[] = $min_range .'-'.$max_range; 

}
echo '<pre>';
print_r($range);


?>

Output:

Array
(
    [0] => 192.168.2.1-192.168.2.3
    [1] => 192.168.2.5-192.168.2.7
    [2] => 192.168.2.9-192.168.2.11
    [3] => 192.168.2.15-192.168.2.17
)

Demo at CodeFiddle>>

Solution 3:[3]

function printCombinedIPs($ip_arr) {
    //$ip_arr = array('192.168.1.5','192.168.1.6');
    $k = NULL;
    $prev = -1;
    foreach ( $ip_arr as $ip ) {
        $octet = explode(".",$ip);
        $fourth = $octet[3];
        $net = $octet[0].".".$octet[1].".".$octet[2];

        if ( $fourth < $prev ) { 
            $prev = -1;
        }

        $end = $fourth;
        if ( $fourth - 1 != $prev ) {
            $start = $fourth;
            $rtn_arr[] = $k;
        }
        
        if ( $start == $end ) 
            $k = $net.".$end";
        else
            $k = $net.".$start-$end";

        $prev = $octet[3];
    }
    $rtn_arr[] = $k;
    array_shift($rtn_arr);
    return implode(", ", $rtn_arr); 
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Vijaya Pandey
Solution 3 Ivaylo M. Ivanov