PHP调用ipinfo.io报Trying to get property 'country'非对象错误
错误触发原因
你遇到的Trying to get property 'country' of non-object报错,核心原因是json_decode(curl_exec($curlSession))没有返回预期的标准对象,属于典型的接口调用无容错导致的偶发问题,具体触发场景包括:
- IP获取逻辑缺陷:
HTTP_X_FORWARDED_FOR字段在经过多层代理时会返回逗号分隔的IP列表,直接把整段字符串拼到接口请求URL中,会导致接口返回参数错误,无法解析出合法JSON - Curl请求异常:偶发的网络波动、DNS解析失败、接口临时不可用、请求超时都会导致
curl_exec返回false,传入json_decode后得到null而非预期对象 - 接口返回非预期内容:触发免费接口调用频率限制时会返回429状态码的纯文本错误,请求内网/保留IP时接口会返回错误结构,未开启跟随跳转时HTTP请求会被强制跳转到HTTPS返回3xx响应,这些场景下
json_decode都无法解析出带country字段的对象
修复方案
1. 修正IP获取逻辑
不要直接返回代理头的原始值,拆分多IP场景取第一个真实公网IP,过滤内网保留段,避免无效请求:
function getip() { $ip = '0.0.0.0'; if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); foreach ($ipList as $tmpIp) { $tmpIp = trim($tmpIp); if (filter_var($tmpIp, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { $ip = $tmpIp; break; } } } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) { $tmpIp = trim($_SERVER['HTTP_CLIENT_IP']); if (filter_var($tmpIp, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { $ip = $tmpIp; } } elseif (!empty($_SERVER['REMOTE_ADDR'])) { $ip = trim($_SERVER['REMOTE_ADDR']); } if ($ip == '0.0.0.0') { if (getenv('HTTP_X_FORWARDED_FOR')) { $ipList = explode(',', getenv('HTTP_X_FORWARDED_FOR')); foreach ($ipList as $tmpIp) { $tmpIp = trim($tmpIp); if (filter_var($tmpIp, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { $ip = $tmpIp; break; } } } elseif (getenv('HTTP_CLIENT_IP')) { $tmpIp = trim(getenv('HTTP_CLIENT_IP')); if (filter_var($tmpIp, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { $ip = $tmpIp; } } elseif (getenv('REMOTE_ADDR')) { $ip = trim(getenv('REMOTE_ADDR')); } } return $ip; }
2. 给接口调用增加全链路容错
改用HTTPS请求,配置合理超时、跳转跟随,每一步返回值都做合法性校验,设置默认国家兜底,避免报错和页面加载阻塞:
function getCountry(){ $ip = getip(); // 按业务默认站点适配的国家填写,比如默认中文站填CN $defaultCountry = 'CN'; if ($ip == '0.0.0.0') { return $defaultCountry; } $url="https://ipinfo.io/{$ip}/json"; $curlSession = curl_init(); curl_setopt($curlSession, CURLOPT_URL, $url); curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, true); curl_setopt($curlSession, CURLOPT_FOLLOWLOCATION, true); curl_setopt($curlSession, CURLOPT_CONNECTTIMEOUT, 2); curl_setopt($curlSession, CURLOPT_TIMEOUT, 3); curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($curlSession); $httpCode = curl_getinfo($curlSession, CURLINFO_HTTP_CODE); curl_close($curlSession); if ($response === false || $httpCode != 200) { return $defaultCountry; } $jsonData = json_decode($response); if (!is_object($jsonData) || !isset($jsonData->country)) { return $defaultCountry; } return $jsonData->country; }
生产环境可选优化
- 增加本地缓存:IP归属地变动频率极低,可以把IP和国家的映射存在本地文件/缓存中,缓存有效期设7天,减少接口请求量,避免触发免费额度限制,同时加快页面加载速度
- 结果写入Cookie:识别到用户所属国家后写入有效期30天的Cookie,后续访问直接读取Cookie值,不用重复调用接口
注意:免费版IP归属地接口有每日请求配额限制,站点流量较大时需要提前评估额度,避免超限后所有用户都走默认跳转逻辑。
内容的提问来源于stack exchange,提问作者zvon
相关产品推荐
相关产品推荐

