API 介面開發中,資料庫查詢結果轉 JSON 是高頻操作。原文提供的兩個範例雖能跑通,但存在嚴重問題:使用已棄用的 mysql_* 函式、缺乏錯誤處理、SQL 直接拼接變數導致注入風險、分頁參數未驗證、資源釋放不完整。
class Database {
private static ?\PDO $instance = null;
private function __construct() {}
public static function getInstance(): \PDO {
if (self::$instance === null) {
$dsn = "mysql:host=localhost;dbname=jilinwula;charset=utf8mb4";
self::$instance = new \PDO($dsn, 'root', 'root', [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
]);
}
return self::$instance;
}
}
class UserService {
public function getUsersPaginated(int $page = 1, int $pageSize = 15): array {
$page = max(1, $page);
$pageSize = min(max(1, $pageSize), 100); // 限制最大頁長
$offset = ($page - 1) * $pageSize;
$pdo = Database::getInstance();
$stmt = $pdo->prepare("SELECT `username`, `email`, `created_at` FROM `userinfo` LIMIT ? OFFSET ?");
$stmt->bindValue(1, $pageSize, \PDO::PARAM_INT);
$stmt->bindValue(2, $offset, \PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetchAll();
// 總筆數查詢(可優化為快取)
$total = (int)$pdo->query("SELECT COUNT(*) FROM `userinfo`")->fetchColumn();
return [
'data' => $data,
'meta' => [
'current_page' => $page,
'page_size' => $pageSize,
'total' => $total,
'last_page' => (int)ceil($total / $pageSize),
],
];
}
}
header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');
try {
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$pageSize = isset($_GET['pageSize']) ? (int)$_GET['pageSize'] : 15;
$service = new UserService();
$result = $service->getUsersPaginated($page, $pageSize);
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} catch (\Throwable $e) {
http_response_code(500);
echo json_encode([
'error' => 'Internal Server Error',
'message' => $e->getMessage(), // 生產環境應隱藏詳細錯誤
], JSON_UNESCAPED_UNICODE);
}
| 維度 | 不良實踐 | 推薦做法 |
|---|---|---|
| 資料庫驅動 | mysql_* 函式 | PDO / mysqli + 預處理語句 |
| 字元編碼 | SET NAMES UTF8 | DSN 中指定 charset=utf8mb4 |
| 錯誤處理 | die()/or die() | try-catch + 統一異常處理器 |
| 輸出格式 | 直接 echo json_encode | 設定正確 Content-Type + JSON 選項常數 |
| 分頁參數 | 直接使用 $_GET | 型別轉換 + 範圍驗證 + 預設值 |
| 資源管理 | 手動 free/close | PDO 物件自動管理 / 依賴注入容器 |
WHERE id > ? ORDER BY id LIMIT ? 替代 OFFSET從「能跑通」到「生產可用」,中間隔著資料庫抽象層、參數驗證、異常處理、資源管理、安全防護等多層工程化建設。掌握 PDO 預處理、依賴注入、分層架構等核心技能,才能寫出經得起時間考驗的 PHP 程式碼。