首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 入門

MySQL PHP 語法完全指南:mysqli/PDO 核心 API、預處理防注入、錯誤處理與現代最佳實踐

     編輯:MySQL 入門

MySQL PHP 語法完全指南:mysqli/PDO 核心 API、預處理防注入、錯誤處理與現代最佳實踐

PHP 是 MySQL 應用最廣泛的語言。從早期的 mysql_*(已移除)、mysqli 到現代的 PDO,API 經歷了巨大演進。本文系統梳理核心 API、預處理機制、錯誤處理模式,並確立「PDO + 預處理 + 異常模式」的現代最佳實踐標準。

歷史 API 演進與現狀

擴展 狀態 特點
mysql_*PHP 7.0 移除過程式、無預處理、不支援新特性、極不安全
mysqli維護中MySQL 專用、支援預處理、程序/物件雙風格、功能最全
PDO推薦標準資料庫抽象層、支援 12+ 驅動、統一 API、原生預處理、異常模式

核心 API 對比速查

操作 mysqli (OO) PDO
連線new mysqli($h,$u,$p,$db)new PDO($dsn,$u,$p)
設定字符集$mysqli->set_charset('utf8mb4')DSN charset=utf8mb4
預處理$stmt = $mysqli->prepare($sql)$stmt = $pdo->prepare($sql)
綁參執行$stmt->bind_param($types, ...$vals); $stmt->execute()$stmt->execute($vals) 或 bindValue
獲取結果$res = $stmt->get_result(); $row = $res->fetch_assoc()$row = $stmt->fetch(PDO::FETCH_ASSOC)
影響行數$stmt->affected_rows$stmt->rowCount()
自增 ID$mysqli->insert_id$pdo->lastInsertId()
事務$mysqli->begin_transaction(); commit(); rollback()$pdo->beginTransaction(); commit(); rollBack()
錯誤模式mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
關閉$mysqli->close()$pdo = null

PDO 現代最佳實踐範本

<?php
// 1. 連線配置
$dsn = 'mysql:host=localhost;port=3306;dbname=myapp;charset=utf8mb4';
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 拋出異常
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,         // 原生預處理
    PDO::ATTR_STRINGIFY_FETCHES => false,        // 保持原生類型
    PDO::MYSQL_ATTR_SSL_CA => '/etc/ssl/certs/ca.pem', // 生產環境 SSL
];

try {
    $pdo = new PDO($dsn, 'app_user', 'strong_password', $options);
} catch (PDOException $e) {
    error_log('DB Connect Failed: ' . $e->getMessage());
    die('Service unavailable');
}

// 2. 查詢範例:預處理 + 綁定 + 異常捕獲
function getUserByEmail(PDO $pdo, string $email): ?array {
    $sql = 'SELECT id, name, email, created_at FROM users WHERE email = ? AND deleted_at IS NULL';
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$email]);
    return $stmt->fetch() ?: null;
}

// 3. 寫入範例:事務 + 預處理 + 回滾
function createOrder(PDO $pdo, int $userId, array $items): int {
    $pdo->beginTransaction();
    try {
        $stmt = $pdo->prepare('INSERT INTO orders (user_id, total, status) VALUES (?, ?, "pending")');
        $total = array_sum(array_column($items, 'price'));
        $stmt->execute([$userId, $total]);
        $orderId = (int)$pdo->lastInsertId();

        $stmtItem = $pdo->prepare('INSERT INTO order_items (order_id, product_id, qty, price) VALUES (?, ?, ?, ?)');
        foreach ($items as $item) {
            $stmtItem->execute([$orderId, $item['product_id'], $item['qty'], $item['price']]);
        }
        $pdo->commit();
        return $orderId;
    } catch (Throwable $e) {
        $pdo->rollBack();
        throw $e;
    }
}

// 4. 批次插入:單條 SQL 多值
function batchInsertLogs(PDO $pdo, array $logs): int {
    if (!$logs) return 0;
    $placeholders = implode(',', array_fill(0, count($logs), '(?, ?, ?)'));
    $sql = "INSERT INTO access_logs (ip, path, created_at) VALUES $placeholders";
    $stmt = $pdo->prepare($sql);
    $vals = [];
    foreach ($logs as $log) {
        $vals[] = $log['ip'];
        $vals[] = $log['path'];
        $vals[] = $log['created_at'];
    }
    $stmt->execute($vals);
    return $stmt->rowCount();
}
?>

mysqli 物件風格範例(僅限 MySQL 專用專案)

<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // 異常模式
$mysqli = new mysqli('localhost', 'user', 'pass', 'db', 3306);
$mysqli->set_charset('utf8mb4');

$stmt = $mysqli->prepare('SELECT id, name FROM users WHERE email = ?');
$stmt->bind_param('s', $email);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) { /* ... */ }
$stmt->close();
$mysqli->close();
?>

防 SQL 注入鐵律

  1. 永遠使用預處理:數值、字串、日期、NULL 皆用佔位符 ?(PDO)或 ?(mysqli),絕不拼接變數進 SQL 字串。
  2. 識別符(表名、欄位名)不可參數化:需白名單驗證後拼接:if (in_array($col, $allowed)) $sql .= "`$col`";
  3. EMULATE_PREPARES = false(PDO):強制使用原生預處理,避免驅動層模擬導致的注入風險。
  4. 錯誤訊息不回顯給前端:只記錄日誌,前端返回通用錯誤碼,防資訊洩漏。

常見錯誤與避坑

  • PDOException: SQLSTATE[HY000] [2002] Connection refused:服務未啟動/埠錯/防火牆/主機名解析。
  • mysqli::prepare(): Couldn't fetch mysqli:連線已關閉或連線失敗未檢查。
  • Packets out of order:連線被併發使用(mysqli 非線程安全),每請求獨立連線或用連線池。
  • Commands out of sync:多結果集未全部讀取/釋放,$stmt->free_result() / $stmt->next_result()。

效能優化清單

  1. 連線池:PHP-FPM + PDO/mysqli 持久連線(PDO::ATTR_PERSISTENT => true / p:host)。
  2. 預處理重用:同一 SQL 重複執行時,準備一次、執行多次。
  3. 批次操作:INSERT ... VALUES (...), (...), ... 单條 SQL 寫入成百上千行。
  4. 只選所需欄位:SELECT id, name FROM ... 而非 SELECT *。

PHP 操作 MySQL 的核心哲學:PDO 統一介面、預處理防注入、異常模式處錯、連線池復用、批次寫入、最小權限帳號。將這套標準內化為團隊規範、封裝為基礎庫、納入 Code Review 清單,讓每一行資料庫代碼都安全、高效、可維護。

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