fix(ip-geo): real pconline + amap sig/errors; clearer admin test failures
This commit is contained in:
@@ -311,26 +311,52 @@ class ExtensionController extends ControllerBase {
|
||||
$ok = !empty($result['country']) || !empty($result['province']) || !empty($result['city']);
|
||||
|
||||
if (!$ok) {
|
||||
$detail = trim((string) ($result['detail'] ?? ''));
|
||||
switch ($reason) {
|
||||
case 'private_ip':
|
||||
$message = __('该地址为私有/保留 IP,无法查询归属地。请换公网 IP 测试。', 'aksrt');
|
||||
$message = __('该地址为私有/保留 IP(如 127.0.0.1 / 内网),无法查询。请用公网 IP 测试,例如 114.114.114.114。', 'aksrt');
|
||||
break;
|
||||
case 'invalid_ip':
|
||||
$message = __('IP 格式无效。', 'aksrt');
|
||||
break;
|
||||
case 'missing_key':
|
||||
$message = __('当前服务商 AppKey 未填写,请在「扩展 → IP归属地」补全凭据。', 'aksrt');
|
||||
break;
|
||||
case 'provider_error':
|
||||
// e.g. INVALID_USER_KEY from Amap
|
||||
$message = $detail !== ''
|
||||
? sprintf(
|
||||
/* translators: %s: provider error code/message */
|
||||
__('服务商拒绝请求:%s。请检查 Key 是否开通「IP定位」且平台类型匹配(Web服务)。', 'aksrt'),
|
||||
$detail
|
||||
)
|
||||
: __('服务商返回错误,请检查 Key 权限与配额。', 'aksrt');
|
||||
break;
|
||||
case 'http_error':
|
||||
$message = $detail !== ''
|
||||
? sprintf(
|
||||
/* translators: %s: transport error */
|
||||
__('无法访问归属地接口:%s。请检查服务器出站网络/防火墙。', 'aksrt'),
|
||||
$detail
|
||||
)
|
||||
: __('无法访问归属地接口,请检查服务器出站网络。', 'aksrt');
|
||||
break;
|
||||
case 'provider_empty':
|
||||
if (!empty($status['missing_fields'])) {
|
||||
$message = sprintf(
|
||||
/* translators: %s: missing config field keys */
|
||||
__('当前服务商凭据不完整(缺少:%s),请先在「扩展 → IP归属地」补全配置。', 'aksrt'),
|
||||
__('当前服务商凭据不完整(缺少:%s)。', 'aksrt'),
|
||||
implode(', ', (array) $status['missing_fields'])
|
||||
);
|
||||
} else {
|
||||
$message = __('服务商未返回有效归属地,请检查凭据、网络出口或换一个 IP 重试。', 'aksrt');
|
||||
$message = __('服务商未返回有效归属地,请换公网 IP 或检查 Key 权限。', 'aksrt');
|
||||
}
|
||||
break;
|
||||
case 'disabled':
|
||||
$message = __('IP 归属地功能未启用。', 'aksrt');
|
||||
break;
|
||||
default:
|
||||
$message = __('未查询到归属地信息。', 'aksrt');
|
||||
$message = $detail !== '' ? $detail : __('未查询到归属地信息。', 'aksrt');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+319
-69
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* IP 归属地查询(腾讯 / 高德 / 太平洋 / 轮询)。
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
@@ -12,25 +14,31 @@ class IPGeo {
|
||||
private static $instance = null;
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) self::$instance = new self();
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询 IP 归属地
|
||||
* 查询 IP 归属地。
|
||||
*
|
||||
* @param string $ip IPv4 or IPv6
|
||||
* @param bool $force 管理端测试用:跳过「启用」开关与缓存,仍拒绝私有 IP
|
||||
* @return array{country: string, province: string, city: string, raw: array, reason?: string}
|
||||
* @param string $ip IPv4 / IPv6
|
||||
* @param bool $force 管理端测试:跳过启用开关与缓存;仍拒绝私有 IP
|
||||
* @return array{country:string,province:string,city:string,raw:array,reason?:string,provider?:string,http_code?:int,detail?:string}
|
||||
*/
|
||||
public function lookup(string $ip, bool $force = false): array {
|
||||
$config = $this->get_config();
|
||||
$ip = trim($ip);
|
||||
|
||||
if (!$force && empty($config['ip_geo_enabled'])) {
|
||||
return $this->empty_result('disabled');
|
||||
}
|
||||
|
||||
// 本地/私有 IP 跳过查询
|
||||
if ($ip === '' || !filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
return $this->empty_result('invalid_ip');
|
||||
}
|
||||
|
||||
if ($this->is_private_ip($ip)) {
|
||||
return $this->empty_result('private_ip');
|
||||
}
|
||||
@@ -47,7 +55,7 @@ class IPGeo {
|
||||
$provider = sanitize_key((string) ($config['ip_addr_sdk'] ?? 'null'));
|
||||
$result = $this->query_provider($provider, $ip, $config);
|
||||
|
||||
if (!empty($result['province']) || !empty($result['city']) || !empty($result['country'])) {
|
||||
if ($this->has_location($result)) {
|
||||
if (!$force) {
|
||||
set_transient($cache_key, $result, $ttl);
|
||||
}
|
||||
@@ -61,9 +69,6 @@ class IPGeo {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务运行时状态
|
||||
*/
|
||||
public function get_status(): array {
|
||||
$config = $this->get_config();
|
||||
$provider = sanitize_key((string) ($config['ip_addr_sdk'] ?? 'null'));
|
||||
@@ -71,12 +76,14 @@ class IPGeo {
|
||||
|
||||
$missing = [];
|
||||
if ($enabled && $provider === 'qq') {
|
||||
if (empty($config['ip_addr_sdk_qq_appkey'])) $missing[] = 'ip_addr_sdk_qq_appkey';
|
||||
if (empty($config['ip_addr_sdk_qq_secretkey'])) $missing[] = 'ip_addr_sdk_qq_secretkey';
|
||||
if (empty($config['ip_addr_sdk_qq_appkey'])) {
|
||||
$missing[] = 'ip_addr_sdk_qq_appkey';
|
||||
}
|
||||
}
|
||||
if ($enabled && $provider === 'amap') {
|
||||
if (empty($config['ip_addr_sdk_amap_appkey'])) $missing[] = 'ip_addr_sdk_amap_appkey';
|
||||
if (empty($config['ip_addr_sdk_amap_secretkey'])) $missing[] = 'ip_addr_sdk_amap_secretkey';
|
||||
if (empty($config['ip_addr_sdk_amap_appkey'])) {
|
||||
$missing[] = 'ip_addr_sdk_amap_appkey';
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
@@ -93,24 +100,25 @@ class IPGeo {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化归属地显示文本
|
||||
*/
|
||||
public function format(array $geo, string $format = ''): string {
|
||||
if ($format === '') {
|
||||
$config = $this->get_config();
|
||||
$format = (string) ($config['ip_geo_format'] ?? 'province_city');
|
||||
}
|
||||
|
||||
$country = $this->string_field($geo['country'] ?? '');
|
||||
$province = $this->string_field($geo['province'] ?? '');
|
||||
$city = $this->string_field($geo['city'] ?? '');
|
||||
|
||||
switch ($format) {
|
||||
case 'province':
|
||||
return $geo['province'] ?? '';
|
||||
return $province !== '' ? $province : $city;
|
||||
case 'country_province':
|
||||
return trim(($geo['country'] ?? '') . ' ' . ($geo['province'] ?? ''));
|
||||
return trim($country . ' ' . $province);
|
||||
case 'province_city':
|
||||
default:
|
||||
$parts = array_filter([$geo['province'] ?? '', $geo['city'] ?? '']);
|
||||
return implode(' ', $parts) ?: ($geo['country'] ?? '');
|
||||
$parts = array_filter([$province, $city]);
|
||||
return $parts ? implode(' ', $parts) : $country;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,90 +136,294 @@ class IPGeo {
|
||||
case 'amap':
|
||||
return $this->query_amap($ip, $config);
|
||||
case 'polling':
|
||||
$last = $this->empty_result('provider_empty');
|
||||
foreach (['qq', 'amap', 'null'] as $fallback) {
|
||||
// Skip providers that clearly lack keys.
|
||||
if ($fallback === 'qq' && empty($config['ip_addr_sdk_qq_appkey'])) {
|
||||
continue;
|
||||
}
|
||||
if ($fallback === 'amap' && empty($config['ip_addr_sdk_amap_appkey'])) {
|
||||
continue;
|
||||
}
|
||||
$result = $this->query_provider($fallback, $ip, $config);
|
||||
if (!empty($result['province']) || !empty($result['city']) || !empty($result['country'])) {
|
||||
if ($this->has_location($result)) {
|
||||
return $result;
|
||||
}
|
||||
$last = $result;
|
||||
}
|
||||
return $this->empty_result();
|
||||
return $last;
|
||||
case 'null':
|
||||
default:
|
||||
return $this->query_ip_api($ip);
|
||||
// Canonical free provider: 太平洋 (pconline). Label matches admin UI.
|
||||
return $this->query_pconline($ip);
|
||||
}
|
||||
}
|
||||
|
||||
private function query_qq(string $ip, array $config): array {
|
||||
$appkey = trim((string) ($config['ip_addr_sdk_qq_appkey'] ?? ''));
|
||||
$appkey = trim((string) ($config['ip_addr_sdk_qq_appkey'] ?? ''));
|
||||
$secretkey = trim((string) ($config['ip_addr_sdk_qq_secretkey'] ?? ''));
|
||||
if ($appkey === '') {
|
||||
return $this->empty_result();
|
||||
return $this->empty_result('missing_key', ['provider' => 'qq', 'detail' => 'missing appkey']);
|
||||
}
|
||||
|
||||
$url = add_query_arg([
|
||||
'ip' => $ip,
|
||||
'key' => $appkey,
|
||||
], 'https://apis.map.qq.com/ws/location/v1/ip');
|
||||
// Path used for Tencent signature (zibll parity).
|
||||
$path = '/ws/location/v1/ip';
|
||||
$query = 'ip=' . rawurlencode($ip) . '&key=' . rawurlencode($appkey);
|
||||
$url = 'https://apis.map.qq.com' . $path . '?' . $query;
|
||||
if ($secretkey !== '') {
|
||||
$url .= '&sig=' . md5($path . '?' . $query . $secretkey);
|
||||
}
|
||||
|
||||
$response = wp_remote_get($url, [
|
||||
'timeout' => 8,
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
]);
|
||||
if (is_wp_error($response)) return $this->empty_result();
|
||||
$http = $this->http_get($url, 8);
|
||||
if (!$http['ok']) {
|
||||
return $this->empty_result('http_error', [
|
||||
'provider' => 'qq',
|
||||
'http_code' => $http['code'],
|
||||
'detail' => $http['error'],
|
||||
'raw' => $http['body_raw'] !== '' ? ['body' => $http['body_raw']] : [],
|
||||
]);
|
||||
}
|
||||
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
if (!is_array($data) || (int) ($data['status'] ?? 1) !== 0) return $this->empty_result();
|
||||
$data = $http['json'];
|
||||
if (!is_array($data) || (int) ($data['status'] ?? 1) !== 0) {
|
||||
$msg = is_array($data) ? (string) ($data['message'] ?? 'status_error') : 'invalid_json';
|
||||
return $this->empty_result('provider_error', [
|
||||
'provider' => 'qq',
|
||||
'http_code' => $http['code'],
|
||||
'detail' => $msg,
|
||||
'raw' => is_array($data) ? $data : [],
|
||||
]);
|
||||
}
|
||||
|
||||
$info = $data['result']['ad_info'] ?? [];
|
||||
$info = is_array($data['result']['ad_info'] ?? null) ? $data['result']['ad_info'] : [];
|
||||
return [
|
||||
'country' => '中国',
|
||||
'province' => (string) ($info['province'] ?? ''),
|
||||
'city' => (string) ($info['city'] ?? ''),
|
||||
'country' => $this->string_field($info['nation'] ?? '中国'),
|
||||
'province' => $this->string_field($info['province'] ?? ''),
|
||||
'city' => $this->string_field($info['city'] ?? ''),
|
||||
'raw' => $data,
|
||||
'provider' => 'qq',
|
||||
];
|
||||
}
|
||||
|
||||
private function query_amap(string $ip, array $config): array {
|
||||
$appkey = trim((string) ($config['ip_addr_sdk_amap_appkey'] ?? ''));
|
||||
$appkey = trim((string) ($config['ip_addr_sdk_amap_appkey'] ?? ''));
|
||||
$secretkey = trim((string) ($config['ip_addr_sdk_amap_secretkey'] ?? ''));
|
||||
if ($appkey === '') {
|
||||
return $this->empty_result();
|
||||
return $this->empty_result('missing_key', [
|
||||
'provider' => 'amap',
|
||||
'detail' => 'missing appkey',
|
||||
]);
|
||||
}
|
||||
|
||||
$url = add_query_arg([
|
||||
'ip' => $ip,
|
||||
'key' => $appkey,
|
||||
], 'https://restapi.amap.com/v3/ip');
|
||||
// Amap sign: md5(sorted query without sig + secret) — zibll uses ip=&key= order.
|
||||
$base_query = 'ip=' . $ip . '&key=' . $appkey;
|
||||
$url = 'https://restapi.amap.com/v3/ip?' . $base_query;
|
||||
if ($secretkey !== '') {
|
||||
$url .= '&sig=' . md5($base_query . $secretkey);
|
||||
}
|
||||
|
||||
$response = wp_remote_get($url, [
|
||||
'timeout' => 8,
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
]);
|
||||
if (is_wp_error($response)) return $this->empty_result();
|
||||
$http = $this->http_get($url, 8);
|
||||
if (!$http['ok']) {
|
||||
return $this->empty_result('http_error', [
|
||||
'provider' => 'amap',
|
||||
'http_code' => $http['code'],
|
||||
'detail' => $http['error'],
|
||||
'raw' => $http['body_raw'] !== '' ? ['body' => $http['body_raw']] : [],
|
||||
]);
|
||||
}
|
||||
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
if (!is_array($data) || (string) ($data['status'] ?? '0') !== '1') return $this->empty_result();
|
||||
$data = $http['json'];
|
||||
if (!is_array($data) || (string) ($data['status'] ?? '0') !== '1') {
|
||||
$info = is_array($data) ? (string) ($data['info'] ?? 'status_error') : 'invalid_json';
|
||||
// Common: INVALID_USER_KEY / DAILY_QUERY_OVER_LIMIT / USERKEY_PLAT_NOMATCH
|
||||
return $this->empty_result('provider_error', [
|
||||
'provider' => 'amap',
|
||||
'http_code' => $http['code'],
|
||||
'detail' => $info,
|
||||
'raw' => is_array($data) ? $data : [],
|
||||
]);
|
||||
}
|
||||
|
||||
// Amap returns empty arrays for province/city on some IPs / overseas.
|
||||
$province = $this->string_field($data['province'] ?? '');
|
||||
$city = $this->string_field($data['city'] ?? '');
|
||||
$country = '中国';
|
||||
if ($province === '' && $city === '') {
|
||||
return $this->empty_result('provider_empty', [
|
||||
'provider' => 'amap',
|
||||
'detail' => 'empty province/city',
|
||||
'raw' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
'country' => '中国',
|
||||
'province' => (string) ($data['province'] ?? ''),
|
||||
'city' => (string) ($data['city'] ?? ''),
|
||||
'country' => $country,
|
||||
'province' => $province,
|
||||
'city' => $city,
|
||||
'raw' => $data,
|
||||
'provider' => 'amap',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 太平洋网络 whois(GB2312 正文)。
|
||||
*/
|
||||
private function query_pconline(string $ip): array {
|
||||
$url = 'http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=' . rawurlencode($ip);
|
||||
$http = $this->http_get($url, 8, [
|
||||
'Accept' => '*/*',
|
||||
'Accept-Language' => 'zh-CN,zh;q=0.9',
|
||||
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'Referer' => 'https://www.pconline.com.cn/',
|
||||
]);
|
||||
|
||||
if (!$http['ok']) {
|
||||
// Fallback: ip-api only when pconline network fails (not dual storage — runtime failover).
|
||||
$fallback = $this->query_ip_api($ip);
|
||||
if ($this->has_location($fallback)) {
|
||||
$fallback['provider'] = 'ip-api';
|
||||
$fallback['detail'] = 'pconline_failed_fallback_ip_api';
|
||||
return $fallback;
|
||||
}
|
||||
return $this->empty_result('http_error', [
|
||||
'provider' => 'pconline',
|
||||
'http_code' => $http['code'],
|
||||
'detail' => $http['error'] !== '' ? $http['error'] : 'pconline blocked or unreachable',
|
||||
'raw' => $http['body_raw'] !== '' ? ['body' => mb_substr($http['body_raw'], 0, 500)] : [],
|
||||
]);
|
||||
}
|
||||
|
||||
$body = $http['body_raw'];
|
||||
// pconline often serves GBK/GB2312.
|
||||
if (function_exists('mb_convert_encoding')) {
|
||||
$converted = @mb_convert_encoding($body, 'UTF-8', 'GBK,GB2312,UTF-8');
|
||||
if (is_string($converted) && $converted !== '') {
|
||||
$body = $converted;
|
||||
}
|
||||
}
|
||||
|
||||
$json = json_decode($body, true);
|
||||
if (is_array($json)) {
|
||||
$province = $this->string_field($json['pro'] ?? $json['province'] ?? '');
|
||||
$city = $this->string_field($json['city'] ?? '');
|
||||
$region = $this->string_field($json['region'] ?? '');
|
||||
$addr = $this->string_field($json['addr'] ?? '');
|
||||
$country = '';
|
||||
if ($addr !== '' && preg_match('/^(\S+)/u', $addr, $m)) {
|
||||
$country = $m[1];
|
||||
}
|
||||
if ($province !== '' || $city !== '') {
|
||||
return [
|
||||
'country' => $country !== '' ? $country : '中国',
|
||||
'province' => $province,
|
||||
'city' => $city !== '' ? $city : $region,
|
||||
'raw' => $json,
|
||||
'provider' => 'pconline',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Regex fallback (zibll).
|
||||
preg_match('/"pro":"(.*?)"/u', $body, $pro);
|
||||
preg_match('/"city":"(.*?)"/u', $body, $city);
|
||||
preg_match('/"region":"(.*?)"/u', $body, $region);
|
||||
preg_match('/"addr":"(.*?)(\s)(.*?)"/u', $body, $addr);
|
||||
|
||||
$province = $this->string_field($pro[1] ?? '');
|
||||
$city_s = $this->string_field($city[1] ?? '');
|
||||
$country = $this->string_field($addr[1] ?? '');
|
||||
|
||||
if ($province === '' && $city_s === '') {
|
||||
return $this->empty_result('provider_empty', [
|
||||
'provider' => 'pconline',
|
||||
'detail' => 'parse_failed',
|
||||
'raw' => ['body' => mb_substr($body, 0, 500)],
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
'country' => $country !== '' ? $country : '中国',
|
||||
'province' => $province,
|
||||
'city' => $city_s !== '' ? $city_s : $this->string_field($region[1] ?? ''),
|
||||
'raw' => ['body' => $body],
|
||||
'provider' => 'pconline',
|
||||
];
|
||||
}
|
||||
|
||||
private function query_ip_api(string $ip): array {
|
||||
$response = wp_remote_get("http://ip-api.com/json/{$ip}?lang=zh-CN", [
|
||||
'timeout' => 5,
|
||||
]);
|
||||
if (is_wp_error($response)) return $this->empty_result();
|
||||
$url = 'http://ip-api.com/json/' . rawurlencode($ip) . '?lang=zh-CN&fields=status,message,country,regionName,city,query';
|
||||
$http = $this->http_get($url, 6);
|
||||
if (!$http['ok']) {
|
||||
return $this->empty_result('http_error', [
|
||||
'provider' => 'ip-api',
|
||||
'http_code' => $http['code'],
|
||||
'detail' => $http['error'],
|
||||
]);
|
||||
}
|
||||
$data = $http['json'];
|
||||
if (!is_array($data) || ($data['status'] ?? '') !== 'success') {
|
||||
return $this->empty_result('provider_error', [
|
||||
'provider' => 'ip-api',
|
||||
'detail' => is_array($data) ? (string) ($data['message'] ?? 'fail') : 'invalid_json',
|
||||
'raw' => is_array($data) ? $data : [],
|
||||
]);
|
||||
}
|
||||
return [
|
||||
'country' => $this->string_field($data['country'] ?? ''),
|
||||
'province' => $this->string_field($data['regionName'] ?? ''),
|
||||
'city' => $this->string_field($data['city'] ?? ''),
|
||||
'raw' => $data,
|
||||
'provider' => 'ip-api',
|
||||
];
|
||||
}
|
||||
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
if (!is_array($data) || ($data['status'] ?? '') !== 'success') return $this->empty_result();
|
||||
/**
|
||||
* @param array<string,string> $extra_headers
|
||||
* @return array{ok:bool,code:int,error:string,json:?array,body_raw:string}
|
||||
*/
|
||||
private function http_get(string $url, int $timeout = 8, array $extra_headers = []): array {
|
||||
$headers = array_merge([
|
||||
'Accept' => 'application/json,text/plain,*/*',
|
||||
'User-Agent' => 'AKSRT-IPGeo/3.4 (+WordPress)',
|
||||
], $extra_headers);
|
||||
|
||||
$response = wp_remote_get($url, [
|
||||
'timeout' => $timeout,
|
||||
'redirection' => 3,
|
||||
'sslverify' => true,
|
||||
'headers' => $headers,
|
||||
]);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return [
|
||||
'ok' => false,
|
||||
'code' => 0,
|
||||
'error' => $response->get_error_message(),
|
||||
'json' => null,
|
||||
'body_raw' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$code = (int) wp_remote_retrieve_response_code($response);
|
||||
$body = (string) wp_remote_retrieve_body($response);
|
||||
if ($code < 200 || $code >= 300) {
|
||||
return [
|
||||
'ok' => false,
|
||||
'code' => $code,
|
||||
'error' => 'HTTP ' . $code,
|
||||
'json' => null,
|
||||
'body_raw' => $body,
|
||||
];
|
||||
}
|
||||
|
||||
$json = json_decode($body, true);
|
||||
|
||||
return [
|
||||
'country' => (string) ($data['country'] ?? ''),
|
||||
'province' => (string) ($data['regionName'] ?? ''),
|
||||
'city' => (string) ($data['city'] ?? ''),
|
||||
'raw' => $data,
|
||||
'ok' => true,
|
||||
'code' => $code,
|
||||
'error' => '',
|
||||
'json' => is_array($json) ? $json : null,
|
||||
'body_raw' => $body,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -219,11 +431,49 @@ class IPGeo {
|
||||
return !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
|
||||
}
|
||||
|
||||
private function empty_result(string $reason = ''): array {
|
||||
$out = ['country' => '', 'province' => '', 'city' => '', 'raw' => []];
|
||||
private function has_location(array $result): bool {
|
||||
return $this->string_field($result['province'] ?? '') !== ''
|
||||
|| $this->string_field($result['city'] ?? '') !== ''
|
||||
|| $this->string_field($result['country'] ?? '') !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Amap may return [] for empty province/city.
|
||||
*
|
||||
* @param mixed $value Raw field.
|
||||
*/
|
||||
private function string_field($value): string {
|
||||
if (is_array($value)) {
|
||||
if ($value === []) {
|
||||
return '';
|
||||
}
|
||||
$value = reset($value);
|
||||
}
|
||||
if (!is_scalar($value) && $value !== null) {
|
||||
return '';
|
||||
}
|
||||
return trim((string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $extra Extra fields.
|
||||
* @return array{country:string,province:string,city:string,raw:array,reason?:string}
|
||||
*/
|
||||
private function empty_result(string $reason = '', array $extra = []): array {
|
||||
$out = [
|
||||
'country' => '',
|
||||
'province' => '',
|
||||
'city' => '',
|
||||
'raw' => is_array($extra['raw'] ?? null) ? $extra['raw'] : [],
|
||||
];
|
||||
if ($reason !== '') {
|
||||
$out['reason'] = $reason;
|
||||
}
|
||||
foreach (['provider', 'http_code', 'detail'] as $k) {
|
||||
if (array_key_exists($k, $extra)) {
|
||||
$out[$k] = $extra[$k];
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user