Вход Регистрация
Файл: SxGeo.php
Строк: 349
<?php
/***************************************************************************
| Sypex Geo                  version 2.2.3                                  |
| (c)2006-2014 zapimir       zapimir@zapimir.net       http://sypex.net/    |
| (c)2006-2014 BINOVATOR     info@sypex.net                                 |
|---------------------------------------------------------------------------|
|     created: 2006.10.17 18:33              modified: 2014.06.20 18:57     |
|---------------------------------------------------------------------------|
| Sypex Geo is released under the terms of the BSD license                  |
|   http://sypex.net/bsd_license.txt                                        |
***************************************************************************/

define ('SXGEO_FILE'0);
define ('SXGEO_MEMORY'1);
define ('SXGEO_BATCH',  2);
class 
SxGeo {
    protected 
$fh;
    protected 
$ip1c;
    protected 
$info;
    protected 
$range;
    protected 
$db_begin;
    protected 
$b_idx_str;
    protected 
$m_idx_str;
    protected 
$b_idx_arr;
    protected 
$m_idx_arr;
    protected 
$m_idx_len;
    protected 
$db_items;
    protected 
$country_size;
    protected 
$db;
    protected 
$regions_db;
    protected 
$cities_db;

    public 
$id2iso = array(
        
'''AP''EU''AD''AE''AF''AG''AI''AL''AM''CW''AO''AQ''AR''AS''AT''AU',
        
'AW''AZ''BA''BB''BD''BE''BF''BG''BH''BI''BJ''BM''BN''BO''BR''BS',
        
'BT''BV''BW''BY''BZ''CA''CC''CD''CF''CG''CH''CI''CK''CL''CM''CN',
        
'CO''CR''CU''CV''CX''CY''CZ''DE''DJ''DK''DM''DO''DZ''EC''EE''EG',
        
'EH''ER''ES''ET''FI''FJ''FK''FM''FO''FR''SX''GA''GB''GD''GE''GF',
        
'GH''GI''GL''GM''GN''GP''GQ''GR''GS''GT''GU''GW''GY''HK''HM''HN',
        
'HR''HT''HU''ID''IE''IL''IN''IO''IQ''IR''IS''IT''JM''JO''JP''KE',
        
'KG''KH''KI''KM''KN''KP''KR''KW''KY''KZ''LA''LB''LC''LI''LK''LR',
        
'LS''LT''LU''LV''LY''MA''MC''MD''MG''MH''MK''ML''MM''MN''MO''MP',
        
'MQ''MR''MS''MT''MU''MV''MW''MX''MY''MZ''NA''NC''NE''NF''NG''NI',
        
'NL''NO''NP''NR''NU''NZ''OM''PA''PE''PF''PG''PH''PK''PL''PM''PN',
        
'PR''PS''PT''PW''PY''QA''RE''RO''RU''RW''SA''SB''SC''SD''SE''SG',
        
'SH''SI''SJ''SK''SL''SM''SN''SO''SR''ST''SV''SY''SZ''TC''TD''TF',
        
'TG''TH''TJ''TK''TM''TN''TO''TL''TR''TT''TV''TW''TZ''UA''UG''UM',
        
'US''UY''UZ''VA''VC''VE''VG''VI''VN''VU''WF''WS''YE''YT''RS''ZA',
        
'ZM''ME''ZW''A1''XK''O1''AX''GG''IM''JE''BL''MF''BQ''SS'
    
);

    public 
$batch_mode  false;
    public 
$memory_mode false;

    public function 
__construct($db_file 'SxGeo.dat'$type SXGEO_FILE){
        
$this->fh fopen($db_file'rb');
        
// Сначала убеждаемся, что есть файл базы данных
        
$header fread($this->fh40); // В версии 2.2 заголовок увеличился на 8 байт
        
if(substr($header03) != 'SxG') die("Can't open {$db_file}n");
        
$info unpack('Cver/Ntime/Ctype/Ccharset/Cb_idx_len/nm_idx_len/nrange/Ndb_items/Cid_len/nmax_region/nmax_city/Nregion_size/Ncity_size/nmax_country/Ncountry_size/npack_size'substr($header3));
        if(
$info['b_idx_len'] * $info['m_idx_len'] * $info['range'] * $info['db_items'] * $info['time'] * $info['id_len'] == 0) die("Wrong file format {$db_file}n");
        
$this->range       $info['range'];
        
$this->b_idx_len   $info['b_idx_len'];
        
$this->m_idx_len   $info['m_idx_len'];
        
$this->db_items    $info['db_items'];
        
$this->id_len      $info['id_len'];
        
$this->block_len   $this->id_len;
        
$this->max_region  $info['max_region'];
        
$this->max_city    $info['max_city'];
        
$this->max_country $info['max_country'];
        
$this->country_size$info['country_size'];
        
$this->batch_mode  $type SXGEO_BATCH;
        
$this->memory_mode $type SXGEO_MEMORY;
        
$this->pack        $info['pack_size'] ? explode(""fread($this->fh$info['pack_size'])) : '';
        
$this->b_idx_str   fread($this->fh$info['b_idx_len'] * 4);
        
$this->m_idx_str   fread($this->fh$info['m_idx_len'] * 4);

        
$this->db_begin ftell($this->fh);
        if (
$this->batch_mode) {
            
$this->b_idx_arr array_values(unpack("N*"$this->b_idx_str)); // Быстрее в 5 раз, чем с циклом
            
unset ($this->b_idx_str);
            
$this->m_idx_arr str_split($this->m_idx_str4); // Быстрее в 5 раз чем с циклом
            
unset ($this->m_idx_str);
        }
        if (
$this->memory_mode) {
            
$this->db  fread($this->fh$this->db_items $this->block_len);
            
$this->regions_db $info['region_size'] > fread($this->fh$info['region_size']) : '';
            
$this->cities_db  $info['city_size'] > fread($this->fh$info['city_size']) : '';
        }
        
$this->info $info;
        
$this->info['regions_begin'] = $this->db_begin $this->db_items $this->block_len;
        
$this->info['cities_begin']  = $this->info['regions_begin'] + $info['region_size'];
    }

    protected function 
search_idx($ipn$min$max){
        if(
$this->batch_mode){
            while(
$max $min 8){
                
$offset = ($min $max) >> 1;
                if (
$ipn $this->m_idx_arr[$offset]) $min $offset;
                else 
$max $offset;
            }
            while (
$ipn $this->m_idx_arr[$min] && $min++ < $max){};
        }
        else {
            while(
$max $min 8){
                
$offset = ($min $max) >> 1;
                if (
$ipn substr($this->m_idx_str$offset*44)) $min $offset;
                else 
$max $offset;
            }
            while (
$ipn substr($this->m_idx_str$min*44) && $min++ < $max){};
        }
        return  
$min;
    }

    protected function 
search_db($str$ipn$min$max){
        if(
$max $min 1) {
            
$ipn substr($ipn1);
            while(
$max $min 8){
                
$offset = ($min $max) >> 1;
                if (
$ipn substr($str$offset $this->block_len3)) $min $offset;
                else 
$max $offset;
            }
            while (
$ipn >= substr($str$min $this->block_len3) && ++$min $max){};
        }
        else {
            
$min++;
        }
        return 
hexdec(bin2hex(substr($str$min $this->block_len $this->id_len$this->id_len)));
    }

    public function 
get_num($ip){
        
$ip1n = (int)$ip// Первый байт
        
if($ip1n == || $ip1n == 10 || $ip1n == 127 || $ip1n >= $this->b_idx_len || false === ($ipn ip2long($ip))) return false;
        
$ipn pack('N'$ipn);
        
$this->ip1c chr($ip1n);
        
// Находим блок данных в индексе первых байт
        
if ($this->batch_mode){
            
$blocks = array('min' => $this->b_idx_arr[$ip1n-1], 'max' => $this->b_idx_arr[$ip1n]);
        }
        else {
            
$blocks unpack("Nmin/Nmax"substr($this->b_idx_str, ($ip1n 1) * 48));
        }
        if (
$blocks['max'] - $blocks['min'] > $this->range){
            
// Ищем блок в основном индексе
            
$part $this->search_idx($ipnfloor($blocks['min'] / $this->range), floor($blocks['max'] / $this->range)-1);
            
// Нашли номер блока в котором нужно искать IP, теперь находим нужный блок в БД
            
$min $part $part $this->range 0;
            
$max $part $this->m_idx_len $this->db_items : ($part+1) * $this->range;
            
// Нужно проверить чтобы блок не выходил за пределы блока первого байта
            
if($min $blocks['min']) $min $blocks['min'];
            if(
$max $blocks['max']) $max $blocks['max'];
        }
        else {
            
$min $blocks['min'];
            
$max $blocks['max'];
        }
        
$len $max $min;
        
// Находим нужный диапазон в БД
        
if ($this->memory_mode) {
            return 
$this->search_db($this->db$ipn$min$max);
        }
        else {
            
fseek($this->fh$this->db_begin $min $this->block_len);
            return 
$this->search_db(fread($this->fh$len $this->block_len), $ipn0$len);
        }
    }

    protected function 
readData($seek$max$type){
        
$raw '';
        if(
$seek && $max) {
            if (
$this->memory_mode) {
                
$raw substr($type == $this->regions_db $this->cities_db$seek$max);
            } else {
                
fseek($this->fh$this->info[$type == 'regions_begin' 'cities_begin'] + $seek);
                
$raw fread($this->fh$max);
            }
        }
        return 
$this->unpack($this->pack[$type], $raw);
    }

    protected function 
parseCity($seek$full false){
        if(!
$this->pack) return false;
        
$only_country false;
        if(
$seek $this->country_size){
            
$country $this->readData($seek$this->max_country0);
            
$city $this->unpack($this->pack[2]);
            
$city['lat'] = $country['lat'];
            
$city['lon'] = $country['lon'];
            
$only_country true;
        }
        else {
            
$city $this->readData($seek$this->max_city2);
            
$country = array('id' => $city['country_id'], 'iso' => $this->id2iso[$city['country_id']]);
            unset(
$city['country_id']);
        }
        if(
$full) {
            
$region $this->readData($city['region_seek'], $this->max_region1);
            if(!
$only_country$country $this->readData($region['country_seek'], $this->max_country0);
            unset(
$city['region_seek']);
            unset(
$region['country_seek']);
            return array(
'city' => $city'region' => $region'country' => $country);
        }
        else {
            unset(
$city['region_seek']);
            return array(
'city' => $city'country' => array('id' => $country['id'], 'iso' => $country['iso']));
        }
    }

    protected function 
unpack($pack$item ''){
        
$unpacked = array();
        
$empty = empty($item);
        
$pack explode('/'$pack);
        
$pos 0;
        foreach(
$pack AS $p){
            list(
$type$name) = explode(':'$p);
            
$type0 $type{0};
            if(
$empty) {
                
$unpacked[$name] = $type0 == 'b' || $type0 == 'c' '' 0;
                continue;
            }
            switch(
$type0){
                case 
't':
                case 
'T'$l 1; break;
                case 
's':
                case 
'n':
                case 
'S'$l 2; break;
                case 
'm':
                case 
'M'$l 3; break;
                case 
'd'$l 8; break;
                case 
'c'$l = (int)substr($type1); break;
                case 
'b'$l strpos($item""$pos)-$pos; break;
                default: 
$l 4;
            }
            
$val substr($item$pos$l);
            switch(
$type0){
                case 
't'$v unpack('c'$val); break;
                case 
'T'$v unpack('C'$val); break;
                case 
's'$v unpack('s'$val); break;
                case 
'S'$v unpack('S'$val); break;
                case 
'm'$v unpack('l'$val . (ord($val{2}) >> "xff" "")); break;
                case 
'M'$v unpack('L'$val ""); break;
                case 
'i'$v unpack('l'$val); break;
                case 
'I'$v unpack('L'$val); break;
                case 
'f'$v unpack('f'$val); break;
                case 
'd'$v unpack('d'$val); break;

                case 
'n'$v current(unpack('s'$val)) / pow(10$type{1}); break;
                case 
'N'$v current(unpack('l'$val)) / pow(10$type{1}); break;

                case 
'c'$v rtrim($val' '); break;
                case 
'b'$v $val$l++; break;
            }
            
$pos += $l;
            
$unpacked[$name] = is_array($v) ? current($v) : $v;
        }
        return 
$unpacked;
    }

    public function 
get($ip){
        return 
$this->max_city $this->getCity($ip) : $this->getCountry($ip);
    }
    public function 
getCountry($ip){
        if(
$this->max_city) {
            
$tmp $this->parseCity($this->get_num($ip));
            return 
$tmp['country']['iso'];
        }
        else return 
$this->id2iso[$this->get_num($ip)];
    }
    public function 
getCountryId($ip){
        if(
$this->max_city) {
            
$tmp $this->parseCity($this->get_num($ip));
            return 
$tmp['country']['id'];
        }
        else return 
$this->get_num($ip);
    }
    public function 
getCity($ip){
        
$seek $this->get_num($ip);
        return 
$seek $this->parseCity($seek) : false;
    }
    public function 
getCityFull($ip){
        
$seek $this->get_num($ip);
        return 
$seek $this->parseCity($seek1) : false;
    }
    public function 
about(){
        
$charset = array('utf-8''latin1''cp1251');
        
$types   = array('n/a''SxGeo Country''SxGeo City RU''SxGeo City EN''SxGeo City''SxGeo City Max RU''SxGeo City Max EN''SxGeo City Max');
        return array(
            
'Created' => date('Y.m.d'$this->info['time']),
            
'Timestamp' => $this->info['time'],
            
'Charset' => $charset[$this->info['charset']],
            
'Type' => $types[$this->info['type']],
            
'Byte Index' => $this->b_idx_len,
            
'Main Index' => $this->m_idx_len,
            
'Blocks In Index Item' => $this->range,
            
'IP Blocks' => $this->db_items,
            
'Block Size' => $this->block_len,
            
'City' => array(
                
'Max Length' => $this->max_city,
                
'Total Size' => $this->info['city_size'],
            ),
            
'Region' => array(
                
'Max Length' => $this->max_region,
                
'Total Size' => $this->info['region_size'],
            ),
            
'Country' => array(
                
'Max Length' => $this->max_country,
                
'Total Size' => $this->info['country_size'],
            ),
        );
    }
}
Онлайн: 0
Реклама