【问题标题】:Storing IP in MySQL from PHP从 PHP 在 MySQL 中存储 IP
【发布时间】:2017-03-04 06:15:09
【问题描述】:

经过数小时的搜索,我只能找到“过时”和/或“不完整”的答案。 (显然它们早于 PDO。)我正在编写 PHP 代码(版本 7.0.15),并使用 MySQL 版本 5.7.17-0(都在 KUbuntu“虚拟机”上)。虽然我从事计算机工作超过 45 年,但我对 PHP 和 MySQL 还是很陌生。

我可以在 PHP 中获取访问者的 IP 地址。然后我想检查“try_ur_table”是否已经有一个条目,如果没有,则插入一个条目并查找它,这样我就可以在程序的其他部分使用“ur_index”。 ur_index 是 int(11),ur_ip 是 binary(16)。

问题是每次运行代码,第一次select都失败了,所以新建了一条,然后第二次select也找不到匹配的!

下面是相关代码sn-p:

  try
  {
    $pdc = new PDO('mysql:host=localhost;dbname=toy_database', 'xxxxx', 'xxxxx' );
    $pdc->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdc->exec('SET NAMES "utf8"');
  }
  catch (PDOException $e)
  {
    $output = 'Unable to connect tothe databaseserver. ' . $e->getMessage();
    include 'errout.html.php';
    exit();
  }
  // Find the caller data...
  if ( isset( $_SERVER[ 'REMOTE_ADDR' ] ) )
  {
    $cd = inet_pton( $_SERVER[ 'REMOTE_ADDR' ] );
    if ( $cd )
    {
      // inet_pton thinks it succeeded...
      try
      {
        $sql = "SELECT * FROM try_ur_table WHERE ur_ip = ? ";
        $rt = $pdc->prepare($sql);
        $rt->execute(array($cd));
        $u_list = $rt->fetchAll();
      }
      catch (PDOException $e)
      {
        $output = 'Problem looking for ur_ip.  ' .  $e->getMessage();
        include 'errout.html.php';
        exit();
      }
      if ( $u_list == NULL )
      {
        // New user!
        try
        {
          $sqm = "INSERT INTO try_ur_table SET ur_ip=?";
          $rs = $pdc->prepare($sqm);
          $rs->execute(array($cd));
        }
        catch (PDOException $e)
        {
          $output = 'Problem inserting new ur_ip. ' . $e->getMessage();
          include 'errout.html.php';
          exit();
        }
        // Now go find the new entry...
        try
        {
          $sql = "SELECT * FROM try_ur_table WHERE ur_ip = ? ";
          $rt = $pdc->prepare($sql);
          $rt->execute(array($cd));
          $u_list = $rt->fetchAll();
        }
        catch (PDOException $e)
        {
          $output = 'Problem looking for new ur_ip.  ' . $e->getMessage();
          include 'errout.html.php';
          exit();
        }
      } // $u_list == NULL
      // At this point there should be exactly one row in $u_list...
    } // $cd != false
  }
  else
  {
    // ! isset( $_SERVER[ 'REMOTE_ADDR' ]
    $cd = false;
  }

其他测试表明 $_SERVER[ 'REMOTE_ADDR' ] 返回 127.0.0.1(这是有道理的,因为这是“本地主机”)。但是,每次我运行上面的代码时,phpMyAdmin 都会说 ur_ip 是 0x7f00000001000000000000000000000 (希望我已经正确计算了零 - 似乎无法从 phpMyAdmin 复制和粘贴,但这是次要的)。另外,因为我有 ur_index,所以我尝试了基于它的选择,当我尝试通过 inet_ntop() 运行 ur_ip 时,我得到了垃圾,既不是有效的 IPv4 也不是 IPv6 地址。

【问题讨论】:

  • 当您插入一个新条目时,您不必返回并选择相同的数据。 PDO::lastInsertId 是你的朋友。
  • 45 年?你是说当他们还是蒸汽动力的时候? ;-)
  • 一般情况下,不需要“先检查”。事实上,这往往适得其反。您可以简单地提交 INSERT。如果 INSERT 失败,那么您知道 IP 已经列出。
  • @Strawberry 关于“蒸汽动力”,由于美国的大部分电力是使用蒸汽轮机(由煤、天然气或铀驱动)产生的,因此有人可以说所有我们的现代计算机中有一部分是“蒸汽驱动的”! ;-)

标签: php mysql pdo


【解决方案1】:

您的问题源于二进制类型。要匹配存储的值,您应该填充要比较的数据,如the documentation 所示。

或者您可以简单地使用 VARBINARY 来避免麻烦。

此外,您的代码非常重复,因此难以阅读和维护。异常的想法是您一次捕获它们。还有一些条件是无用的,你不应该使用 fetchAll() 而是使用 fetch method that is appropriate for your task

当然,尝试解码不是实际的二进制值,但是 phpmyadmin 显示的十六进制表示确实不会给您任何合理的结果。

以下是您被要求提供的最小、完整且可验证的示例。它代表了实际问题,只要提供正确的凭据,任何关心运行它的人都可以使用。

try {
    $pdc = new PDO('mysql:host=localhost;dbname=toy_database;charset=utf8', 'xxxxx', 'xxxxx' );
    $pdc->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // let's create a temporary table for the example purpose
    // change varbinary for binary and it goes astray
    $pdc->query("CREATE temporary TABLE try_ur_table 
                 (ur_index int primary key auto_increment,ur_ip varbinary(16))");

    $cd = inet_pton( $_SERVER["REMOTE_ADDR"] );

    $sql = "SELECT ur_index FROM try_ur_table WHERE ur_ip = ? ";
    $rt = $pdc->prepare($sql);
    $rt->execute(array($cd));
    $ur_index = $rt->fetchColumn();

    if ( !$ur_index )
    {
        $sqm = "INSERT INTO try_ur_table SET ur_ip=?";
        $pdc->prepare($sqm)->execute(array($cd));
        // here: that's all you need to get the index
        $ur_index = $pdc->lastInsertId();
    }

    // OK, let's verify
    $sql = "SELECT ur_ip FROM try_ur_table WHERE ur_ip = ? ";
    $rt = $pdc->prepare($sql);
    $rt->execute(array($cd));
    $ur_ip = $rt->fetchColumn();
    var_dump($ur_ip === $cd, bin2hex($ur_ip));

} catch (PDOException $e) {
    error_log($e);
    $output = ini_get('display_errors') ? $e : "There is a temporary problem. Try again later";
    include 'errout.html.php';
    exit();
}

【讨论】:

  • 谢谢!由于这仍然是“玩具”状态,我在桌子上做了一个“drop”并使用 ur_ip 作为 varbinary(16) 而不是 binary(16) 重新构建它,现在它可以工作了。
  • 因为它 INSISTS 在 5 分钟内输入 cmets (argh),我将在此处添加更多内容。我想我可能误导了一点:我尝试过 inet_ntop() 来自 SELECT * WHEN ur_index=1 的数据,而不是来自 phpMyAdmin 的手动剪切/粘贴。一个临时的 mod 显示 inet_ntop() 认为 ur_ip 内容是 127.0.0.1 这是正确的答案。也感谢剩下的 cmets。正如我所指出的,当谈到 MySQL/PHP 时,我是一个“新手”。
猜你喜欢
  • 2011-10-30
  • 2010-11-09
  • 2011-09-19
  • 2015-08-26
  • 1970-01-01
  • 2018-11-13
  • 2014-09-28
  • 2014-09-07
  • 2016-02-08
相关资源
最近更新 更多