65.9K
CodeProject 正在变化。 阅读更多。
Home

获取在线用户数及其他功能

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (12投票s)

2013年3月12日

CPOL

7分钟阅读

viewsIcon

57329

downloadIcon

2093

使用 PHP 和 MySQL 获取用户是否在线、当前在线用户数量、历史最高在线用户数、今日在线用户数量以及用户的“上次活动时间”。

引言

了解您的网站统计数据是追踪网站增长的一个非常重要的因素。此类统计信息在论坛、社区和社交网络中更明显的用途是,知道以下信息至关重要:

  • 用户是否在线(在实时网络聊天中很有用)
  • 当前在线用户数量
  • 历史最高在线用户数(带日期和时间)
  • 当天在线用户数量
  • 用户“上次活动时间”的日期和时间

本文介绍了如何使用 PHP 和 MySQL 实现这些功能。使用 PHP 和 MySQL 实现此功能非常简单明了(请相信我 Smile)。您可以获取本文随附的源文件,其中包含两个文件:checker.php(后端代码)和 check.php(包含 HTML、PHP 和 AJAX,用于异步向服务器 ping checker.php)。

在各种浏览器和标签页上测试此功能,以模拟多个用户。关闭一些标签页和浏览器以查看效果。

设置  

您的 MySQL 服务器上需要一个名为 online_users 的数据库。此数据库中需要两张表:onlinehighest。表 online 应包含两列:列 id 可以是 INTVARCHAR,具体取决于您网站上用户 ID 的处理方式,但为了本示例的说明,它将是 VARCHAR(10)。表 online 中的第二列应是 time,它应为 TIMESTAMP。 

第二张表 highest 应包含两列;列 time,它是一个默认为服务器当前时间的 TIMESTAMP,以及列 num,它是一个 INT

您可以使用 phpMyAdmin 手动设置这些,或使用以下代码(假设您的数据库名称为“online_user”并且您已使用变量 $connection 连接到 MySQL 服务器)。

<?php
.
.
.
//already connected to MySQL using $connection
//To create database
$sql = “CREATE DATABASE online_users”;
mysql_query($sql, $connection);
 
//To create tables
$sql = “CREATE TABLE online_users.online (id VARCHAR( 10 ) NOT NULL ,
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE = InnoDB”;
mysql_query($sql, $connection);
 
$sql = “CREATE TABLE online_users.highest (time TIMESTAMP NOT NULL 
              DEFAULT CURRENT_TIMESTAMP , num INT NOT NULL ) ENGINE = InnoDB”;
mysql_query($sql, $connection);
.
.
.
?>  

注意,您必须在本文包含的源文件可以工作之前创建这些数据库和表。数据库和表创建的这一部分已从源代码中删除,以使其保持干净、清晰和直截了当。 

检查器检查 

checker.php 包含后端代码,但在使用之前,您必须将 $db_host$db_username$db_password$db_name 变量分别更改为您的 MySQL 主机、用户名、密码和数据库名称。 

挑战 1 

实现我们目标的第一步是将网站上每个用户的 ID 和当前时间记录到数据库的“online”表中,并确保只要用户仍在浏览器中打开网站,此信息就会更新。如果实现了这一点,以下问题将同时解决。

解决方案 1: 

  • 获取用户是否在线:通过检查用户行上次更新的时间,您可以判断用户是否仍在线。例如,如果您将行设置为每 30 秒更新一次,并且上次更新时间是 1 分钟或更久以前,您可以正确地说用户不再在线。
  • 获取在线用户数量:如果您选择将用户视为离线,如果行上次更新时间是 1 分钟或更久以前,那么通过获取 1 分钟以内更新的行数,您可以得到在线用户数量。
     
  • 上次活动时间:通过检查用户行上次更新的时间,您可以确定他是否在线或“上次活动时间”。  
  • 今日在线用户数量:通过获取当天已更新的行数,您可以得到当天在线的用户数量。

挑战 2

第二个挑战(也是最后一个,因为解决方案 1 解决了 5 个中的 4 个)是如何获取历史最高在线用户数。当然,这将在“某个时间点”发生。乍一看,这可能看起来很吓人,当您认为一个脚本可能需要一直在您的服务器上运行,以便每毫秒获取在线用户数量时。但这并不是解决方案,所以放松!

解决方案 2: 

  • 历史最高在线:要拥有历史最高在线,您必须有在线用户!这可能乍一看像是一句废话,但它是真的。那么,为什么不将检查“历史最高在线”的责任放在在线用户身上,而不是让 Cron Jobs 在您的服务器上永久运行一个脚本呢?如果没有在线用户,就没有必要检查历史最高在线。

当用户报告其存在时,将检查在线用户数量并与“highest”表的“num”列进行比较。如果该数量大于或等于“num”,则将当前时间和数量 INSERT INTOUPDATE 到表中。

ping 检查

check.php 文件包含在前端运行的 PHP 代码、AJAX 脚本和 HTML。这就像您的网页一样,即用户将看到的内容。AJAX 用于每 30 秒异步与服务器通信,以报告用户与其他用户的存在。

代码和解释

我们的五个挑战中有四个可以通过页面简单地向服务器报告其存在来解决,例如每 30 秒一次,服务器会相应地更新“online”表中的行。

  • 报告用户存在
  • .
    .
    .
    //Report your presence
    if(isset($_GET[report]))
    {
        $query = "SELECT * FROM ". $db_name .".online WHERE id = '%s'";
        $query = sprintf($query, mysql_real_escape_string(stripslashes($_GET[id])));
        if(mysql_num_rows(mysql_query($query, $con)) > 0)
        {
            $query = "UPDATE ". $db_name .".online SET time = '%s' WHERE id = '%s'";
            $now = date('Y-m-d H:i:s', strtotime('now'));
            $query = sprintf($query, $now, mysql_real_escape_string(stripslashes($_GET[id])));
            mysql_query($query, $con);echo $query;
        }
        else
        {
            $query = "INSERT INTO ". $db_name .".online (id, time) VALUES ('%s', '%s')";
            $now = date('Y-m-d H:i:s', strtotime('now'));
            $query = sprintf($query, mysql_real_escape_string(stripslashes($_GET[id])), $now);
            mysql_query($query, $con);
        }
        //Set highest online info
        setHighestOnline($con, $db_name);
    }
    .
    .
    .

    这段代码位于后端(checker.php),并检查 $_GET[report] 是否 set()。如果为 true,则表示一个页面正在报告用户的存在。然后,代码检查用户是否已注册到“online”表中。如果为 true(即查询返回的行数大于零),则 UPDATE 该行,否则将信息 INSERT INTO 表中。

    请记住,在解决方案 2 中,指出每当报告用户存在时,都应检查在线用户数量并适当处理,这就是 setHighestOnline() 函数的原因。该函数将在后面讨论。

    用户使用以下 AJAX 代码从 check.php 报告存在

    .
    .
    .
    function doReport()
    {
    k = getXMLHttpRequestObject();
        if(k != false)
        {
            url = "checker.php?report&id=" + 
                         document.getElementById("userID").value;
            k.open("POST", url, true);
            k.onreadystatechange=function()
            {
    if(k.readyState==4)
                {
                    //...Do nothing...
                }
            }                                        
            k.send();
        }
        else
        {
            alert("Cant create XMLHttpRequest");
        }
    }
    .
    .
    .
    doReport();
    setInterval(doReport, 30000);// Report user presence every 30sec
    .
    .
    .

    这只是一个简单的 AJAX 代码,但请注意,在 open 方法中使用了“POST”,但参数是使用 GET 方法传递的(即,将参数附加到 URL)。后端代码中也使用了 $_GET[]。在 open 方法中使用 POST 的目的是防止缓存,如果 open 方法中使用了 GET,它总是会缓存,这在这种情况下是不希望的,因此使用了 POST。所有其他 AJAX 代码都遵循此模式。上述 URL 变量中的参数 id 应该是用户的 UserID。在此示例中,随机生成一个 ID 并存储在隐藏的输入字段中。

    .
    .
    .
    <?php
    echo "<input type=hidden id=userID value=" . rand(0, 5) . ">";
    ?>
    .
    .
    .

    在您自己的代码中使用真实的 UserID。

  • 设置历史最高在线
  • .
    .
    .
    //Set the highest online at all time
        $highest_online = 0;
        function setHighestOnline($con, $db_name)
        {
            //Get number of user currently online
            mysql_select_db($db_name, $con);
            $query = "SELECT * FROM ". $db_name .".online";
            $result = mysql_query($query, $con);
    
            if(mysql_num_rows($result) > 0)
            {
                while($row = mysql_fetch_assoc($result)) 
                {    //If last seen is less than 1 minute, user is assumed online
                    if(abs((strtotime($row['time'])-strtotime(
                        date('Y-m-d H:i:s', strtotime("now"))))) < 60)
                    {
                        $highest_online++;
                    }
                }
            }
            
            //Get highest online previous record
            $query = "SELECT * FROM ". $db_name .".highest";
            $result = mysql_query($query, $con);
            if(mysql_num_rows($result) > 0)
            {
                $row = mysql_fetch_assoc($result);
                if($highest_online >= $row['num'])
                {
                    //The currently online is greater
                    //than or equals to the previous record so update
                    $query = "UPDATE ". $db_name .
                      ".highest SET time = NOW(), num = '" . $highest_online . "'";
                    mysql_query($query, $con);
                }
            }
            else
            {
                //Highest online never set before so set the record
                $query = "INSERT INTO ". $db_name .
                  ".highest(time, num) VALUES(NOW(), '" . $highest_online . "')"; 
                mysql_query($query, $con);
            }
        }
    .
    .
    .

    这将简单地从“online”表中检查在线用户数量,并将结果与“highest”表中的记录进行比较。“highest”表会适当地 UPDATEINSERT INTO。其他函数只是遵循这种检查、更新和插入 MySQL 数据库的模式,并且非常容易理解。JavaScript 中的 setInterval() 函数用于以指定的时间间隔运行所有 AJAX 函数。

    .
    .
    .
    setInterval(doReport, 30000);// Report user presence every 30sec
    setInterval(numOnline, 60000);//Get number of user online every 60sec
    setInterval(lastSeen, 60000);//Get the last seen time of a user every 60sec
    setInterval(highestOnlineEver,(60000*5));//Get the highest online every 5 minutes
    .
    .
    .

其他必要事项

  1. 确保 checker.phpcheck.php 都在同一个目录中,否则您将不得不相应地更新 AJAX 代码中的 URL 变量。
  2. 请注意,代码假定您与数据库的连接永远不会失败,因此没有内置错误处理
  3. 您还可以扩展代码以包含一个函数,以获取用户在特定时间正在查看的当前页面。这些以及其他内容未包含在内,以使代码保持专注、简短和清晰。尽管这可以作为第二部分在广泛需求下推出。

一张截图

第一次修订:2013 年 4 月 9 日

基本算法仍然保留,但原始代码略有修改以解决一些疏忽

  1. PHP strtotime() 的夏令时行为:在初始代码中没有考虑 PHP strtotime() 在夏令时期间时间提前 1 小时的问题。这导致代码在夏令时期间无法检测在线用户,因为所有在线用户似乎都已离线了一个小时。通过将 date('Y-m-d H:i:s', strtotime('now')) 作为当前时间而不是使用 MySQL NOW() 来修复。
  2. 还检测到一个意外行为,即函数 setHighestOnline() 返回 MySQL 错误“未选择数据库”。这通过将已连接的 MySQL 链接传递给函数来修复。
  3. 修复了一些错别字。
© . All rights reserved.