It's not a novelty, it's already been done a long time ago.
is to use PHP to operate a pure IP library or polyp IP library, based on the IP of the visitors to get the physical location.
I'll post the code first. And then step by step analysis out. Hope to be helpful to the friends who want to know this piece.
Only the code for PHP5. will continue to optimize the code.
Class iplocation{
Private $fp;
Private $wrydat;
Private $wrydat _version;
Private $ipnumber;
Private $firstip;
Private $lastip;
Private $ip _range_begin;
Private $ip _range_end;
Private $country;
Private $area;
Const REDIRECT_MODE_0 = 0;
Const REDIRECT_MODE_1 = 1;
Const REDIRECT_MODE_2 = 2;
function __construct () {
$args = Func_get_args ();
$this->wrydat = Func_num_args () >0? $args [0]: ' CoralWry.dat ';
$this->initialize ();
}
function __destruct () {
Fclose ($this->FP);
}
Private Function Initialize () {
if (file_exists ($this->wrydat))
$this->FP = fopen ($this->wrydat, ' RB ');
$this->getipnumber ();
$this->getwryversion ();
}
Public function Get ($STR) {
return $this $str;
}
Public function set ($STR, $val) {
$this $str = $val;
}
Private Function GetByte ($length, $offset =null) {
if (!is_null ($offset)) {
Fseek ($this->fp, $offset, Seek_set);
}
$b = Fread ($this->fp, $length);
return $b;
}
/**
* Package IP address into binary data, packed in big endian (high in front) format
* Data storage format is little endian (low in front) such as:
* C6 DA 218.198.40.0 Little Endian
* 3F C6 DA 218.198.40.0 Little Endian
* Such data cannot be compared for binary search, so the acquired IP data must first be converted to big endian using Strrev
* @param $ip
* @return binary data in big endian format
*/
Private Function Packip ($IP) {
Return Pack ("N", Intval (Ip2long ($IP)));
}
Private Function Getlong ($length =4, $offset =null) {
$CHR =null;
for ($c =0; $length%4!=0&& $c < (4-$length%4); $c + +) {
$CHR. = chr (0);
}
$var = Unpack ("Vlong", $this->getbyte ($length, $offset). $CHR);
return $var [' Long '];
}
Private Function Getwryversion () {
$length = Preg_match ("/coral/i", $this->wrydat)? 26:30;
$this->wrydat_version = $this->getbyte ($length, $this->firstip-$length);
}
Private Function Getipnumber () {
$this->firstip = $this->getlong ();
$this->lastip = $this->getlong ();
$this->ipnumber = ($this->lastip-$this->firstip)/7+1;
}
Private Function GetString ($data = "", $offset =null) {
$char = $this->getbyte (1, $offset);
while (Ord ($char) > 0) {
$data. = $char;
$char = $this->getbyte (1);
}
return $data;
}
Private Function Iplocaltion ($IP) {
$ip = $this->packip ($IP);
$low = 0;
$high = $this->ipnumber-1;
$ipposition = $this->lastip;
while ($low <= $high) {
$t = Floor (($low + $high)/2);
if ($ip < Strrev ($this->getbyte (4, $this->firstip+ $t *7)) {
$high = $t-1;
} else {
if ($ip > Strrev ($this->getbyte (4, $this->getlong (3))) {
$low = $t + 1;
}else{
$ipposition = $this->firstip+ $t *7;
Break
}
}
}
return $ipposition;
}
Private Function Getarea () {
$b = $this->getbyte (1);
Switch (ord ($b)) {
Case SELF::REDIRECT_MODE_0:
return "Unknown";
Break
Case Self::redirect_mode_1:
Case self::redirect_mode_2:
return $this->getstring ("", $this->getlong (3));
Break
Default
return $this->getstring ($b);
Break
}
}
Public Function Getiplocation ($IP) {
$ippos = $this->iplocaltion ($IP);
$this->ip_range_begin = Long2ip ($this->getlong (4, $ippos));
$this->ip_range_end = Long2ip ($this->getlong (4, $this->getlong (3)));
$b = $this->getbyte (1);
Switch (ord ($b)) {
Case Self::redirect_mode_1:
$b = $this->getbyte (1, $this->getlong (3));
if (ord ($b) = = redirect_mode_2) {
$countryoffset = $this->getlong (3);
$this->area = $this->getarea ();
$this->country = $this->getstring ("", $countryoffset);
}else{
$this->country = $this->getstring ($b);
$this->area = $this->getarea ();
}
Break
Case self::redirect_mode_2:
$countryoffset = $this->getlong (3);
$this->area = $this->getarea ();
$this->country = $this->getstring ("", $countryoffset);
Break
Default
$this->country = $this->getstring ($b);
$this->area = $this->getarea ();
Break
}
}
}
/* */
Echo Microtime ();
echo "\ n";
$iploca = new Iplocation;
$iploca = new Iplocation (' QQWry.dat ');
echo $iploca->get (' wrydat_version ');
echo "\ n";
echo $iploca->get (' Ipnumber ');
echo "\ n";
$iploca->getiplocation (' 211.44.32.34 ');
/**/
echo $iploca->get (' Ip_range_begin ');
echo "\ n";
echo $iploca->get (' ip_range_end ');
echo "\ n";
echo $iploca->get (' country ');
echo "\ n";
echo $iploca->get (' area ');
echo "\ n";
echo $iploca->get (' Lastip ');
echo "\ n";
Echo Microtime ();
echo "\ n";
Unset ($iploca);
Reference: LUMAQQ's pure IP database format detailed
The CoralWry.dat file structure is divided into 3 regions:
- File header [fixed 8 bytes]
- Data area [not fixed length, record IP address information]
- Index area [size determined by file header]
The file data is stored in the following way: Little endian.
Here's a quote on the difference between little endian and big endian in Unicode encoding
References :
The big endian and little endian are different ways the CPU handles multibyte numbers. For example, the Unicode encoding of the word "Han" is 6c49. So when you write to a file, do you write 6C in front, or write 49 in front? If 6C is written in front, it is big endian. or write 49 in front, is little endian.
The word "endian" is derived from Gulliver's Travels. The civil war in the small country stems from eating eggs is whether from the Big Head (Big-endian) or from the head (Little-endian) knocked Open, which has happened six times rebellion, one of the Emperor sent life, the other lost the throne.
We generally translate endian into "byte order", the big endian and little endian are called "large tail" and "small tail".
File header:
The red box is the file header, the first 4 bytes is the start address of the index area, and the last 4 bytes is the end address of the index area.
As shown in the following:
Click to enlarge
Since the database is a byte library using the little endian, we need to turn it upside down.
Read the 0-3 bytes of the file header, and then use the unpack function to convert the binary data to the unsigned integer in the big endian format.
After processing, the start address location of the index area is: 00077450; the end address location of the index area is: 000ce17c.
If you have ultraedit software on hand, you can open the CoralWry.dat file and find the location of the address: 00077450, which is the beginning of the IP address index area.
As shown in the following:
Click to enlarge
The red box is where the index area begins.