支援單一 IP、通配符、範圍、列舉等多種格式:
192.168.1.1 單一 IP192.168.1.* 等同 192.168.1.1-255192.168.1.2-20 範圍192.168.1.[1|2|3] 列舉<?php
class BlockIP {
private $blockList = array("192.168.1.1", "210.10.2.1-20", "222.34.4.*");
public function __construct() {}
public function __destruct() {}
// 將單段轉為正則片段
private function makePregSegment($seg) {
if (strpos($seg, '-') !== false) {
list($from, $to) = explode('-', $seg);
$parts = array();
for ($i = $from; $i <= $to; $i++) {
$parts[] = $i;
}
return '(' . implode('|', $parts) . ')';
} elseif ($seg == '*') {
return '[0-9]{1,3}';
} else {
return $seg;
}
}
// 將完整 IP 規則轉為正則
private function ruleToRegex($rule) {
$segments = explode('.', $rule);
$regexSegs = array();
foreach ($segments as $seg) {
if (strpos($seg, '[') === 0) { // 已是列舉格式 [1|2|3]
$inner = trim($seg, '[]');
$options = explode('|', $inner);
$regexSegs[] = '(' . implode('|', $options) . ')';
} else {
$regexSegs[] = $this->makePregSegment($seg);
}
}
return '/^' . implode('\.', $regexSegs) . '$/';
}
// 建立所有封禁正則
private function getAllRegex() {
$regexes = array();
foreach ($this->blockList as $rule) {
$regexes[] = $this->ruleToRegex($rule);
}
return $regexes;
}
// 取得客戶端真實 IP
private function getClientIP() {
$headers = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR');
foreach ($headers as $h) {
$val = isset($_SERVER[$h]) ? $_SERVER[$h] : (function_exists('getenv') ? getenv($h) : '');
if ($val && strcasecmp($val, 'unknown') !== 0) {
// 可能包含多個 IP,取第一個
$ips = explode(',', $val);
return trim($ips[0]);
}
}
return 'unknown';
}
// 公開檢查方法
public function checkIP() {
$ip = $this->getClientIP();
if ($ip === 'unknown') return; // 無法判斷則放行
foreach ($this->getAllRegex() as $regex) {
if (preg_match($regex, $ip)) {
header("HTTP/1.1 403 Forbidden");
exit('IP Forbidden');
}
}
}
}
// 使用方式
$blocker = new BlockIP();
$blocker->checkIP();
?>
makePregIP 邏輯錯誤與變數未定義問題。[1|2|3] 列舉語法。X-Forwarded-For 多 IP 情況。192.168.1.0/24)。