首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 與 Access 打造聊天室系列(四):房間列表管理與動態交互實現

     編輯:PHP 綜合知識

模塊定位:room.php3

本文為系列第四部分,對應 room.php3 —— 聊天室左上角「房間面板」的完整邏輯與界面。功能覆蓋:房間列表展示、加入房間、創建房間入口、刪除房間(拆房)、自動刷新機制、權限控制。代碼延續「PHP 混編 HTML、Meta Refresh 輪詢、JavaScript 輔助表單提交」的經典模式。

核心業務流程

  1. 啟動時清理:刪除無人佔用且非大廳(RoomID≠1)的房間記錄。
  2. 用戶驗證:同 member.php3,檢查密碼與心跳(30分鐘)。
  3. 處理「拆房」請求($bPost=="1"):
    • 大廳不可刪;
    • 非管理員僅能刪除自己創建的房間(檢查 MasterID);
    • 執行刪除前將該房間用戶遷移至大廳,更新當前用戶 RoomID=1。
  4. 渲染房間下拉列表:當前房間 selected 高亮。
  5. 按權限輸出操作按鈕:管理員無「新建/加入」按鈕;普通用戶可「新建」(跳轉 newroom.php3)、「加入」(提交 enterrm.php3)、「拆房」(確認彈窗後提交本頁)。
  6. 自動刷新:<meta http-equiv="refresh" content="RefRate*8;url=room.php3?...">。

關鍵代碼片段解析

1. 啟動清理空房間

@odbc_exec($ConnID, "DELETE * FROM Room WHERE (RoomID Not IN (SELECT DISTINCT RoomID FROM User)) AND RoomID<>1");

子查詢找出所有被佔用的 RoomID,反選刪除其餘(保留 RoomID=1 大廳)。Access SQL 支持 DELETE * FROM 語法。

2. 拆房邏輯與權限判斷

if ($bPost=="1") {
  $sDelRoom = substr($sDelRoom, 2); // 去掉前綴 "rm"
  if ($sDelRoom=="1") $bCanDel=false; // 大廳不可刪
  else $bCanDel=true;
  
  if ($id!="1" && $ps=="superldz") { // 非管理員
    $result = @odbc_exec($ConnID, "SELECT MasterID FROM Room WHERE RoomID=$sDelRoom");
    if (@odbc_fetch_into($result,0,$rArr)) {
      if ($rArr[0] != $id) $bCanDel = false; // 非房主不可刪
    } else $bCanDel = false;
  }
  
  if ($bCanDel) {
    @odbc_exec($ConnID, "UPDATE User SET LstTime=".time()." WHERE RoomID=$sDelRoom AND UserID=$id");
    @odbc_exec($ConnID, "UPDATE User SET RoomID=1 WHERE RoomID=$sDelRoom");
    @odbc_exec($ConnID, "DELETE * FROM Room WHERE RoomID=$sDelRoom");
    $sRoomID = "1";
  }
}

注意:$ps=="superldz" 此處疑為筆誤,應為 !=;原代碼邏輯可能導致非管理員也被視為管理員。

3. 房間下拉列表生成

echo "<select name='selRoom' size='7' style='WIDTH: 160px'>\n";
$result = @odbc_exec($ConnID, "SELECT RoomID, RoomName FROM Room ORDER BY RoomID");
while (@odbc_fetch_into($result,0,$rArr)) {
  if ((int)$sRoomID == $rArr[0])
    echo "\t\t<option value='rm{$rArr[0]}' selected>{$rArr[1]}</option>\n";
  else
    echo "\t\t<option value='rm{$rArr[0]}'>{$rArr[1]}</option>\n";
}
echo "\t\t</select>\n";

值格式 rm{RoomID},前端 JS CheckValid() / AlertIt() 解析提交。

4. 前端交互腳本

<script language="JavaScript">
function CheckValid() {
  document.frmInRoom.selRoom.value = document.frmRoom.selRoom.value;
  return true;
}
function AlertIt() {
  document.frmDelRoom.sDelRoom.value = document.frmRoom.selRoom.value;
  return confirm("你真的要拆了這個房子啊!");
}
</script>

三個表單分別對應:frmRoom(列表選擇)、frmInRoom(加入)、frmDelRoom(拆房),通過隱藏字段傳遞動作類型與參數。

安全與架構隱患

問題代碼位置風險等級
SQL 注入所有 $id、$sDelRoom 直接拼接🔴 嚴重
權限判斷邏輯錯誤if ($id!="1" && $ps=="superldz") 應為 !=🟠 高
CSRF 缺失所有狀態改變操作無 Token 驗證🟠 高
XSS 風險RoomName 直接輸出到 option,未轉義🟡 中
Meta Refresh 體驗差整頁刷新導致閃爍、滾動位置丟失🟡 中
Access 並發瓶頸文件鎖、無行級鎖、易損壞🔴 嚴重

現代化重構方案

API 設計

端點方法功能權限
/api/roomsGET獲取房間列表(含在線人數)登錄用戶
/api/roomsPOST創建房間普通用戶
/api/rooms/{id}DELETE刪除房間(遷移成員至大廳)房主/管理員
/api/rooms/{id}/joinPOST加入房間(驗證密碼)登錄用戶

前端架構(Vue 3 + Pinia 示例)

<script setup>
import { ref, onMounted, watch } from 'vue'
import { useRoomStore } from '@/stores/room'
import { useUserStore } from '@/stores/user'

const roomStore = useRoomStore()
const userStore = useUserStore()
const rooms = computed(() => roomStore.list)
const currentRoomId = computed(() => userStore.user.room_id)
const selectedRoom = ref(null)
const showDeleteConfirm = ref(false)
const deletingRoomId = ref(null)

onMounted(() => {
  roomStore.fetchList()
  // WebSocket 訂閱房間變更
  ws.on('room:update', roomStore.fetchList)
  ws.on('room:delete', id => roomStore.remove(id))
})

function joinRoom(room) {
  if (room.id === currentRoomId.value) return
  if (room.pass_hash && !await promptPassword()) return
  api.post(`/rooms/${room.id}/join`).then(() => {
    userStore.setRoom(room.id)
    router.push('/chat')
  })
}

function confirmDelete(room) {
  deletingRoomId.value = room.id
  showDeleteConfirm.value = true
}

async function executeDelete() {
  await api.delete(`/rooms/${deletingRoomId.value}`)
  showDeleteConfirm.value = false
  if (currentRoomId.value === deletingRoomId.value) {
    userStore.setRoom(1) // 大廳
  }
}
</script>

<template>
  <div class="room-panel">
    <h3>房間列表</h3>
    <select v-model="selectedRoom" size="10" class="room-list">
      <option v-for="r in rooms" :key="r.id" :value="r" :selected="r.id===currentRoomId">
        {{ r.name }} ({{ r.online_count }})
      </option>
    </select>
    <div class="actions">
      <button @click="joinRoom(selectedRoom)" :disabled="!selectedRoom || selectedRoom.id===currentRoomId">加入</button>
      <button @click="router.push('/rooms/create')">新建</button>
      <button @click="confirmDelete(selectedRoom)" :disabled="!selectedRoom || selectedRoom.id===1" class="danger">拆房</button>
    </div>
    <ConfirmDialog v-model:show="showDeleteConfirm" title="確認拆房" message="確定要刪除該房間嗎?成員將被移至大廳。" @confirm="executeDelete" />
  </div>
</template>

後端關鍵邏輯(Laravel 風格)

public function destroy(Room $room) {
  // 權限:房主或管理員
  $this->authorize('delete', $room);
  
  return DB::transaction(function() use ($room) {
    // 1. 將房間內用戶遷移至大廳 (id=1)
    User::where('room_id', $room->id)->update([
      'room_id' => 1,
      'to_id' => 0,
      'is_secret' => false,
    ]);
    
    // 2. 記錄系統消息
    Message::createSystem(1, "房間「{$room->name}」已被拆除,成員遷移至大廳");
    
    // 3. 刪除房間
    $room->delete();
    
    // 4. 廣播事件
    event(new RoomDeleted($room->id));
  });
}

總結

room.php3 以 200 行代碼實現了房間全生命周期管理,展示了早期 PHP 開發「單文件包辦數據邏輯、模板渲染、前端交互」的高密度特點。其核心業務模型(房間、成員、權限、狀態遷移)清晰可復用。現代化重構的關鍵在於:拆分關注點(API/Service/Frontend)、引入事務與行級鎖保證一致性、用 WebSocket 替換 Meta Refresh 實現實時同步、統一權限策略。本文提供的重構藍圖可直接指導遷移項目落地。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved