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

PHP 連接 MySQL 完整實戰:從舊版 mysql_* 到 PDO 最佳實踐與亂碼徹底解決

     編輯:PHP 相關知識

⚠️ 重要提示:棄用 mysql_* 擴展

文章原始代碼使用 mysql_connect、mysql_query 等函數,自 PHP 5.5 棄用、PHP 7.0 完全移除。現代項目**必須使用 PDO 或 mysqli**。本文保留原邏輯作對比,重點講解 PDO 規範寫法與亂碼根治方案。

一、舊代碼問題剖析

<?php
// ❌ 已棄用寫法,僅供歷史參考
$link = mysql_connect("localhost", "root", "密碼");
if (!$link) echo "連接失敗!";
mysql_select_db("infosystem", $link);
mysql_query("SET NAMES GB2312"); // 編碼設置位置不當、易失效
$rs = mysql_query("SELECT * FROM info");
while ($row = mysql_fetch_array($rs)) {
    echo "<tr><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>";
}
mysql_free_result($rs);
?>

痛點: 無預處理(SQL 注入風險)、編碼設置靠後且不穩定、錯誤處理原始、資源釋放易忘。

二、PDO 規範寫法(推薦標準)

<?php
// config.php - 集中管理配置
return [
    'dsn'      => 'mysql:host=localhost;dbname=infosystem;charset=utf8mb4',
    'username' => 'root',
    'password' => '你的強密碼',
    'options'  => [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // 拋出異常
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // 返回關聯數組
        PDO::ATTR_EMULATE_PREPARES   => false,                  // 使用真實預處理
    ],
];

// db.php - 建立單例連接
class Database {
    private static ?PDO $pdo = null;
    public static function pdo(): PDO {
        if (self::$pdo === null) {
            $config = require __DIR__ . '/config.php';
            self::$pdo = new PDO(
                $config['dsn'],
                $config['username'],
                $config['password'],
                $config['options']
            );
        }
        return self::$pdo;
    }
}

// 業務代碼
try {
    $pdo = Database::pdo();
    $stmt = $pdo->query("SELECT dept_name, emp_name, pc_name FROM info");
    echo "<table><tr><th>部門</th><th>員工</th><th>PC</th></tr>";
    while ($row = $stmt->fetch()) {
        // 防 XSS:輸出前轉義
        echo "<tr><td>" . htmlspecialchars($row['dept_name']) . "</td>";
        echo "<td>" . htmlspecialchars($row['emp_name']) . "</td>";
        echo "<td>" . htmlspecialchars($row['pc_name']) . "</td></tr>";
    }
    echo "</table>";
} catch (PDOException $e) {
    error_log("DB Error: " . $e->getMessage());
    die("系統繁忙,請稍後再試");
}

三、中文亂碼根因與徹底解決方案

亂碼本質是「編碼鏈路任一環節不一致」。完整鏈路:

  1. 資料庫/表/列:CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci(支援 Emoji)。
  2. 連接層:DSN 含 charset=utf8mb4,或連接後執行 SET NAMES utf8mb4(PDO DSN 方式優先)。
  3. PHP 文件編碼:編輯器保存為 **UTF-8 無 BOM**。
  4. HTTP 響應頭:header('Content-Type: text/html; charset=utf-8'); 或 Nginx/Apache 配置。
  5. HTML 頁面:<meta charset="utf-8">。
  6. 瀏覽器:自動識別或強制 UTF-8。

常見誤區對照

誤區正確做法
用 SET NAMES utf8(無 mb4)必須 utf8mb4,否則 4 字節字符(Emoji、生僻字)截斷/亂碼
僅在 phpMyAdmin 設置字符集phpMyAdmin 只影響其自身會話,PHP 代碼需在 DSN 或連接後顯式設置
用 mysql_query("SET NAMES 'utf8'") 字符串拼接PDO 用 DSN 參數 charset=utf8mb4,mysqli 用 $mysqli->set_charset('utf8mb4')
GBK 項目強行混用 UTF-8若歷史庫為 GBK,連接層用 charset=gbk,輸出頁面 charset=gbk,全鏈路統一即可

四、進階:預處理語句防注入(必會技能)

$dept = $_GET['dept'] ?? '';
$stmt = $pdo->prepare("SELECT emp_name, pc_name FROM info WHERE dept_name = ?");
$stmt->execute([$dept]);
$rows = $stmt->fetchAll();

foreach ($rows as $row) {
    echo htmlspecialchars($row['emp_name']) . ' - ' . htmlspecialchars($row['pc_name']) . '<br>';
}

核心優勢: SQL 與數據分離,徹底杜絕注入;同一條 SQL 重複執行時,數據庫只解析優化一次,性能更優。

五、從 mysql_* 遷移清單

  • [ ] 所有 mysql_connect → PDO 單例
  • [ ] mysql_query → $pdo->query() / $pdo->prepare()->execute()
  • [ ] mysql_fetch_array → $stmt->fetch(PDO::FETCH_ASSOC)
  • [ ] mysql_real_escape_string → **刪除**,改用預處理
  • [ ] mysql_free_result → **自動釋放**,無需手動
  • [ ] 編碼統一 → utf8mb4 全鏈路
  • [ ] 錯誤處理 → try-catch + 記錄日志 + 友好提示

結語

告別 mysql_* 不僅是語法升級,更是安全與可維護性的重構。**PDO + utf8mb4 + 預處理 + 異常處理** 是現代 PHP 數據庫開發的「四件套」。從今天起,寫下的每一條 SQL 都應經得起代碼審查與安全掃描。

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