萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> PHP 程式設計

PHP 連接 MySQL 資料庫完整教學:從基礎到 PDO 最佳實踐

前言

PHP 操作 MySQL 是後端開發的核心技能。早期使用 mysql_* 函數已被棄用(PHP 7.0 移除),現代開發應採用 PDO (PHP Data Objects) 或 MySQLi。本文從基礎連線、資料庫操作,到建立可重用的資料庫類,完整示範現代最佳實踐。

環境準備

  1. 確認 php.ini 已啟用擴充:extension=pdo_mysql、extension=mysqli
  2. 重啟 Web 伺服器(Apache/Nginx + PHP-FPM)
  3. 驗證:php -m | grep -E 'pdo_mysql|mysqli'

方案一:PDO(推薦,支援多資料庫)

基本連線

<?php
$dsn = 'mysql:host=localhost;dbname=guestbook;charset=utf8mb4';
$username = 'root';
$password = '';

try {
    $pdo = new PDO($dsn, $username, $password, [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // 拋出例外
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // 回傳關聯陣列
        PDO::ATTR_EMULATE_PREPARES   => false,                  // 使用原生預處理
    ]);
    echo "連線成功";
} catch (PDOException $e) {
    die("連線失敗: " . $e->getMessage());
}
?>

執行查詢(預處理語句,防 SQL 注入)

<?php
// 查詢單筆
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([1]);
$user = $stmt->fetch();

// 查詢多筆
$stmt = $pdo->prepare('SELECT * FROM users WHERE status = ? ORDER BY created_at DESC');
$stmt->execute([1]);
$users = $stmt->fetchAll();

// 新增資料
$stmt = $pdo->prepare('INSERT INTO users (username, email) VALUES (?, ?)');
$stmt->execute(['JohnDoe', 'john@example.com']);
$newId = $pdo->lastInsertId();

// 更新資料
$stmt = $pdo->prepare('UPDATE users SET email = ? WHERE id = ?');
$stmt->execute(['new@example.com', 1]);

// 刪除資料
$stmt = $pdo->prepare('DELETE FROM users WHERE id = ?');
$stmt->execute([1]);

// 事務處理
try {
    $pdo->beginTransaction();
    $pdo->exec("INSERT INTO logs (msg) VALUES ('操作 A')");
    $pdo->exec("INSERT INTO logs (msg) VALUES ('操作 B')");
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    throw $e;
}
?>

方案二:MySQLi(僅限 MySQL,程序式/物件導向雙模式)

<?php
// 物件導向風格
$mysqli = new mysqli('localhost', 'root', '', 'guestbook');
if ($mysqli->connect_errno) {
    die("連線失敗: " . $mysqli->connect_error);
}
$mysqli->set_charset('utf8mb4');

// 預處理查詢
$stmt = $mysqli->prepare('SELECT * FROM users WHERE id = ?');
$stmt->bind_param('i', $id);
$id = 1;
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
$stmt->close();
$mysqli->close();
?>

建立可重用的 Database 類(單例模式 + PDO)

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

    private function __construct() {
        $dsn = 'mysql:host=localhost;dbname=guestbook;charset=utf8mb4';
        $this->pdo = new PDO($dsn, 'root', '', [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => false,
        ]);
    }

    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    // 禁止克隆與反序列化
    private function __clone() {}
    public function __wakeup() {}

    public function getPdo(): PDO {
        return $this->pdo;
    }

    // 便利方法
    public function query(string $sql, array $params = []): PDOStatement {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt;
    }

    public function fetchOne(string $sql, array $params = []): ?array {
        return $this->query($sql, $params)->fetch();
    }

    public function fetchAll(string $sql, array $params = []): array {
        return $this->query($sql, $params)->fetchAll();
    }

    public function insert(string $table, array $data): int|false {
        $fields = implode(', ', array_keys($data));
        $placeholders = ':' . implode(', :', array_keys($data));
        $sql = "INSERT INTO {$table} ({$fields}) VALUES ({$placeholders})";
        $stmt = $this->pdo->prepare($sql);
        $success = $stmt->execute($data);
        return $success ? (int)$this->pdo->lastInsertId() : false;
    }
}

// 使用方式
$db = Database::getInstance();
$user = $db->fetchOne('SELECT * FROM users WHERE email = ?', ['john@example.com']);
$newId = $db->insert('users', ['username' => 'Jane', 'email' => 'jane@example.com']);
?>

重要安全提醒

  • 永遠使用預處理語句:杜絕字串拼接 SQL,防止 SQL Injection。
  • 資料庫帳號最小權限:網站帳號僅需 SELECT/INSERT/UPDATE/DELETE,勿給 DROP/ALTER 權限。
  • 錯誤訊息勿直接輸出給前端:生產環境關閉 display_errors,記錄至 log。
  • 使用 utf8mb4:支援完整 Unicode(含 Emoji),避免 utf8 截斷問題。

結語

摒棄過時的 mysql_* 函數,擁抱 PDO 或 MySQLi 的預處理機制,是撰寫安全、可維護 PHP 程式的第一步。封裝 Database 類能進一步提升開發效率,統一錯誤處理與資源管理。

copyright © 萬盛學電腦網 all rights reserved