(registration required). * IP2Location LiteĀ® is a registered trademark of Hexasoft Development Sdn Bhd. */ namespace Yusurko\UserAgent; use Yusurko\UserAgent\IpAddress; class IpLocator { protected $conn, $source_file; const SOURCE_FILE = '.err/data/IP2LOCATION-LITE-DB1.IPV6.CSV'; const LOCAL_RANGES = [ ["0", "1", '@'], ["281470681743360", "281470698520575", '+'], ["281470849515520", "281470866292735", "+"], ["281472812449792", "281472829227007", "@"], ["281473533739008", "281473533804543", "+"], ["281473568473088", "281473569521663", "+"], ["281473913978880", "281473914044415", "+"], ["338288524927261089654018896841347694592", "338288524927261089672465640915057246207", "+"] ]; public function __construct($conn, $source_file = self::SOURCE_FILE) { $this->conn = $conn; $this->source_file = $source_file; } public function locate($addr){ $addr = new IpAddress($addr); if (!$this->isDbLoaded()) { $up_to_range = $this->getDbLoadedUpto(); $this->loadCsvIntoDb($up_to_range); } $country = $this->lookupDb($addr); return new Country($country); } private function isDbLoaded() { $ex = $this->conn->prepare("SELECT 1 FROM af_ip2location WHERE range_end = x'ffffffffffffffffffffffffffffffff'"); $ex->execute(); $res = $ex->get_result(); return ($res->num_rows > 0); } private function getDbLoadedUpto(){ $ex = $this->conn->prepare("SELECT hex(range_end) FROM af_ip2location ORDER BY id DESC LIMIT 1"); $ex->execute(); $ex->store_result(); $ex->bind_result($range_end); $ex->fetch(); return IpAddress::fromHex($range_end); } private function loadCsvIntoDb($start = false) { $f = fopen($this->source_file, 'r'); while (($line = fgetcsv($f)) !== false) { $rs = new IpAddress($line[0]); $re = new IpAddress($line[1]); if ($rs->compare($start) <= 0) continue; try { $this->storeRangeIntoDb($rs, $re, $line[2]); } catch (\mysqli_sql_exception $ee) { return false; } } return true; } private function storeRangeIntoDb($range_start, $range_end, $country) { $range_start_x = $range_start->toFullHex(); $range_end_x = $range_end->toFullHex(); $ins = $this->conn->prepare('INSERT INTO af_ip2location (range_start, range_end, country) VALUES (unhex(?), unhex(?), ?)'); $ins->bind_param('sss', $range_start_x, $range_end_x, $country); $ins->execute(); $ins->store_result(); } private function lookupDb($addr) { // look up forbidden ranges first foreach (self::LOCAL_RANGES as $rang) { [ $rs, $re, $co ] = $rang; if ($addr->compare(new IpAddress($rs)) >= 0 && $addr->compare(new IpAddress($re)) <= 0) { return $co; } } $addr_x = $addr->toFullHex(); $st = $this->conn->prepare('SELECT country FROM af_ip2location WHERE range_end >= unhex(?) AND range_start <= unhex(?)'); $st->bind_param('ss', $addr_x, $addr_x); $st->execute(); $res = $st->get_result(); if ($res->num_rows < 1) { return false; } return $res->fetch_assoc()['country']; } }