How do I retrieve house number in magento street value via soap? - php

I have magento, and I'm posting a request via the soap v2 api to get the address of an order.
With that I get the following object which contains the street name + housenumber(God knows why these fields are not seperate...)
$shipping_address->street = "4th avenue 108";
Now what I want is to have the housenumber 108.
How do I get this house number without getting the 4?
(if someone has a more reliable function/piece of code than the one I post below, please feel free to post it.)

What you basically have to do is check for the first number occurence with a space in front of it.
This way you minimse the risk of fetching the wrong number:
// Code by Michael Dibbets
// shared under creative commons Attribution 3.0 Unported (CC BY 3.0 http://creativecommons.org/licenses/by/3.0/ )
$value = "4th street26";
$spl = str_split($value);
$pos = 0;
$location = 0;
// simple loop to find the first character with space + integer
foreach($spl as $char)
{
if(is_numeric($char) && $spl[$pos-1]==' ')
{
$location = $pos;
break;
}
$pos++;
}
// If we didn't encounter the space + integer combination
if(!$location)
{
// is the last character an integer? Assume that the last numbers are house numbers
if(is_numeric($spl[count($spl)-1]))
{
for($c=count($spl)-1;$c>0;$c--)
{
if(is_numeric($spl[$c]))
{
continue;
}
else
{
$location = $c+1;
break;
}
}
}
}
if($location)
{
$street = substr($value,0,$location);
$number = substr($value,$location);
}
else
{
$street = $value;
$number = null;
}
// echoing the results. The number should appear after the dash.
echo $street . ' - ' . $number;

Related

Universal Mobile number validation pattern in PHP

Here is My working Code, if you got a better one post below.
Took me hours to build.
Number can be between (4-15) Universal Standard.
Might contain '+' at the beginning and '-' is allowed once with allowed format like ('1+123,12+123,+1-123,+12-123).
Rest all spaces and '-' and '+' will be replaced by blank and a proper Number will be returned.
public function validateMobileNumber($mobile){
$mobile = str_replace(' ','',$mobile);//remove all the blank spaces i.e +1-123456342234
//now let's do it for mobile numbers
if(preg_match('/^([0-9,\\-,+,]){4,15}$/', $mobile)){//pratially valid number
$mobile = rtrim($mobile, "+");
$mobile = trim($mobile, "-");
//removing multiple '-'
$mobile_arr = explode('-',$mobile);//elplode a number like +1-123 456-342-234
$sub1 = $mobile_arr[0];//+1
if(strlen($sub1) != strlen($mobile)){ // condition where 12345678 valid nos is detected
$check_plus = explode('+',$sub1); //logic to detect number starts with +1-123.. or +12-123.. or 1-123.. or 12-123...
if($check_plus[0] == ''){ // occurs when we get +1...
if(strlen($sub1) == 2 || strlen($sub1) == 3){//valid digits like +1-123, +12-123
unset($mobile_arr[0]);
} else {
//invlid number
return array('status'=>'error','message'=>'Number must be in +1-123.. or +12-123.. or 1-123.. or 12-123... format');
}
} else {
if(strlen($sub1) == 1 || strlen($sub1) == 2){//valid digits like 1-123, 12-123
unset($mobile_arr[0]);
} else {
//invlid number
return array('status'=>'error','message'=>'Number must be in 1-123.. or 12-123.. or +1-123.. or +12-123... format');
}
}
$mobile = $sub1 .'-'.implode('',$mobile_arr);//+1-123 456342234
}
//removing '-' ends
//removing '+'
$mobile_arr = explode('+',$mobile);//explode a number like +1-123 456+342-234
if($mobile_arr[0]!='') {
if (strlen($mobile_arr[0]) != strlen($mobile)){ //number might be 1-123 456+342-234+
return array('status'=>'error','message'=>'Number must have "+" at the start ');
}
} else {
if($mobile_arr[2]!=''){//when we have more than one +
$sub1 = $mobile_arr[1];
unset($mobile_arr[1]);
$mobile = '+'.$sub1.implode('',$mobile_arr);//+1-123 456342234
}
}
return array('status'=>'success','data'=>$mobile);
} else {
return array('status'=>'error','message'=>'Invalid Mobile Number.');
}
//Validate Mobile number logic ends
}

formatting local mobile to international in php

I am looking for a simple regex to convert both UK (44) and Indian (91) numbers into a valid international format using PHP. The formats required are:
447856555333 (for uk mobile numbers)
919876543456 (for indian mobile numbers)
I need a regex that will accept and format the following variations:
1) 07856555333
2) 0785 6555333
3) 0785 655 5333
4) 0785-655-5333
5) 00447856555333
6) 0044785 6555333
7) 0044785 655 5333
8) 0044785-655-5333
9) 00447856555333
10) +447856555333
11) +44785 6555333
12) +44785 655 5333
13) +44785-655-5333
14) +919876543456
15) 00919876543456
Any help would be much appreciated.
UPDATE: Based on answer below I have amended the code slightly and it works very well. It is not bullet proof but covers most of the popular formats:
public static function formatMobile($mobile) {
$locale = '44'; //need to update this
$sms_country_codes = Config::get('sms_country_codes');
//lose any non numeric characters
$numeric_p_number = preg_replace("#[^0-9]+#", "", $mobile);
//remove leading zeros
$numeric_p_number = preg_replace("#^[0]*#", "", $numeric_p_number);
//get first 2 digits
$f2digit = substr($numeric_p_number, 0,2);
if(strlen($numeric_p_number) == 12) {
if(in_array($f2digit, $sms_country_codes) ) {
//no looks ok
}
else {
return ""; //is correct length but missing country code so must be invalid!
}
}
else {
if(strlen($locale . $numeric_p_number) == 12 && !(in_array($f2digit, $sms_country_codes))) {
$numeric_p_number = $locale . $numeric_p_number;
//the number is ok after adding the country prefix
} else {
//something is missing from here
return "";
}
}
return $numeric_p_number;
}
for your particular scope think something like this might work ... not really a regex-only solution but should do the trick for your needs:
$locale = "your_locale_prefix";
$valid_codes = array("44","91");
//loose any non numeric characters
$numeric_p_number = preg_replace("#[^0-9]+#", "", $phone_number);
//remove leading zeros
$numeric_p_number = preg_replace("#^[0]*#", "", $numeric_p_number);
//get first 2 digits
$f2digit = substr($numeric_p_number, 0,2);
if(in_array($f2digit, $valid_codes) && strlen($numeric_p_number) == 12){
//code is ok
} else {
if(strlen($locale . $numeric_p_number) == 12) {
//the number is ok after adding the country prefix
} else {
//something is missing from here
}
}

PHP MeCard Decoder or MeCard to VCard Converter

I'm working on a QR code parser and I'm wondering if anyone knows of a MeCard library, or code that converts a MeCard to a VCard. If not, is there an official specification doc for MeCard out there? I know NTT DoCoMo created it, but I can't find any kind of RFC on it.
From http://code.google.com/p/zxing/wiki/BarcodeContents, I found a link to DoCoMo's specification for MeCard, at http://www.nttdocomo.co.jp/english/service/developer/make/content/barcode/function/application/addressbook/index.html. It is pretty dirt simple and converting it to a VCard with a function call should be pretty trivial.
== EDIT ==
I wrote a little conversion function. Just in case anyone in the future wants the code, it is below:
private function MeCardtoVCard($mecard_text)
{
// Useful References:
// http://www.nttdocomo.co.jp/english/service/developer/make/content/barcode/function/application/addressbook/index.html
// http://code.google.com/p/zxing/wiki/BarcodeContents
// http://en.wikipedia.org/wiki/VCard
// https://theqrplace.wordpress.com/2011/05/02/qr-code-tech-info-mecard-format/
$vcard = '';
if (stripos($mecard_text, "mecard") === 0)
{
$mecard_text = str_replace("\n", "", $mecard_text); // Strip out newlines
$mecard_text = substr($mecard_text,7); // Strip off the MECARD: header
$lines = explode(";", $mecard_text);
if (count($lines) > 0)
{
// Using Version 2.1 because it is the most commonly supported.
$vcard = "BEGIN:VCARD\nVERSION:3.0\n";
foreach($lines as $l)
{
$line_elements = explode(":",$l);
if (count($line_elements) > 1)
{
// Must split into two parts. Not sure how DoCoMo escapes
// data that actually contains a ":", so for now we are presuming
// that the first token is the property name and all other elements
// are the value
$property = $line_elements[0];
$value = implode(":", array_slice($line_elements,1));
if ($property != '' && $value != '')
{
if ($property == 'N')
{
// MeCards only support first and last name.
$tmp = explode(",",$value);
if (count ($tmp) == 1)
{
$vcard .= "N:;" . $tmp[0] . "\n";
}
else
{
$vcard .= "N:" . implode(";",explode(",",$value)) . "\n";
}
}
if ($property == 'TEL')
{
// MeCard does not use card types, so we will presume all of them are type CELL
$vcard .= "TEL:" . $value . "\n";
}
if ($property == 'ADR')
{
// MeCard: "The fields divided by commas (,) denote PO box, room number, house number, city, prefecture, zip code and country, in order."
// VCard: "...post office box; the extended address; the street address; the locality (e.g., city); the region (e.g., state or province); the postal code; the country name" See http://www.ietf.org/rfc/rfc2426.txt 3.2.1
$vcard .= "ADR:" . implode(";",explode(",",$value)) . "\n";
}
if (in_array($property, array('NOTE', 'BDAY', 'URL', 'NICKNAME')))
{
$vcard .= $property . ':' . $value . "\n";
}
}
}
}
$vcard .= "END:VCARD\n";
return $vcard;
}
else
{
return false;
}
}
return false;
}

Finding nearest value in array from user-input

I have a CSV of GDP figures, both nominal and PPP. The structure is Country,Nominal,PPP.
Example:
Islamic Republic of Afghanistan,560.673,998.466
Albania,"3,616.10","7,381.00"
Algeria,"4,477.80","7,103.61"
Users enter in a nominal GDP value. What I'm trying to figure out is how to use that value to find the nearest nominal GDP value in an array constructed from the CSV file. Once I find the nominal value, I'm wanting to print the country and the PPP value.
I haven't scripted anything in quite some time, so I can't get my head around how to go about doing this.
// $input_gdp is entered by user
// $my_data is an array from the csv
$closest = -1;
$closest_diff = false;
foreach($my_data as $key=>$row) {
// $row[0] - Country
// $row[1] - Nominal
// $row[2] - PPP
if($closest_diff === false) {
$closest = $key;
$closest_diff = abs($row[1] - $input_gdp);
} else {
$current_diff = abs($row[1] - $input_gdp);
if($current_diff < $closest_diff) {
$closest = $key;
$closest_diff = $current_diff;
}
}
}
if($closest > -1) {
echo 'The closest is ' . $my_data[$closest][0] . ' with a PPP of ' . $my_data[$closest][2] . '.';
} else {
echo 'None were found.';
}

Getting list IPs from CIDR notation in PHP

Is there a way (or function/class) to get the list of IP addresses from a CIDR notation?
For example, I have 73.35.143.32/27 CIDR and want to get the list of all IP's in this notation. Any suggestions?
Thank you.
I'll edit the aforementioned class to contain a method for that. Here is the code I came up with that might help you until then.
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"
// }
/////////////////////////////////////////////////
Returns the low end of the ip range as the first entry in the array, then returns the high end as the second entry.
Well, it's a bitmask - 73.35.143.32/27 means that 27 bits are the network mask, and the rest is available for assigning to the nodes in the network:
73.35.143.32
in binary is this (dots shown for legibility):
01001001.00100011.10001111.00100000
The netmask is 27 bits:
11111111.11111111.11111111.11100000
So you can just AND them together and get this:
01001001.00100011.10001111.001 00000
network prefix (27 bits) | node address (5 bits)
From here, you can just enumerate all the combinations in the node address (00000 is 0, 11111 is 31, so a simple loop is enough), and you'll have all the available hosts.
Converting this pseudocode to PHP is left as an exercise to the reader ;)
Oh, and the obligatory deprecation warning: IPv4 is now full, consider also IPv6.
Here is one fast 64bits function to do it, please comment the return line you don't need. Accepting any valid Ipv4 with or without valid CIDR Routing Prefix for example 63.161.156.0/24 or 63.161.156.0
<?php
function cidr2range($ipv4){
if ($ip=strpos($ipv4,'/'))
{$n_ip=(1<<(32-substr($ipv4,1+$ip)))-1; $ip_dec=ip2long(substr($ipv4,0,$ip)); }
else
{$n_ip=0; $ip_dec=ip2long($ipv4); }
$ip_min=$ip_dec&~$n_ip;
$ip_max=$ip_min+$n_ip;
#Array(2) of Decimal Values Range
return [$ip_min,$ip_max];
#Array(2) of Ipv4 Human Readable Range
return [long2ip($ip_min),long2ip($ip_max)];
#Array(2) of Ipv4 and Subnet Range
return [long2ip($ip_min),long2ip(~$n_ip)];
#Array(2) of Ipv4 and Wildcard Bits
return [long2ip($ip_min),long2ip($n_ip)];
#Integer Number of Ipv4 in Range
return ++$n_ip;
}
To run fast the function don't check input but formally it should be a string matching the following regex
#^(?:((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))(?:/((?:(?:0)|(?:3[0-2])|(?:[1-2]?[0-9]))))?)$#
If you want to verify the input before using the function
<?php
if (is_string($ipv4) && preg_match('#^(?:((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))(?:/((?:(?:0)|(?:3[0-2])|(?:[1-2]?[0-9]))))?)$#',$ipv4))
{
#This is a valid ipv4 with or without CIDR Routing Prefix
$result=cidr2range($ipv4);
print_r($result);
}
To get the full range as an array for a given IP (with or without CIDR Routing Prefix) you can use the following code but be carefull because for example 25.25.25.25/16 return an array with 65536 elements and you can easily run out of memory using a smaller Routing Prefix
<?php
$result=cidr2range($ipv4);
for($ip_dec=$result[0];$ip_dec<=$result[1];$ip_dec++)
$full_range[$ip_dec]=long2ip($ip_dec);
print_r($full_range);
To fast check if a given ipv4 is matching a given CIDR you can do it inline like in this example
<?php
$given_cidr='55.55.55.0/24';
$given_ipv4='55.55.55.55';
if(($range=cidr2range($given_cidr)) &&
($check=ip2long($given_ipv4))!==false &&
$check>=$range[0] && $check<=$range[1])
{
echo 'Yes, '.$given_ipv4.' is included in '.$given_cidr;
}
else
{
echo 'No, '.$given_ipv4.' is not included in '.$given_cidr;
}
To fast check if a given ipv4 is matching a given array of IP (with or without CIDR Routing Prefix)
<?php
#This code is checking if a given ip belongs to googlebot
$given_ipv4='74.125.61.208';
$given_cidr_array=['108.59.93.43/32','108.59.93.40/31','108.59.93.44/30','108.59.93.32/29','108.59.93.48/28','108.59.93.0/27','108.59.93.64/26','108.59.93.192/26','108.59.92.192/27','108.59.92.128/26','108.59.92.96/27','108.59.92.0/27','108.59.94.208/29','108.59.94.192/28','108.59.94.240/28','108.59.94.128/26','108.59.94.16/29','108.59.94.0/28','108.59.94.32/27','108.59.94.64/26','108.59.95.0/24','108.59.88.0/22','108.59.81.0/27','108.59.80.0/24','108.59.82.0/23','108.59.84.0/22','108.170.217.128/28','108.170.217.160/27','108.170.217.192/26','108.170.217.0/25','108.170.216.0/24','108.170.218.0/23','108.170.220.0/22','108.170.208.0/21','108.170.192.0/20','108.170.224.0/19','108.177.0.0/17','104.132.0.0/14','104.154.0.0/15','104.196.0.0/14','107.167.160.0/19','107.178.192.0/18','125.17.82.112/30','125.16.7.72/30','74.125.0.0/16','72.14.192.0/18','77.109.131.208/28','77.67.50.32/27','66.102.0.0/20','66.227.77.144/29','66.249.64.0/19','67.148.177.136/29','64.124.98.104/29','64.71.148.240/29','64.68.64.64/26','64.68.80.0/20','64.41.221.192/28','64.41.146.208/28','64.9.224.0/19','64.233.160.0/19','65.171.1.144/28','65.170.13.0/28','65.167.144.64/28','65.220.13.0/24','65.216.183.0/24','70.32.132.0/23','70.32.128.0/22','70.32.136.0/21','70.32.144.0/20','85.182.250.128/26','85.182.250.0/25','80.239.168.192/26','80.149.20.0/25','61.246.224.136/30','61.246.190.124/30','63.237.119.112/29','63.226.245.56/29','63.158.137.224/29','63.166.17.128/25','63.161.156.0/24','63.88.22.0/23','41.206.188.128/26','12.234.149.240/29','12.216.80.0/24','8.34.217.24/29','8.34.217.0/28','8.34.217.32/27','8.34.217.64/26','8.34.217.128/25','8.34.216.0/24','8.34.218.0/23','8.34.220.0/22','8.34.208.128/29','8.34.208.144/28','8.34.208.160/27','8.34.208.192/26','8.34.208.0/25','8.34.209.0/24','8.34.210.0/23','8.34.212.0/22','8.35.195.128/28','8.35.195.160/27','8.35.195.192/26','8.35.195.0/25','8.35.194.0/24','8.35.192.0/23','8.35.196.0/22','8.35.200.0/21','8.8.8.0/24','8.8.4.0/24','8.6.48.0/21','4.3.2.0/24','23.236.48.0/20','23.251.128.0/19','216.239.32.0/19','216.252.220.0/22','216.136.145.128/27','216.33.229.160/29','216.33.229.144/29','216.34.7.176/28','216.58.192.0/19','216.109.75.80/28','216.74.130.48/28','216.74.153.0/27','217.118.234.96/28','208.46.199.160/29','208.44.48.240/29','208.21.209.0/28','208.184.125.240/28','209.185.108.128/25','209.85.128.0/17','213.200.103.128/26','213.200.99.192/26','213.155.151.128/26','199.192.112.224/29','199.192.112.192/27','199.192.112.128/26','199.192.112.0/25','199.192.113.176/28','199.192.113.128/27','199.192.113.192/26','199.192.113.0/25','199.192.115.80/28','199.192.115.96/27','199.192.115.0/28','199.192.115.128/25','199.192.114.192/26','199.192.114.0/25','199.223.232.0/21','198.108.100.192/28','195.16.45.144/29','192.104.160.0/23','192.158.28.0/22','192.178.0.0/15','206.160.135.240/28','207.223.160.0/20','203.222.167.144/28','173.255.125.72/29','173.255.125.80/28','173.255.125.96/27','173.255.125.0/27','173.255.125.128/25','173.255.124.240/29','173.255.124.232/29','173.255.124.192/27','173.255.124.128/29','173.255.124.144/28','173.255.124.160/27','173.255.124.48/29','173.255.124.32/28','173.255.124.0/27','173.255.124.64/26','173.255.126.0/23','173.255.122.128/26','173.255.122.64/26','173.255.123.0/24','173.255.121.128/26','173.255.121.0/25','173.255.120.0/24','173.255.117.32/27','173.255.117.64/26','173.255.117.128/25','173.255.116.192/27','173.255.116.128/26','173.255.116.0/25','173.255.118.0/23','173.255.112.0/22','173.194.0.0/16','172.102.8.0/21','172.253.0.0/16','172.217.0.0/16','162.216.148.0/22','162.222.176.0/21','180.87.33.64/26','128.177.109.0/26','128.177.119.128/25','128.177.163.0/25','130.211.0.0/16','142.250.0.0/15','146.148.0.0/17'];
echo '<pre>';
$in_range=false;
if (($given_ipv4_dec=ip2long($given_ipv4))!==false)
{
foreach($given_cidr_array as $given_cidr){
if(($range=cidr2range($given_cidr)) &&
$given_ipv4_dec>=$range[0] && $given_ipv4_dec<=$range[1])
{
$in_range=true;
echo $given_ipv4.' matched '.$given_cidr.' ('.join(array_map('long2ip',$range),' - ').")\n";
}
}
}
echo $given_ipv4.' is probably'.($in_range?'':' not').' a Googlebot IP';
hope that these few lines have helped you…
There is small fix - when someone place ip prefix like: 127.0.0.15/26 this function returns bad last IP adress. In this function is at line 4 (starts: $range[1]...) changed $cidr[0] to $range[0] - now its return last IP address good for every IP in range.
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("127.0.0.15/26"));
Before fix:
array(2) {
[0]=>
string(9) "127.0.0.0"
[1]=>
string(10) "127.0.0.78"
}
string(41) "127.0.0.15/26 >> 2130706432 -> 2130706510"
After fix:
array(2) {
[0]=>
string(9) "127.0.0.0"
[1]=>
string(10) "127.0.0.63"
}
string(41) "127.0.0.15/26 >> 2130706432 -> 2130706495"
Highly recommend this PHP Lib:
https://github.com/S1lentium/IPTools
It can manipulate network easily with many functions.
e.g. Iterate over Network IP adresses:
$network = Network::parse('192.168.1.0/24');
foreach($network as $ip) {
echo (string)$ip . PHP_EOL;
}
// output:
192.168.1.0
...
192.168.1.255
I come up with a better idea
$ip_from= long2ip(ip2long($ip)& (-1<<(32-$net_mask)));
$ip_to= long2ip(ip2long($ip)| (~(-1<<(32-$net_mask))));
P.S:$ip is a ipv4 address like 60.12.34.5;$net_mask is a int net mask like 25;
it's very fast because of the bit shift operation
this returns an array of ips:
function get_list_ip($ip_addr_cidr){
$ip_arr = explode("/", $ip_addr_cidr);
$bin = "";
for($i=1;$i<=32;$i++) {
$bin .= $ip_arr[1] >= $i ? '1' : '0';
}
$ip_arr[1] = bindec($bin);
$ip = ip2long($ip_arr[0]);
$nm = $ip_arr[1];
$nw = ($ip & $nm);
$bc = $nw | ~$nm;
$bc_long = ip2long(long2ip($bc));
for($zm=1;($nw + $zm)<=($bc_long - 1);$zm++)
{
$ret[]=long2ip($nw + $zm);
}
return $ret;
}
Extension to #jonavon 's answer. If you need flat list of IPs. You can convert his function like below.
function cidrToRange($value) {
$range = array();
$split = explode('/', $value);
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])));
$rangeEnd = ip2long($split[0]) + pow(2, (32 - (int)$split[1])) - 1;
for ($i = $rangeStart; $i <= $rangeEnd; $i++) {
$range[] = long2ip($i);
}
return $range;
} else {
return $value;
}
}
I don't believe this class will return a list of IPs, but it does provide some useful methods for working with CIDR blocks.
I previously wrote an entire Class which handled this task pretty well (for another Language), and thanks to PHP's ip2long(...) method, we don't need to calculate manually in PHP.
But my Unit-Test data can be used to test any of the other answers:
<?php
// Below has "CIDR = Minimum ... Maximum" format.
$validRangeMap = [
'0.0.0.0/1=0.0.0.0...127.255.255.255',
'128.0.0.0/2=128.0.0.0...191.255.255.255',
'192.0.0.0/9=192.0.0.0...192.127.255.255',
'192.128.0.0/11=192.128.0.0...192.159.255.255',
'192.160.0.0/13=192.160.0.0...192.167.255.255',
'192.168.0.0/19=192.168.0.0...192.168.31.255',
'192.168.32.0/21=192.168.32.0...192.168.39.255',
'192.168.40.0/23=192.168.40.0...192.168.41.255',
'192.168.42.0/23=192.168.42.0...192.168.43.255',
'192.168.44.0/22=192.168.44.0...192.168.47.255',
'192.168.48.0/20=192.168.48.0...192.168.63.255',
'192.168.64.0/18=192.168.64.0...192.168.127.255',
'192.168.128.0/17=192.168.128.0...192.168.255.255',
'192.169.0.0/16=192.169.0.0...192.169.255.255',
'192.170.0.0/15=192.170.0.0...192.171.255.255',
'192.172.0.0/14=192.172.0.0...192.175.255.255',
'192.176.0.0/12=192.176.0.0...192.191.255.255',
'192.192.0.0/10=192.192.0.0...192.255.255.255',
'193.0.0.0/8=193.0.0.0...193.255.255.255',
'194.0.0.0/7=194.0.0.0...195.255.255.255',
'196.0.0.0/6=196.0.0.0...199.255.255.255',
'200.0.0.0/5=200.0.0.0...207.255.255.255',
'208.0.0.0/4=208.0.0.0...223.255.255.255',
'224.0.0.0/4=224.0.0.0...239.255.255.255',
'240.0.0.0/5=240.0.0.0...247.255.255.255',
'248.0.0.0/6=248.0.0.0...251.255.255.255',
'252.0.0.0/7=252.0.0.0...253.255.255.255',
'254.0.0.0/8=254.0.0.0...254.255.255.255',
'255.0.0.0/9=255.0.0.0...255.127.255.255',
'255.128.0.0/10=255.128.0.0...255.191.255.255',
'255.192.0.0/11=255.192.0.0...255.223.255.255',
'255.224.0.0/12=255.224.0.0...255.239.255.255',
'255.240.0.0/13=255.240.0.0...255.247.255.255',
'255.248.0.0/14=255.248.0.0...255.251.255.255',
'255.252.0.0/15=255.252.0.0...255.253.255.255',
'255.254.0.0/16=255.254.0.0...255.254.255.255',
'255.255.0.0/17=255.255.0.0...255.255.127.255',
'255.255.128.0/18=255.255.128.0...255.255.191.255',
'255.255.192.0/19=255.255.192.0...255.255.223.255',
'255.255.224.0/20=255.255.224.0...255.255.239.255',
'255.255.240.0/21=255.255.240.0...255.255.247.255',
'255.255.248.0/22=255.255.248.0...255.255.251.255',
'255.255.252.0/23=255.255.252.0...255.255.253.255',
'255.255.254.0/24=255.255.254.0...255.255.254.255',
'255.255.255.0/25=255.255.255.0...255.255.255.127',
'255.255.255.128/26=255.255.255.128...255.255.255.191',
'255.255.255.192/27=255.255.255.192...255.255.255.223',
'255.255.255.224/28=255.255.255.224...255.255.255.239',
'255.255.255.240/29=255.255.255.240...255.255.255.247',
'255.255.255.248/30=255.255.255.248...255.255.255.251',
'255.255.255.252/31=255.255.255.252...255.255.255.253',
'255.255.255.254/32=255.255.255.254...255.255.255.254',
];
Example
We can test Jonavon's answer, like:
foreach ($validRangeMap as $entry) {
$expected = preg_split('/(=|(\.\.\.))+/', $entry);
$actual = cidrToRange($expected[0]);
if ($actual[0] !== $expected[1]) {
echo "Test-Failed! '$actual[0]' should be equal '$expected[1]' <br>" . PHP_EOL;
} else if ($actual[1] !== $expected[2]) {
echo "Test-Failed! '$actual[1]' should be equal '$expected[2]' <br>" . PHP_EOL;
} else {
echo "Tested '$expected[0]' CIDR <br>" . PHP_EOL;
}
}
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;
}
Note that it passed the tests.
The utility cidrl will do this:
$ cidrl 73.35.143.32/27
73.35.143.32
73.35.143.33
73.35.143.34
...
73.35.143.63
The PHP function cidrl() iterates over each IP address in a CIDR block using an anonymous function:
cidrl('194.168.0.1/28', $error_code, function($address) {
print "$address\n";
});
Or, as an array:
$addresses = cidrl('194.168.0.1/28', $error_code);
Can be installed from Packagist emden-norfolk/cidrl with Composer:
composer require emden-norfolk/cidrl
(Note: This package is no longer supported in favour of the cidrl command mentioned in my other answer.)
Hej. I also needed this function and I edit 1 other to return me list of all IP addresses from range.
This will return list of IP addresses from CIDR Notation. Enjoy it ;)
<?php
$ip_addr_cidr = "192.256.0.0/16";
$ip_arr = explode('/', $ip_addr_cidr);
$bin = '';
for($i=1;$i<=32;$i++) {
$bin .= $ip_arr[1] >= $i ? '1' : '0';
}
$ip_arr[1] = bindec($bin);
$ip = ip2long($ip_arr[0]);
$nm = ip2long($ip_arr[1]);
$nw = ($ip & $nm);
$bc = $nw | (~$nm);
echo "Number of Hosts: " . ($bc - $nw - 1) . "<br/>";
echo "Host Range: " . long2ip($nw + 1) . " -> " . long2ip($bc - 1) . "<br/>". "<br/>";
for($zm=1;($nw + $zm)<=($bc - 1);$zm++)
{
echo long2ip($nw + $zm). "<br/>";
}
?>
Fixed version of Kosmonaft script:
function get_list_ip($ip_addr_cidr){
$ip_arr = explode("/", $ip_addr_cidr);
$bin = "";
for($i=1;$i<=32;$i++) {
$bin .= $ip_arr[1] >= $i ? '1' : '0';
}
$ip_arr[1] = bindec($bin);
$ip = ip2long($ip_arr[0]);
$nm = $ip_arr[1];
$nw = ($ip & $nm);
$bc = $nw | ~$nm;
$bc_long = ip2long(long2ip($bc));
echo "Number of Hosts: " . ($bc_long - $nw - 1) . "<br/>";
echo "Host Range: " . long2ip($nw + 1) . " -> " . long2ip($bc - 1) . "<br/>". "<br/>";
for($zm=1;($nw + $zm)<=($bc_long - 1);$zm++)
{
echo long2ip($nw + $zm). "<br/>";
}
}
I had some problems with the previous versions, various PHP errors like undefined offset etc.
I found what I think is a better way to do it without the errors as detailed below.
This takes IPV4 CIDR notation IP addresses and can be used to get the start/end IP addresses of the CIDR range or count the total number of IP addresses in a database of IP addresses.
$count = 0;
$result = $sia_db_con->query("SELECT `ip_address` FROM `ip4` WHERE `ip_ban_active`='True' ORDER BY ip4.added ASC");
while ($row = $result->fetch_array()) {
$pos = strpos($row[0], "/");
$ip_arr = [0 => substr($row[0], 0, $pos), 1 => substr($row[0], $pos+1)];
$start = cidr2ip($ip_arr)[0];
$end = cidr2ip($ip_arr)[1];
$count = $count + (ip2long($end) - ip2long($start)) + 1;
}
unset($result, $row, $pos, $ip_arr, $start, $end, $range);
function cidr2ip($cidr) {
$start = ip2long($cidr[0]);
$nm = $cidr[1];
$num = pow(2, 32 - $nm);
$end = $start + $num - 1;
$range = [0 => $cidr[0], 1 => long2ip($end)];
unset($start, $nm, $num, $end);
return $range;
}
The secret here is the way in which I create the $ip_arr variable. Rather than using PHP explode, I set the array dimensions manually for each IP address. This is of course a database version of the code and cycles through all IP addresses in the database which meet the search criteria.

Categories