<?php /** * ip2address.php * IP地址转换地理位置类 * Create By Deepseath * $Author$ * $Id$ */ namespace Org\Net; use Think\Log; class Ip2address { /** * 返回的结果 */ public $result = array( 'address' => '', // 完整地址信息字符串 'address_list' => array(), // 地址信息数组 'source' => '' ); // 接口获取到的原始数据 /** * 错误编码 */ public $errcode = 0; /** * 错误描述 */ public $errmsg = ''; /** * 淘宝ip库接口url */ const URI_TAOBAO = 'http://ip.taobao.com/service/getIpInfo.php?ip=%s'; /** * 首选使用的服务:local=本地ip库、taobao=淘宝ip库 */ protected $_first_service = 'taobao'; /** * 待查询的IP地址 */ protected $_ip = ''; /** * 请求的url */ protected $__request_url = ''; /** * Snoopy类 */ protected $__snoopy = null; public function __construct($first_service = 'taobao') { $this->_first_service = $first_service; } /** * 获取IP所在地 * * @param string $ip * @return boolean */ public function get($ip) { $this->_ip = $ip; $validator = new \Com\Validator(); if (! $validator->is_ip($this->_ip)) { return false; } // 多个获取方式 $get_methods = array( 'local', 'taobao' ); // 首选获取服务方法 $method = '_get_by_' . $this->_first_service; // 临时的错误代码和错误消息 $errcode = 0; $errmsg = ''; $result = ''; // 使用首选服务出错,则尝试其他服务 if (! $this->$method()) { // 将首选服务的错误信息输出 $errcode = $this->errcode; $errmsg = $this->errmsg; // 遍历其他方法获取 foreach ($get_methods as $_method) { if (rstrtolower($_method) == rstrtolower($this->_first_service)) { // 由于已过使用首选方法,则忽略 continue; } $method = '_get_by_' . $_method; if ($this->$method()) { // 获取成功则退出 break; } } } // 输出首选服务的错误 if ($errcode) { $this->errcode = $errcode; $this->errmsg = $errmsg; } return true; } /** * TODO 本地解析方式尚未实现 * 通过本地纯真库获取IP所在地位置 * * @return boolean */ protected function _get_by_local() { $this->errcode = 100; $this->errmsg = '尚未实现本地化'; return false; } /** * 通过淘宝IP API接口获取地址 * * @return boolean */ protected function _get_by_taobao() { // 将ip地址变量赋值 $url = sprintf(self::URI_TAOBAO, $this->_ip); // 初始化结果 $sdata = array(); if (! $this->_get_json_by_httprequest($sdata, $url)) { $this->errcode = 1002; $this->errmsg = '解析TAOBAO数据发生错误'; // Log::record('taobao ip api error: '.$url.'|'.$this->errcode.'|'.$this->errmsg); Log::record('taobao ip api error: ' . $url . '|' . $this->errcode . '|' . $this->errmsg); return false; } // 无法获取结果集中的返回码,可能该接口发生变动 if (! isset($sdata['code'])) { $this->errcode = '1003'; $this->errmsg = '获取 IP 位置接口错误'; Log::record('taobao ip api error: ' . $url . '|' . $this->errcode . '|' . $this->errmsg); return false; } // 结果集中返回码不为空,则获取地址发生解析错误 if (! empty($sdata['code'])) { $this->errcode = '1002'; $this->errmsg = '获取 IP 位置信息发生错误(code:' . $sdata['code'] . ')'; Log::record('tobao ip api error:' . $url . '|' . $this->errcode . '|' . $this->errmsg . '|' . $sdata['code'] . '|' . $sdata['data']); return false; } // 如果结果集中数据为空,则可能出现意外的错误 if (empty($sdata['data'])) { $this->errcode = 1004; $this->errmsg = '获取 IP 位置信息发生错误'; Log::record('taobao ip api error:' . $url . '|' . $this->errcode . '|' . $this->errmsg); return false; } // 地址信息原始结果数组 $data = $sdata['data']; // 地址信息数组 $address_list = array( 'country' => '', 'region' => '', 'city' => '', 'county' => '' ); // 地址信息字符串 $address = ''; // 国家 if (! empty($data['country']) && is_scalar($data['country'])) { $address_list['country'] = trim($data['country']); if (! isset($data['country_id']) || rstrtolower($data['country_id']) != 'cn') { // 不显示“中国” $address .= $address_list['country']; } } // 省份 if (! empty($data['region']) && is_scalar($data['region'])) { $address_list['region'] = trim($data['region']); $address .= $address_list['region']; } // 城市 if (! empty($data['city']) && is_scalar($data['city'])) { $address_list['city'] = trim($data['city']); // 省份与城市不一致则显示,主要用于直辖市不重复显示 if ($address_list['city'] != $address_list['region']) { $address .= $address_list['city']; } } // 县 if (! empty($data['county']) && is_scalar($data['county'])) { $address_list['county'] = trim($data['county']); $address .= $address_list['county']; } // 输出结果 $this->result = array( 'address' => $address, 'address_list' => $address_list, 'source' => $data ); return true; } /** * 通过http请求获取json结果数据 * * @param string $data * <strong style="color:red">(引用结果)</strong>返回的结果 * @param string $url * 请求的url * @param string $post * 请求的数据 * @param array $http_header * 请求的header头信息 * @param string $http_method * 请求方法 * @param object $snoopy_reporting * <strong style="color:red">(引用结果)</strong>snoopy信息 * @return boolean */ protected function _get_json_by_httprequest(&$data, $url, $post = array(), $http_header = array(), $http_method = 'GET', &$snoopy_reporting = array()) { // 初始化结果数据 $data = ''; // 通过网络读取数据 $http_data = ''; if ($this->__httprequest($http_data, $url, $post, $http_header, $http_method, $snoopy_reporting) === false) { return false; } // 尝试解析json $data = @json_decode(trim($http_data), true); // 解析失败 if ($data === null) { $this->errcode = 1003; $this->errmsg = '解析JSON数据发生错误'; Log::record('json parse error: ' . $this->__request_url . '|' . $http_data . '|' . $this->errcode . '|' . $this->errmsg); return false; } return true; } /** * http请求私有方法 * * @param string $data * <strong style="color:red">(引用结果)</strong>请求结果 * @param string $url * 请求的url * @param string $post * 请求的数据 * @param array $http_header * 请求的header头信息 * @param string $http_method * 请求方法 * @param object $snoopy_reporting * <strong style="color:red">(引用结果)</strong>snoopy信息 * @return boolean */ protected function __httprequest(&$data, $url, $post = array(), $http_header = array(), $http_method = 'GET', &$snoopy_reporting = array()) { // 初始化结果数据 $data = ''; $this->__request_url = $url; // 载入 Snoopy 类 if ($this->__snoopy === null) { $this->__snoopy = new \Org\Net\Snoopy(); } // 使用自定义的头字段,格式为 array(字段名 => 值, ... ...) if (! empty($http_header) && is_array($http_header)) { $this->__snoopy->rawheaders = $http_header; } switch (rstrtoupper($http_method)) { case 'POST': // 使用 POST 协议 case 'PUT': // 使用 PUT 协议 $result = $this->__snoopy->submit($url, $post); break; case 'DELETE': // 使用 DELETE 协议 $result = $this->__snoopy->submit_by_delete($url, $post); break; default: // 使用 GET 协议 if ($post) { if (is_array($post)) { $get_data = http_build_query($post); } else { $get_data = $post; } if (strpos($url, '?') === false) { $url .= '?'; } else { $url .= '&'; } $url .= $get_data; } $result = $this->__snoopy->fetch($url); } // 将Snoopy信息返回,用于调试 $snoopy_reporting = $this->__snoopy; // 如果读取错误 if (! $result || 200 != $this->__snoopy->status) { $this->errcode = 1004; $this->errmsg = '请求接口发生网络错误'; Log::record('$snoopy->submit error: ' . $url . '|' . $result . '|' . $this->__snoopy->status . '|' . $this->errcode . '|' . $this->errmsg); return false; } // 返回结果数据 $data = $this->__snoopy->results; return true; } }