基于Redis的PHP聊天室程序

时间:2021-12-10 10:08:11

Redis是一个轻量级的key-value型数据存储系统,相对于memcache/memcached,提供了更多的冗余性,并且可以将存储的数据转存到磁盘文件中,以便下次启动时恢复。同时Redis提供了集合、列表等数据结构。


结合以上特性,采用Redis作为聊天室的数据存储解决方案,可获得较高的执行效率。

直接贴服务端代码了,需要php_redis扩展支持,没有用户系统没做,这个程序目前是挂到一个phpwind论坛里的,直接从全局变量读取用户和用户组。
界面部分在此省略。
完整演示可参考http://bbs.91d2.cn/,点击快捷操作栏右下角的“论坛聊天室”即可打开。
<?phpdefine('DISABLE_REFRESH_LIMIT', 1);
require_once('global.php');
header('Content-Type: text/html; charset=UTF-8');
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$if_init = get_string('chat_init');
if(!$if_init)
{
if(get_string('chat_init_lock')){die;}
set_string('chat_init_lock', '1');
set_array('chat_channel', array('GLOBAL'));
set_array('chat_motd', array('GLOBAL' => 'Hello!'));
set_array('chat_online', array());
set_string('chat_init', '1');
set_string("0_name", '系统');
set_string("0_flag", 'SA');
}

if(!$winduid){
echo 'chatLogin();';
die;
}

set_string("{$winduid}_name", $windid);
set_string("uid_{$windid}", $winduid);

$channel_list = get_array('chat_channel');
$channel_motd = get_array('chat_motd');

$my_channel = get_string("{$winduid}_in");

$my_index = get_string("{$winduid}_index");
if(!$my_channel || !in_array($my_channel, $channel_list)){
$my_channel = $channel_list[0];
set_string("{$winduid}_in", $my_channel);
$my_index = 0;
}

$channel_index = get_string("{$my_channel}_index");
$channel_ca = get_array("{$my_channel}_ca");

if ($winddb['groupid'] == 3 || $winddb['groupid'] == 4 || $winddb['groupid'] == 22)
{
$user_flag = 'SA';
}
else if(in_array($winduid, $channel_ca))
{
$user_flag = 'CA';
}
else
{
$user_flag = 'U';
}

set_string("{$winduid}_flag", $user_flag);

InitGP(array('action', 'first'));

if(!$my_index || $first){
if ($my_index == 0)
{
send_message(0, 'MOTD: ' . $channel_motd[$my_channel], 'info', $my_channel, $winduid);
}
$my_index = $first ? ($channel_index - 10) : ($channel_index + 1);
if($my_index < 0){
$my_index = 0;
}
set_string("{$winduid}_index", $my_index);
}

if($action == 'ui')
{
require_once PrintEot('chat_ui');
}

if($action == 'channel')
{
echo "chatChannelListBegin();\r\n";
foreach($channel_list as $channel_name)
{
echo "chatHandleChannelList('" . mb_convert_encoding($channel_name, 'UTF-8', 'GBK') . "');\r\n";
}
echo "chatChannelListEnd();\r\n";
}

if($action == 'send')
{
InitGP(array('content', 'receiver'));
if (!$content || !$receiver)
{
echo '0';
die;
}
if ($winddb['postnum'] < 100)
{
send_message(0, '您的发贴数不足100,没有发言权限', 'warn', $my_channel, $winduid);
}
else
{
if (get_string("{$winduid}_block") > $timestamp)
{
send_message(0, '您已被禁言,无法发言', 'warn', $my_channel, $winduid);
}
else
{
$content = htmlspecialchars($content);
send_message($winduid, stripslashes(mb_convert_encoding($content, 'GBK', 'UTF-8')), 'info', $my_channel, $receiver);
}
}
echo '0';
}

if($action == 'get')
{
$channel_ca_list = '';
if ($user_flag == 'SA' || $user_flag == 'CA')
{
foreach ($channel_ca as $ca)
{
$channel_ca_list .= get_string("{$ca}_name") . ',';
}
$channel_ca_list = mb_convert_encoding($channel_ca_list, 'UTF-8', 'GBK');
}
echo "chatHandleStatus('" . mb_convert_encoding($my_channel, 'UTF-8', 'GBK') . "', '" . mb_convert_encoding($windid, 'UTF-8', 'GBK') . "', '{$user_flag}', '{$channel_ca_list}');";
for(; $my_index <= $channel_index; $my_index ++)
{
$message = get_array("{$my_channel}_{$my_index}");
if ($message['receiver'] == 'all' || $message['receiver'] == $winduid)
{
$message['content'] = addslashes(mb_convert_encoding($message['content'], 'UTF-8', 'GBK'));
$sender_flag = get_string("{$message[author]}_flag");
$sender_flag == 'SA' && $message['content'] = '<span style="color:red;font-weight:bold">' . $message['content'] . '</span>';
$sender_flag == 'CA' && $message['content'] = '<span style="color:blue">' . $message['content'] . '</span>';
$message['author'] = addslashes(mb_convert_encoding(get_string("{$message[author]}_name"), 'UTF-8', 'GBK'));
echo "chatHandleMessage('{$message[type]}', '" . $message['author'] . "', '{$message[content]}', {$message[date]});\r\n";
}
}
set_string("{$winduid}_index", $my_index);
}

if($action == 'block')
{
InitGP(array('username', 'timespan'));
$username = mb_convert_encoding($username, 'GBK', 'UTF-8');
if ($user_flag == 'SA' || $user_flag == 'CA')
{
set_string(get_string("uid_{$username}") . '_block', $timestamp + $timespan * 3600);
send_message(0, '禁言用户[' . $username . ']成功', 'warn', $my_channel, $winduid);
echo '0';
}
else
{
send_message(0, '权限不足', 'warn', $my_channel, $winduid);
echo '-1';
}
}

if($action == 'setchannel')
{
InitGP(array('option', 'channel', 'channel_name', 'channel_ca'));
if ($option == 'join')
{
$channel = mb_convert_encoding($channel, 'GBK', 'UTF-8');
if (in_array($channel, $channel_list))
{
set_string("{$winduid}_index", 0);
set_string("{$winduid}_in", $channel);
echo '0';
}
else
{
send_message(0, '频道不存在', 'warn', $my_channel, $winduid);
echo '-1';
die;
}
}
else
{
if ($user_flag != 'SA')
{
send_message(0, '目前只有全局管理员可以创建频道', 'warn', $my_channel, $winduid);
echo '-1';
die;
}
if (!$channel_name)
{
send_message(0, '请输入频道名', 'warn', $my_channel, $winduid);
echo '-1';
die;
}
$channel_name = mb_convert_encoding($channel_name, 'GBK', 'UTF-8');
$channel_ca = mb_convert_encoding($channel_ca, 'GBK', 'UTF-8');
if (in_array($channel, $channel_list))
{
send_message(0, '频道名重复', 'warn', $my_channel, $winduid);
echo '-1';
die;
}
$channel_list[] = $channel_name;
$channel_motd[$channel_name] = 'Hello!';
set_array('chat_channel', $channel_list);
set_array('chat_motd', $channel_motd);
set_array("{$channel_name}_ca", explode(',', $channel_ca));
send_message(0, '频道创建成功', 'warn', $my_channel, $winduid);
}
}

if($action == 'setmotd')
{
InitGP(array('motd'));
$motd = mb_convert_encoding($motd, 'GBK', 'UTF-8');
if ($user_flag == 'SA' || $user_flag == 'CA')
{
$channel_motd[$my_channel] = $motd;
set_array('chat_motd', $channel_motd);
send_message(0, 'MOTD设置成功', 'warn', $my_channel, $winduid);
echo '0';
}
else
{
send_message(0, '权限不足', 'warn', $my_channel, $winduid);
echo '-1';
}
}

if($action == 'delchannel')
{
if ($user_flag == 'SA' || $user_flag == 'CA')
{
unset($channel_list[array_search($my_channel, $channel_list)]);
unset($channel_motd[$my_channel]);
set_array('chat_channel', $channel_list);
set_array('chat_motd', $channel_motd);
remove("{$my_channel}_ca");
set_string("{$winduid}_index", 0);
set_string("{$winduid}_in", 'GLOBAL');
send_message(0, '频道删除成功', 'warn', 'GLOBAL', $winduid);
echo '0';
}
else
{
send_message(0, '权限不足', 'warn', $my_channel, $winduid);
echo '-1';
}
}

if($action == 'setca')
{
InitGP(array('username', 'option'));
if ($user_flag == 'SA' || $user_flag == 'CA')
{
$username = mb_convert_encoding($username, 'GBK', 'UTF-8');
$uid = get_string("uid_{$username}");
if (!$uid)
{
send_message(0, '用户没有登录', 'warn', $my_channel, $winduid);
echo '-1';
die;
}
if ($option == 'add')
{
if (!in_array($uid, $channel_ca))
{
$channel_ca[] = $uid;
send_message(0, '增加管理员[' . $username . ']', 'warn', $my_channel, $winduid);
}
}
else
{
if (in_array($uid, $channel_ca))
{
unset($channel_ca[array_search($uid, $channel_ca)]);
send_message(0, '取消管理员[' . $username . ']', 'warn', $my_channel, $winduid);
}
}
set_array("{$my_channel}_ca", $channel_ca);
echo '0';
}
else
{
send_message(0, '权限不足', 'warn', $my_channel, $winduid);
echo '-1';
}
}

function remove()
{
global $redis;
$redis->delete($name);
}

function set_string($name, $val)
{
global $redis;
$redis->set($name, $val);
}

function get_string($name, $val)
{
global $redis;
return $redis->get($name);
}

function append_string($name, $val)
{
global $redis;
return $redis->append($name, $val);
}

function set_array($name, $val)
{
global $redis;
$redis->set($name, serialize($val));
}

function get_array($name)
{
global $redis;
return unserialize($redis->get($name));
}

function send_message($author, $content, $type = 'info', $channel = 'GLOBAL', $receiver = 'all')
{
global $redis, $timestamp;
if (strlen($content) > 200)
{
$content = substr($content, 0 , 200);
}
while(get_string("{$channel}_lock")){usleep(100);}
set_string("{$channel}_lock", 1);
$channel_index = get_string("{$channel}_index");
$channel_index = $channel_index + 1;
set_array("{$channel}_{$channel_index}", array(
'author' => $author,
'content' => $content,
'type' => $type,
'receiver' => $receiver,
'date' => $timestamp
));
set_string("{$channel}_index", $channel_index);
$clean_index = $channel_index - 100;
remove("{$channel}_{$clean_index}");
$author_name = get_string("{$author}_name");
append_string("{$channel}_record", get_date($timestamp, 'Y-m-d H:i:s') . " [{$type}] {$author_name} -> {$receiver}: {$content}\r\n");
check_channel_record($channel);
set_string("{$channel}_lock", 0);
}

function check_channel_record($channel)
{
global $timestamp;
$lastsave = get_string("{$channel}_lastsave");
if($timestamp - $lastsave > 600)
{
set_string("{$channel}_lastsave", $timestamp);
save_record($channel);
}
}

function save_record($channel)
{
global $db, $timestamp;
$record = str_replace(
array("\\", "\"", "'"),
array("\\\\", "\\\"", "\\'"),
get_string("{$channel}_record")
);
set_string("{$channel}_record", '');
if(strlen($record)>0){
$db->update("INSERT INTO `pw_chatrecord` (`channel`, `time`, `content`) VALUES ('$channel', '$timestamp', '$record')");
}
}