刪除資料屬高風險操作,必須在前端攔截確認,防止使用者誤觸。本文示範「PHP 後端渲染列表 + JavaScript confirm() 確認 + AJAX/導頁刪除」的經典模式,並使用 PDO 預處理語句防止 SQL 注入。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>會員列表 - 刪除確認範例</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: center; }
th { background: #f4f4f4; }
.btn-del { color: #c00; text-decoration: none; cursor: pointer; }
</style>
</head>
<body>
<h2>會員列表</h2>
<?php
// ========== 1. 資料庫設定 ==========
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$dbUser = 'root';
$dbPass = 'root';
$pdo = new PDO($dsn, $dbUser, $dbPass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
// ========== 2. 處理刪除請求 (放在輸出 HTML 之前) ==========
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
$id = (int) $_GET['id'];
try {
$stmt = $pdo->prepare('DELETE FROM members WHERE id = :id');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$msg = $stmt->rowCount() ? '刪除成功' : '找不到該筆資料';
} catch (PDOException $e) {
$msg = '刪除失敗:' . $e->getMessage();
}
// 導回列表並攜帶訊息
header('Location: ' . $_SERVER['PHP_SELF'] . '?msg=' . urlencode($msg));
exit;
}
// ========== 3. 讀取列表資料 ==========
$stmt = $pdo->query('SELECT id, username, email, created_at FROM members ORDER BY id DESC');
$rows = $stmt->fetchAll();
?>
<!-- 顯示操作訊息 -->
<?php if (isset($_GET['msg'])): ?>
<div style="color: green; margin-bottom: 10px;"><?= htmlspecialchars($_GET['msg']) ?></div>
<?php endif; ?>
<table>
<thead>
<tr><th>ID</th><th>帳號</th><th>Email</th><th>建立時間</th><th>操作</th></tr>
</thead>
<tbody>
<?php foreach ($rows as $row): ?>
<tr>
<td><?= $row['id'] ?></td>
<td><?= htmlspecialchars($row['username']) ?></td>
<td><?= htmlspecialchars($row['email']) ?></td>
<td><?= $row['created_at'] ?></td>
<td>
<a href="javascript:void(0);"
class="btn-del"
onclick="confirmDelete(<?= $row['id'] ?>)">刪除</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<script>
function confirmDelete(id) {
if (confirm('確定要刪除此會員嗎?此操作無法復原!')) {
// 方式 A: 頁面導向 (簡單、可見 URL)
window.location.href = '?action=delete&id=' + id;
// 方式 B: AJAX (體驗較好、無刷新) — 取消註解即可使用
/*
fetch('?action=delete&id=' + id, {method: 'GET'})
.then(r => r.text())
.then(msg => {
alert(msg);
location.reload();
});
*/
}
}
</script>
</body>
</html>
| 風險 | 對策 | 程式碼位置 |
|---|---|---|
| SQL 注入 | PDO 預處理 + bindValue(:id, $id, PDO::PARAM_INT) | 刪除處理區塊 |
| CSRF (跨站請求偽造) | 表單加入 csrf_token、刪除改用 POST + 驗證 Token | 建議進階實作 |
| XSS (跨站腳本) | 輸出變數用 htmlspecialchars() | 列表渲染、訊息顯示 |
| 誤刪無法復原 | 軟刪除 (deleted_at 欄位) + 回收站機制 | 資料庫設計層面 |
| ID 遍歷攻擊 | 權限檢查:確認登入用戶擁有該資料刪除權限 | 刪除前加入政策判斷 |