17370845950

php获取本机ip用preg_match提取_php正则取ip实例【技巧】
获取本机IP不能用$_SERVER['REMOTE_ADDR'](返回客户端IP),应通过系统命令如ip addr show或ipconfig结合正则提取,并用filter_var校验排除私有/保留地址。

PHP 获取本机 IP 不能靠 $_SERVER['REMOTE_ADDR']

想拿服务器自己的公网或内网 IP,却误用了 $_SERVER['REMOTE_ADDR']——它返回的是客户端(比如浏览器)的 IP,不是 PHP 所在机器的。真要取本机 IP,得走系统命令或网络接口查询,正则只是后续清洗手段,不是源头。

常见错误现象:$_SERVER['SERVER_ADDR'] 看似合理,但它可能返回 127.0.0.1(尤其 Nginx + PHP-FPM 反向代理时),或绑定在 localhost 的任意地址,不可靠。

  • gethostbyname(gethostname()) 最简,但只返回首个 A 记录,可能仍是 127.0.0.1
  • 更稳的方式是执行 ip addr show(Linux)或 ipconfig(Windows),再用正则提取非回环、非空链路本地的 IPv4
  • 注意:PHP 需开启 shell_exec,且 Web 服务器用户要有对应命令权限

preg_match 提取 IPv4 地址的可靠正则模式

别用网上泛滥的 /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/——它会匹配 999.999.999.999 这种非法 IP,也抓不到 CIDR 掩码后的地址(如 192.168.1.10/24)。

真正实用的模式是:

/(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\/\d{1,2})?/

这个正则做了三件事:

  • 每段数字严格限制在 0–255 范围内
  • 允许末尾带 CIDR(如 /24),方便区分子网
  • 不捕获前后空白或冒号,避免混入 inet6 行里的 IPv6 或端口

从 ip addr show 输出中过滤出有效 IPv4(排除 127.0.0.0/8 和 169.254.0.0/16)

Linux 下 ip addr show 输出含多行 inet,但包括 loopback、link-local、Docker bridge 等无效地址。光靠正则不够,得结合上下文过滤。

实操建议:

  • 先用 shell_exec('ip -4 addr show | grep "inet "') 只取 IPv4 行
  • 逐行用 preg_match() 提取 IP,再用 filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) 排除私有网段和保留地址
  • 若需优先选“主网卡”,可限定接口名,例如 ip -4 addr show eth0ens33,避免取到虚拟网卡

示例片段:

$output = shell_exec('ip -4 addr show | grep "inet "');
$ips = [];
if ($output) {
    foreach (explode("\n", trim($output)) as $line) {
        if (preg_match('/inet\s+([0-9.]+)\/\d+/i', $line, $m)) {
            $ip = $m[1];
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                $ips[] = $ip;
            }
        }
    }
}

Windows 下 ipconfig 的适配要点

Windows 的 ipconfig 输出格式不统一:中文系统带“IPv4 地址”,英文系统是 “IPv4 Address”,还夹杂换行和空格。直接硬匹配容易断裂。

关键处理点:

  • shell_exec('ipconfig | findstr /R "IPv4.*:"')(英文)或 findstr "IPv4"(中文)缩小范围
  • 正则改用宽松匹配:/IPv4[\s\S]*?:\s*([0-9.]+)/i,容忍中间任意空白和换行
  • 必须做二次校验:filter_var($ip, FILTER_VALIDATE_IP),因为 ipconf

    ig
    可能输出 0.0.0.0(未获取到 DHCP)
  • 注意:IIS 或某些安全策略会禁用 shell_exec,此时只能退回到 gethostbyname(gethostname()) 并接受其局限性

复杂点在于,没有一个函数能跨平台、无条件返回“正确”的本机 IP;你得根据部署环境(容器?云主机?WAMP?)、权限、是否代理,组合判断。正则只是工具链里最末端的一环,前置逻辑比匹配本身更重要。