My question is simple: I have a text area that is filled with IP/CIDR's and I want to know how I can retrieve an IP from there and then check the IP as used.

The text area (which is implemented in plugin's page configuration and I can edit the configuration page through API, I can get the value from the text area using $pluginData -> ipAddresses) I have, is filled with IP/CIDR that are separated by comma, such as: "32.89.178.0/24, 63.119.179.0/24, 23.119.180.0/24,154.119.181.0/24,193.119.182.0/24,133.119.183.0/24".

My question is: how can I retrieve all the IPs that are in that text area and show them on my admin page, aswell as setting them as a attribute to a service for my client? If it used to check it as "used" and cannot longer be used until it is unchecked.

Since I try to find a solution for my problem I just tried to use "cidrToRange" function but the only thing that I get is "Array".

Dani AI

Generated

A compact, durable approach that fits the suggestions already in this thread (, , , ) is:

  1. Normalize and split the textarea into individual CIDR entries (handle commas, newlines and stray whitespace).
  2. Convert each CIDR into numeric start/end (two unsigned integers) and store those ranges in the database.
  3. Avoid expanding large CIDR blocks into millions of IP strings — expand only when needed for display or small ranges. Use numeric range checks in SQL to mark/check "used" status atomically.

Example parser (robust splitting + trimming):

$raw = (string) $pluginData->ipAddresses;
$entries = array_values(array_filter(array_map('trim',
    preg_split('/[,\r\n;]+/', $raw)
)));
// $entries now holds each "x.x.x.x/yy"

Example helper to get numeric bounds for a CIDR (returns [start_int, end_int] or false):

function cidr_bounds($cidr) {
    $parts = array_pad(explode('/', $cidr, 2), 2, null);
    if (!filter_var($parts[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) || !is_numeric($parts[1])) {
        return false;
    }
    $prefix = (int)$parts[1];
    $ipLong = (float) sprintf('%u', ip2long($parts[0]));
    $hosts = pow(2, 32 - $prefix);
    $start = floor($ipLong / $hosts) * $hosts;
    $end = $start + $hosts - 1;
    return [(int)$start, (int)$end];
}

Suggested table + queries (store ranges; check membership without expanding):

CREATE TABLE ip_ranges (
  id INT AUTO_INCREMENT PRIMARY KEY,
  client_id INT NOT NULL,
  cidr VARCHAR(32) NOT NULL,
  start_ip INT UNSIGNED NOT NULL,
  end_ip   INT UNSIGNED NOT NULL,
  used TINYINT(1) DEFAULT 0,
  INDEX(client_id),
  INDEX(start_ip),
  INDEX(end_ip)
);

Check if an IP is used (MySQL INET_ATON):

SELECT id FROM ip_ranges
WHERE client_id = ? AND start_ip <= INET_ATON(?) AND end_ip >= INET_ATON(?) AND used = 1 LIMIT 1;

Detect overlap before inserting a new CIDR:

SELECT id FROM ip_ranges
WHERE client_id = ? AND NOT (end_ip < :new_start OR start_ip > :new_end) LIMIT 1;

Notes and cautions:

  • Avoid expanding big CIDRs (use a threshold, e.g. > 4096 addresses) — expanding /16+ will exhaust memory and UI.
  • ip2long() can be negative on 32-bit PHP; use sprintf('%u', ip2long(...)) to get unsigned values.
  • For atomic "mark as used" operations use a transaction or UPDATE ... WHERE used = 0 and check affected rows to prevent race conditions.
  • IPv6 requires a different strategy (inet_pton, VARBINARY storage or a library).

This ties the split/parse ideas from the thread into a production-friendly workflow: keep ranges in DB, check membership with integer comparisons, and only expand into individual IP strings when safe and necessary.

Recommended Answers

All 15 Replies

Why not use explode

Have you tried using explode()?

function cidrToRange($cidr) {
    $range = array();
    $cidr = explode('/', $cidr);
    $range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
    $range[1] = long2ip((ip2long($range[0])) + pow(2, (32 - (int)$cidr[1])) - 1);
    return $range;
}
var_dump(cidrToRange("73.35.143.32/27"));

//////////////////OUTPUT////////////////////////
// array(2) {
//   [0]=>
//   string(12) "73.35.143.32"
//   [1]=>
//   string(12) "73.35.143.63"
// }
/////////////////////////////////////////////////
commented: This function only shows the first and the last IP from the CIDR range. +0

If theres discrepencies with spaces etc. I'd consider

$ips = explode(",",str_replace(" ","",$field)); // Gives IP's
foreach($ips as $key => $ip) {
    $range = explode("/",$ip);
    $ips[$key] = $range;
}

which will strip spaces out and then create an array of ip addresses

[0] => [[0] => 192.168.0.0, [1] => 30]
[1] => [[0] => 192.168.10.0, [1] => 10]
commented: Your function works as you said, but what I am trying to do is to retrieve a full list with IPs from the CIDR ranges that I have. +0
<?php
$lines = explode("\n", $instruction_textarea); 
if ( !empty($lines) ) 
  echo '<ul>';
  foreach ( $lines as $line ) 

    echo '<li>'. trim( $line ) .'</li>';


  echo '</ul>';

?>

I found a solution that shows me the list of IPs from the first CIDR range. This is the function that I use:

function cidrToRange($cidr) {
    $range = array();
    $split = explode('/', $cidr);

    if(!empty($split[0]) && is_scalar($split[1]) && filter_var($split[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
        $rangeStart = ip2long($split[0]) & ((-1 << (32 - (int) $split[1])));
        $rangeStartIP = long2ip($rangeStart);
        $rangeEnd = ip2long($rangeStartIP) + pow(2, (32 - (int) $split[1])) - 1;

        for($i = $rangeStart; $i <= $rangeEnd; $i ++) {
            $range[] = long2ip($i);
        }
        return $range;
    } else {
        return $cidr;
    }
}

$IPs = cidrToRange($pluginData -> ipAddresses);

foreach($IPs as $IP) {
    echo "$IP\n";
}

But I want to show a list with all the IPs from all the CIDR ranges.

function cidrToRange($cidr) {
    $range = array();
    $split = explode('/', $cidr);

    if(!empty($split[0]) && is_scalar($split[1]) && filter_var($split[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
        $rangeStart = ip2long($split[0]) & ((-1 << (32 - (int) $split[1])));
        $rangeStartIP = long2ip($rangeStart);
        $rangeEnd = ip2long($rangeStartIP) + pow(2, (32 - (int) $split[1])) - 1;

        for($i = $rangeStart; $i <= $rangeEnd; $i ++) {
            $range[] = long2ip($i);
        }
        return $range;
    } else {
        return $cidr;
    }
}

$ip_arr = array();

foreach($cidrs as $cidr)
{
    array_merge(cidrToRange($cidr),$ip_arr);
}
echo '<ul>';
foreach($ip_arr as $ip)
{
    echo '<li>'.$ip.'</li>';
}
echo '</ul>';
commented: "Notice: Undefined variable: cidrs on line 41 && Warning: Invalid argument supplied for" - Line 41" foreach($cidrs as $cidr) +0

$cidrs is not defined yet. This would be for instance explode(",",$my_form_field) passing an array of ranges

commented: I get only the CIDR, e.g.: 88.39.88.0/24, 89.33.88.0/24 etc.. I want to retrieve a list with IPs from all the CIDRs that I have in my textarea. +0

Give me an 20 mins and I'll write something for you. Just got to finish what I'm on and push to live

commented: Sure, thank you very much! +0

Something like that

<?php

    $ip_ranges = "32.89.178.30/24, 63.119.179.0/24, 23.119.180.0/24,154.119.181.0/24,193.119.182.0/24,133.119.183.0/24";
    function multiCidrToRange($cidrs)
    {
        $return_array = array();
        $cidrs = explode(",",str_replace(" ","",$cidrs));
        foreach($cidrs as $cidr)
        {
            $begin_end = explode("/",$cidr);
            $ip_exp = explode(".",$begin_end[0]);
            $range[] = $begin_end[1];
            $range[] = $ip_exp[3];
            unset($ip_exp[3]);
            $ip_prefix = implode(".",$ip_exp);
            $count = $range[1];
            while( $count <= ($range[0] + $range[1]) )
            {
                $return_array[] = $ip_prefix . "." . $count;
                if($count == 255)
                { exit; }
                $count++;
            }
            $begin_end = false;
            $ip_exp = false;
            $range = false;
            $ip_prefix = false;
            $count = false;
        }
        return $return_array;
    }

    var_dump(multiCidrToRange($ip_ranges));

?>
commented: Using "var_dump($pluginData -> ipAddresses)" it shows me this: "array(0) { }"... +0

What does $pluginData hold? if you pass a list of comma separated range values to the function it will return an array of ip addresses from all of the ranges

commented: The $pluginData -> ipAddresses is the actual text area that holds all the CIDR ranges. +0

So if you var_dump the text field and it's an empty array it implies it has nothing in the textfield. I need to see more of how it's implemented

commented: I solved the error, but the problem is now that I get only the IPs only to .24 and I should get until .255 +0

OH I see. That won't work then. Let me look up the calculations for the range

commented: Any updates? Can't we work something from cidrToRange function? +0

Got meetings for the next couple of hours. I'll post something as soon as I get chance

I tested this while in a meeting so fingers crossed it works for you:

<?php

$ip_ranges = "63.119.179.0/24, 23.119.180.0/24,154.119.181.0/24,193.119.182.0/24,133.119.183.0/24";
    function multiCidrToRange($cidrs)
    {
        $return_array = array();
        $cidrs = explode(",",str_replace(" ","",$cidrs));
        foreach($cidrs as $cidr)
        {
            $begin_end = explode("/",$cidr);
            $ip_exp = explode(".",$begin_end[0]);
            $range[0] = long2ip((ip2long($begin_end[0])) & ((-1 << (32 - (int)$begin_end[1]))));
            $range[1] = long2ip((ip2long($range[0])) + pow(2, (32 - (int)$begin_end[1])) - 1);
            unset($ip_exp[3]);
            $ip_prefix = implode(".",$ip_exp);
            $count = str_replace($ip_prefix . ".","",$range[0]);
            $ncount = 0;
            while( $count <= (str_replace($ip_prefix,"",$range[0]) + str_replace($ip_prefix . ".","",$range[1])) )
            {
                $return_array[] = $ip_prefix . "." . $count;
                $count++;
                $ncount++;
            }
            $begin_end = false;
            $ip_exp = false;
            $range = false;
            $ip_prefix = false;
            $count = false;
        }
        return $return_array;
    }

    var_dump(multiCidrToRange($ip_ranges));

?>
commented: Thank you very much! You saved me. It works in the way I want it to. +0

Glad to hear it :)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.