This function should do what you want..
<?php
function merge_cidr(array $cidr_or_ipv4_list)
{ // Main function
$valid_ip='0*?((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))'; // Build the valid ipv4 regex
$valid_ip.=str_repeat(".$valid_ip",3); // Finalize the ipv4 regex accepting leading zeros for each part
$valid_routing_prefix='(?:0*?((?:(?:0)|(?:3[0-2])|(?:[1-2]?[0-9]))))'; // Build a regex for the routing prefix (accepting leading zeros)
foreach($cidr_or_ipv4_list as $a) // For each entry you pass to the function
if (is_string($a) && preg_match("#^[^0-9]*$valid_ip(?:/$valid_routing_prefix)?[^0-9]*$#", $a, $m))
{ // Extracting the valid ipv4 and optionnaly the routing prefix
$m[5] = ctype_digit($m[5]) ? ((int)$m[5]) : 32; // Initialize the valid routing prefix to the extracted value or 32 if mismatch
$c[$m[5]][] = ip2long("$m[1].$m[2].$m[3].$m[4]") & (-1 << (32 - $m[5])); // Initialize the working array with key (prefix) and value as subnet by bitwise the decimal ip
}
if ($c) // If any valid ipv4 with optional routing prefix matched
{
foreach($c as &$unique) $unique=array_unique($unique); //Make unique as possible before processing
$c = merge_cidr_summarize($c); // Pass the valid array to the slave function
foreach($c as $d => & $e) // For each result as routing prefix => Decimal value
$e = array_map(
function ($a) use($d)
{
return [$a, $a + (1 << (32 - $d)) - 1];
}
, $e); // Change it to an array containing the range of ip
foreach($c as $f => $g) // For each result as routing prefix => array of decimal value
foreach($c as $h => $i) // For each result as routing prefix => array of decimal value
if ($f > $h) // If we are not in the same line and the second line have a lower routing prefix
foreach($g as $j => $k) // For each line as id => array of decimal values
foreach($i as $l) // For each line as decimal value in the second foreach
if ($k[0] >= $l[0] && $k[1] <= $l[1]) // If the block with lower routing prefix is totally including the first
unset($c[$f][$j]); // Unset the smaller block
foreach($c as $f => $g) // For each result as routing prefix => array of decimal value
{
usort($g,
function (array $a, array $b)
{
return $b[0]>$a[0]?1:($b[0]<$a[0]?-1:0);
}); // Order the result "naturally" inversed
foreach($g as $h) // For each ordered result
$z[] = long2ip($h[0]) . '/' . $f; // Convert it to human readable
}
return array_reverse($z); // And return the reversed result (order by routing prefix DESC and ip DESC)
}
}
function merge_cidr_summarize(array $a)
{ // Slave function
$b = false; // Did we make a change ?
$c = []; // Initialize the result to an empty array
krsort($a); // Order the input by routing prefix DESC
foreach($a as $d => $e) { // For each entry as cidr => Array of decimal values
sort($a[$d]); // Order the values "naturally"
$k = count($a[$d]); // Count the values for the loop
for ($i = 0; $i < $k; $i++) // Loop to check all values with this routing prefix
if ($a[$d][$i] == $a[$d][$i + 1]) continue; // If the subnet is the same as the next, then directly goto the next
elseif (($a[$d][$i] & (-1 << 33 - $d)) == ($a[$d][$i + 1] & (-1 << 33 - $d))) { // Check if subnet of this line and the next line are equals
$c[$d - 1][] = $a[$d][$i++] & (-1 << 33 - $d); // If yes add the new subnet in result array and skip the next line
$b = true; // And tell the script to run again
}
else $c[$d][] = $a[$d][$i]; // Else don't make anything
}
return $b ? merge_cidr_summarize($c) : $a; // If any change run again else return the result
}
To try it
<?php
$your_array=['160.11.0.0/16','160.11.14.0/20','160.12.0.0/14','160.16.5.0/15','160.16.1.0/14','160.18.0.0/16','160.20.0.0/14','160.24.0.0/16','160.26.0.0/15','160.28.0.0/15','160.74.0.0/16'];
print_r(merge_cidr($your_array));
Output.. Check it in https://eval.in/745895
/*
Array
(
[0] => 160.16.0.0/13
[1] => 160.12.0.0/14
[2] => 160.28.0.0/15
[3] => 160.26.0.0/15
[4] => 160.74.0.0/16
[5] => 160.24.0.0/16
[6] => 160.11.0.0/16
)
*/