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

PHP 連接 MySQL 完全指南:mysqli 與 PDO 兩大現代方案、編碼防亂碼、擴展安裝排錯

     編輯:PHP 相關知識

PHP 7+ 移除了舊 mysql_* 擴展,現代開發必須掌握 mysqli(面向過程/對象雙模式)與 PDO(數據庫抽象層)兩大主流方案。本文涵蓋擴展安裝驗證、連接代碼模板、字符集設置防亂碼、預處理防注入、錯誤處理模式等核心知識,並給出選型建議。

一、環境檢查與擴展安裝

<?php
// 檢查擴展是否加載
foreach (['mysqli', 'pdo_mysql'] as $ext) {
    echo $ext . ': ' . (extension_loaded($ext) ? '✅ 已加載' : '❌ 未加載') . "\n";
}
// 輸出示例:
// mysqli: ✅ 已加載
// pdo_mysql: ✅ 已加載

常見安裝問題(Linux + PHP-FPM):

  1. php.ini 中啟用:extension=mysqli、extension=pdo_mysql(無需 .dll 後綴)。
  2. 確認 extension_dir 路徑正確(php -i | grep extension_dir)。
  3. 重啟 PHP-FPM:systemctl restart php8.3-fpm(版本號按實際調整)。
  4. 若報 Class 'mysqli' not found,檢查是否為 CLI 與 FPM 不同 php.ini(php --ini vs php-fpm -i)。

二、mysqli 方案(原生、高性能、僅支持 MySQL)

1. 面向對象風格(推薦)

<?php
$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'dbname', 3306);

if ($mysqli->connect_errno) {
    die("連接失敗: {$mysqli->connect_error}");
}

// 關鍵:設置字符集,防止中文亂碼
$mysqli->set_charset('utf8mb4'); // 等同於 SET NAMES utf8mb4

// 查詢
$sql = "SELECT id, name FROM users WHERE status = ? AND created_at > ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('is', 1, '2024-01-01'); // i=integer, s=string
$stmt->execute();
$result = $stmt->get_result();

while ($row = $result->fetch_assoc()) {
    echo "{$row['id']}: {$row['name']}\n";
}
$stmt->close();
$mysqli->close();

2. 面向過程風格(兼容舊代碼)

$link = mysqli_connect('127.0.0.1', 'user', 'pass', 'dbname');
if (!$link) die('連接失敗: ' . mysqli_connect_error());
mysqli_set_charset($link, 'utf8mb4');

$result = mysqli_query($link, "SELECT * FROM users");
while ($row = mysqli_fetch_assoc($result)) { /* ... */ }
mysqli_free_result($result);
mysqli_close($link);

三、PDO 方案(跨數據庫、統一接口、企業級首選)

<?php
$dsn = 'mysql:host=127.0.0.1;dbname=dbname;charset=utf8mb4';
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // 拋出異常
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // 默認關聯數組
    PDO::ATTR_EMULATE_PREPARES   => false,                  // 使用真實預處理
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4",    // 雙重保險
];

try {
    $pdo = new PDO($dsn, 'user', 'pass', $options);

    // 預處理查詢
    $stmt = $pdo->prepare("SELECT id, name FROM users WHERE status = ? AND created_at > ?");
    $stmt->execute([1, '2024-01-01']);

    foreach ($stmt as $row) {
        echo "{$row['id']}: {$row['name']}\n";
    }
} catch (PDOException $e) {
    error_log("DB Error: {$e->getMessage()}");
    die('服務器內部錯誤');
}

四、字符集亂碼根治三部曲

層級操作說明
數據庫/表/列CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci存儲層面支持完整 Unicode(含 Emoji)
連接層SET NAMES utf8mb4 / $mysqli->set_charset('utf8mb4') / DSN charset=utf8mb4客戶端↔服務器通信編碼一致
應用輸出header('Content-Type: text/html; charset=utf-8');瀏覽器正確解碼響應體

⚠️ 絕對不要用 utf8(MySQL 別名,實為 utf8mb3,不支持 4 字節字符),**統一用 utf8mb4**。

五、錯誤處理模式對比

模式mysqliPDO
默認返回 false,需手動檢查 $mysqli->error靜默,需檢查 errorCode()/errorInfo()
推薦mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); 拋出 mysqli_sql_exceptionPDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION 拋出 PDOException

六、選型建議:mysqli vs PDO

維度mysqliPDO
數據庫支持僅 MySQL/MariaDB12+ 驅動
API 風格雙模式,略顯雜亂純 OO,統一規範
預處理性能原生預處理,略快默認模擬,關閉模擬後持平
異步查詢支持 mysqli::poll不支持(需 Swoole/ReactPHP)
命名參數綁定不支持(僅 ? 佔位符)支持 :name 可讀性強
事務嵌套/保存點支持支持
生態/框架支持Laravel/ThinkPHP 底層多用 PDO幾乎所有現代框架默認

結論:新項目首選 PDO(可移植、異常模式、命名參數);極致性能/異步/純 MySQL 老項目維護用 mysqli。

七、完整 PDO 連接池單例模式(生產級)

<?php
class Database {
    private static ?PDO $instance = null;

    public static function getInstance(): PDO {
        if (self::$instance === null) {
            $dsn = 'mysql:host=127.0.0.1;dbname=app;charset=utf8mb4';
            self::$instance = new PDO($dsn, 'user', 'pass', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES => false,
                PDO::ATTR_PERSISTENT => true, // 長連接,慎用(需配合 wait_timeout)
            ]);
        }
        return self::$instance;
    }

    // 禁用克隆/序列化
    private function __construct() {}
    private function __clone() {}
    public function __wakeup() {}
}

// 使用
$pdo = Database::getInstance();
$stmt = $pdo->query("SELECT VERSION()");
echo $stmt->fetchColumn();

總結

告別 mysql_*,擁抱 mysqli/PDO。核心鐵律:**預處理防注入、utf8mb4 全鏈路、異常模式處理錯誤**。PDO 作為統一抽象層,是現代 PHP 應用的數據庫訪問基石。

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