在開發面向多城市的網站時,根據訪客 IP 自動定位城市、並提供手動切換功能是常見需求。本文將詳細介紹兩種實現方案:一種基於純真 IP 庫(QQWry.dat)的完整 PHP 實現,另一種利用 JavaScript 配合 PHP 實現的輕量級跳轉方案。文中包含完整代碼示例,涵蓋 IP 獲取、城市匹配、Cookie 記憶、前端交互等關鍵環節。
<?php
include_once('./qqwry.php');
$QQWry = new QQWry;
/**
* 獲取客戶端真實 IP
*/
function get_real_ip() {
$ip = false;
if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
$ip = $_SERVER["HTTP_CLIENT_IP"];
}
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']);
if ($ip) {
array_unshift($ips, $ip);
$ip = FALSE;
}
for ($i = 0; $i < count($ips); $i++) {
if (!eregi("^(10|172\.16|192\.168)\.", $ips[$i])) {
$ip = $ips[$i];
break;
}
}
}
return $ip ?: $_SERVER['REMOTE_ADDR'];
}
/**
* 簡單 IP 格式驗證
*/
function is_ip($str) {
$ip = explode(".", $str);
if (count($ip) !== 4) return 0;
foreach ($ip as $ip_addr) {
if (!is_numeric($ip_addr) || $ip_addr < 0 || $ip_addr > 255) return 0;
}
return 1;
}
// 獲取 IP 並查詢城市
$ip = get_real_ip();
$QQWry->QQWry($ip);
$city = $QQWry->Country . $QQWry->Local;
// 針對南京市各區進行標準化映射
$cityMap = [
'玄武區' => '玄武區',
'仙林' => '仙林',
'秦淮區' => '秦淮區',
'江寧' => '江寧',
'鼓樓' => '鼓樓',
];
$matched = false;
foreach ($cityMap as $key => $val) {
if (strpos($city, $key) !== false) {
$city = $val;
$matched = true;
break;
}
}
if (!$matched) $city = '棲霞';
// 處理手動切換與 Cookie 記憶
if (isset($_GET['chengshi'])) {
setcookie("cookie_city", $_GET['chengshi'], time() + 86400);
$city = $_GET['chengshi'];
} elseif (isset($_COOKIE["cookie_city"])) {
$city = $_COOKIE["cookie_city"];
} else {
setcookie("cookie_city", $city, time() + 86400);
}
?>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8"/>
<link href="css/css.css" rel="stylesheet">
<script src="js/jquery-1.4.2.min.js"></script>
<title>城市切換演示</title>
</head>
<body>
<!-- 城市選擇彈層 -->
<div class="selCity" id="allCity" style="display: none">
<div class="area">
<table><tbody><tr>
<td><a class="cur" href="show.html">玄武區</a></td>
<td><a href="show1.html">仙林</a></td>
<td><a href="show2.html">秦淮區</a></td>
<td><a href="show3.html">鼓樓</a></td>
<td><a href="show4.html">江寧</a></td>
<td><a href="show5.html">棲霞區</a></td>
</tr></tbody></table>
<div class="none"><a id="foldin" href="javascript:;">全部</a></div>
</div>
</div>
<!-- 頂部導航 -->
<div class="header">
<div class="area">
<div class="r">
<div class="topCtiy clear">
<ul>
<li class="i1"><a href="index.php">全部小區</a></li>
<li class="i1"><?php echo $city; ?></li>
<li class="i2" id="changeCity">切換城市</li>
</ul>
</div>
</div>
</div>
</div>
<script>
jQuery(function($) {
$("#changeCity").click(function(e) {
$("#allCity").slideDown(300);
e.stopPropagation();
$(this).blur();
});
$("#allCity").click(function(e) { e.stopPropagation(); });
$(document).click(function(e) { if (e.button !== 2) $("#allCity").slideUp(300); });
$("#foldin").click(function() { $("#allCity").slideUp(300); });
});
</script>
</body>
</html>
此方案將 IP 解析邏輯封裝為可復用的 ipCity() 函數,配合 JavaScript 實現頁面級跳轉,適合多子域名或多獨立站點場景。
QQWry.dat 純真 IP 數據庫,放置於 ipcity/ 目錄。ipaddress.php,引入下方核心函數。<script src="/ipcity/ipaddress.php"></script>。<?php
function ipCity($userip) {
$dat_path = 'QQWry.dat';
if (!preg_match("/^([0-9]{1,3}\.){3}[0-9]{1,3}$/", $userip)) return 'IP Address Invalid';
if (!$fd = @fopen($dat_path, 'rb')) return 'IP data file not exists or access denied';
$ip = explode('.', $userip);
$ipNum = $ip[0]*16777216 + $ip[1]*65536 + $ip[2]*256 + $ip[3];
$DataBegin = fread($fd, 4); $DataEnd = fread($fd, 4);
$ipBegin = implode('', unpack('L', $DataBegin)); if ($ipBegin < 0) $ipBegin += pow(2,32);
$ipEnd = implode('', unpack('L', $DataEnd)); if ($ipEnd < 0) $ipEnd += pow(2,32);
$ipAllNum = ($ipEnd - $ipBegin) / 7 + 1;
$BeginNum = 0; $EndNum = $ipAllNum;
while ($ip1num > $ipNum || $ip2num < $ipNum) {
$Middle = intval(($EndNum + $BeginNum) / 2);
fseek($fd, $ipBegin + 7 * $Middle);
$ipData1 = fread($fd, 4);
if (strlen($ipData1) < 4) { fclose($fd); return 'File Error'; }
$ip1num = implode('', unpack('L', $ipData1)); if ($ip1num < 0) $ip1num += pow(2,32);
if ($ip1num > $ipNum) { $EndNum = $Middle; continue; }
$DataSeek = fread($fd, 3);
if (strlen($DataSeek) < 3) { fclose($fd); return 'File Error'; }
$DataSeek = implode('', unpack('L', $DataSeek.chr(0)));
fseek($fd, $DataSeek);
$ipData2 = fread($fd, 4);
if (strlen($ipData2) < 4) { fclose($fd); return 'File Error'; }
$ip2num = implode('', unpack('L', $ipData2)); if ($ip2num < 0) $ip2num += pow(2,32);
if ($ip2num < $ipNum) {
if ($Middle == $BeginNum) { fclose($fd); return 'No Data'; }
$BeginNum = $Middle;
}
}
// ... 後續讀取城市名稱邏輯(省略,完整代碼見原文) ...
fclose($fd);
return $cityName;
}
?>
var city = '<?php echo ipCity($xp_UserIp); ?>';
if (city.indexOf("上海市") >= 0) {
window.location.href = "https://shanghai.demo.com/";
}
// 可繼續擴展更多城市跳轉規則