php - 无法选择 ip=inet_pton($ip)

标签 php mysql pdo

我在数据库中有一个名为 ip

的唯一列

IP 地址在使用 PHP 函数转换后以 BINARY(16)(无排序规则)形式存储在此列中

$store_ip = inet_pton($ip);

当我尝试插入相同的 IP 两次时,它工作正常,但由于它是唯一的而失败,

但是当我尝试选择 IP 时它不起作用并且总是返回 FALSE(未找到)

<?php

try {
    $ip = inet_pton($_SERVER['REMOTE_ADDR']);
    $stmt = $db->prepare("SELECT * FROM `votes` WHERE ip=?");
    $stmt->execute([$ip]);
    $get = $stmt->fetch();

    if( ! $get){
        echo 'Not found';
    }else{
        echo 'Found';
    }

    // close connection
    $get = null;
    $stmt = null;

} catch (PDOException $e) {
    error_log($e->getMessage());
}

我插入IP的部分:

<?php

if( ! filter_var($ip, FILTER_VALIDATE_IP)){
        return FALSE;
}

$ip = inet_pton($_SERVER['REMOTE_ADDR']);

try {
    $stmt = $db->prepare("INSERT INTO votes(ip, answer) VALUES(?,?)");
    $stmt->execute([$ip, $answer]);
    $stmt = null;
} catch (PDOException $e) {
    return FALSE;
}

最佳答案

首先修复,这很简单: 如果要同时存储 IPv4 和 IPv6 地址, 您应该使用 VARBINARY(16) 而不是 BINARY(16)

现在问题来了:为什么 BINARY(16) 不能按预期工作?

假设我们有一个表 ips,其中只有一列 ip BINARY(16) PRIMARY KEY。 我们将默认的本地 IPv4 地址存储为

$stmt = $db->prepare("INSERT INTO ips(ip) VALUES(?)");
$stmt->execute([inet_pton('127.0.0.1')]);

并在数据库中找到以下值:

0x7F000001000000000000000000000000

如您所见 - 这是一个 4 字节二进制值 (0x7F000001) 右填充零以适应 16 字节的固定长度列。

当你现在尝试用

找到它时
$stmt = $db->prepare("SELECT * FROM ips WHERE ip = ?");
$stmt->execute([inet_pton('127.0.0.1')]);

会发生以下情况: PHP 发送值 0x7F000001 作为参数,然后进行比较 存储值 0x7F000001000000000000000000000000。 但是由于两个不同长度的二进制值永远不相等, WHERE 条件将始终返回 FALSE。 你可以试试

SELECT 0x00 = 0x0000

这将返回 0 (FALSE)。

注意:固定长度的非二进制字符串 (CHAR(N)) 的行为是不同的。

我们可以使用显式转换作为解决方法:

$stmt = $db->prepare("SELECT * FROM ips WHERE ip = CAST(? as BINARY(16))");
$stmt->execute([inet_pton('127.0.0.1')]);

它会找到该行。但是如果我们看看我们得到了什么

var_dump(inet_ntop($stmt->fetch(PDO::FETCH_OBJ)->ip));

我们会看到

string(8) "7f00:1::"

但这不是(真的)我们试图存储的内容。 当我们现在尝试存储 7f00:1:: 时, 我们会得到一个重复键错误, 虽然我们还没有存储任何 IPv6 地址。

再一次:使用 VARBINARY(16),您可以保持您的代码不受影响。 如果您存储许多 IPv4 地址,您甚至可以节省一些存储空间。

关于php - 无法选择 ip=inet_pton($ip),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55347251/

相关文章:

php - 根据浏览器检测更改PHP包含

java - hibernate 外键插入不起作用

Php pdo 和 offset=0

php - 如何在php中使用PDO transactino插入后获取插入ID

php - 如何在通过.htaccess 重定向 404 时获取 `$_POST` 查询?

javascript - 在新窗口中使用 Highcharts 绘制图表

php - xcode ios 将 nsarray 对象拆分为两个单独的对象

mysql - 非规范化建议

php - 将计算从 MySQL 转移到 PHP 是不可取的吗?

php - Pdo多行插入问题