PHP: PDO数据库操作

MySQL 命令学会了,但在 PHP 中操作数据库才是目标。PDO(PHP Data Objects)是 PHP 推荐的标准数据库抽象层——一套代码,12 种数据库通用。记住:只学 PDO,不学 MySQLi

1. 为什么是 PDO?

PDO MySQLi
支持数据库 12 种(MySQL/PostgreSQL/SQLite...) 仅 MySQL
命名参数 :name ❌ 仅 ?
获取关联数组 PDO::FETCH_ASSOC
获取对象 PDO::FETCH_OBJ ❌(需额外设置)
异常模式 ✅ 可设 ✅ 可设
现代性 🏆 社区推荐标准 遗留

2. PDO 连接

PHP
<?php
$host = 'localhost';
$dbname = 'myblog';
$username = 'root';
$password = '';
$charset = 'utf8mb4';

$dsn = "mysql:host={$host};dbname={$dbname};charset={$charset}";

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 "✅ 数据库连接成功!<br>";
} catch (PDOException $e) {
    die("❌ 连接失败:" . $e->getMessage());
}
?>
连接选项 含义 为什么
ERRMODE_EXCEPTION 错误抛异常 不沉默失败
FETCH_ASSOC 默认关联数组 最常用格式
EMULATE_PREPARES => false 用真实预处理 真正的安全
💡 提示: 这三个 PDO 选项是标准配置,每次创建 PDO 连接都加上。EMULATE_PREPARES => false 确保使用 MySQL 原生的预处理语句,而不是 PDO 模拟的。


3. query() — 执行 SELECT 查询

PHP
<?php
// 简单查询
$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll();

// 遍历结果
foreach ($users as $user) {
    echo "ID: {$user['id']}, 用户名: {$user['username']}<br>";
}

// 只取一条
$stmt = $pdo->query("SELECT * FROM users WHERE id = 1");
$user = $stmt->fetch();

echo $user['username'];  // 小明
?>

(1) 获取模式

PHP
<?php
$stmt = $pdo->query("SELECT id, username FROM users");

// 关联数组(默认)
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo $row['username'];

// 数字索引
$row = $stmt->fetch(PDO::FETCH_NUM);
echo $row[1];

// 对象
$row = $stmt->fetch(PDO::FETCH_OBJ);
echo $row->username;

// 获取单列
$usernames = $stmt->fetchAll(PDO::FETCH_COLUMN, 1);
print_r($usernames);  // ['小明', '小红', ...]
?>

4. 预处理语句(核心!)

预处理是防 SQL 注入的关键:

▶ 示例:安全的用户查询

PHP
<?php
// ❌ 危险:直接拼接 SQL(SQL 注入漏洞)
// $stmt = $pdo->query("SELECT * FROM users WHERE username = '$input'");

// ✅ 安全:预处理语句
$username = $_GET['username'] ?? '';

$sql = "SELECT * FROM users WHERE username = :username";
$stmt = $pdo->prepare($sql);
$stmt->execute(['username' => $username]);
$user = $stmt->fetch();

if ($user) {
    echo "找到用户:{$user['username']}";
} else {
    echo "用户不存在";
}
?>
▶ 试一试

(1) INSERT

PHP
<?php
$sql = "INSERT INTO users (username, email, password, age) 
        VALUES (:username, :email, :password, :age)";

$stmt = $pdo->prepare($sql);
$stmt->execute([
    'username' => '小李',
    'email'    => 'xiaoli@example.com',
    'password' => password_hash('123456', PASSWORD_DEFAULT),
    'age'      => 24,
]);

$newId = $pdo->lastInsertId();
echo "新用户 ID:{$newId}";
?>

(2) UPDATE

PHP
<?php
$sql = "UPDATE users SET age = :age WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute(['age' => 27, 'id' => 1]);

echo "影响了 {$stmt->rowCount()} 行";
?>

(3) DELETE

PHP
<?php
$sql = "DELETE FROM users WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute(['id' => 5]);

echo "删除了 {$stmt->rowCount()} 行";
?>

5. 完整 CRUD 实战

▶ 示例:用户管理页面

PHP 📖 仅展示
<?php
// 连接数据库(同上,省略)
$pdo = new PDO($dsn, $username, $password, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $action = $_POST['action'] ?? '';
    
    if ($action === 'add') {
        $stmt = $pdo->prepare(
            "INSERT INTO users (username, email, password, age) 
             VALUES (:username, :email, :password, :age)"
        );
        $stmt->execute([
            'username' => $_POST['username'],
            'email'    => $_POST['email'],
            'password' => password_hash($_POST['password'], PASSWORD_DEFAULT),
            'age'      => (int)$_POST['age'],
        ]);
        echo "<p style='color:green'>用户添加成功!</p>";
    } elseif ($action === 'delete') {
        $stmt = $pdo->prepare("DELETE FROM users WHERE id = :id");
        $stmt->execute(['id' => (int)$_POST['id']]);
        echo "<p style='color:green'>用户已删除!</p>";
    }
}

// 查询所有用户
$users = $pdo->query("SELECT * FROM users ORDER BY id DESC")->fetchAll();
?>

<h2>用户管理</h2>

<table border="1" cellpadding="8">
    <tr><th>ID</th><th>用户名</th><th>邮箱</th><th>年龄</th><th>注册时间</th><th>操作</th></tr>
    <?php foreach ($users as $u): ?>
    <tr>
        <td><?= $u['id'] ?></td>
        <td><?= htmlspecialchars($u['username']) ?></td>
        <td><?= htmlspecialchars($u['email']) ?></td>
        <td><?= $u['age'] ?></td>
        <td><?= $u['created_at'] ?></td>
        <td>
            <form method="POST" style="display:inline">
                <input type="hidden" name="action" value="delete">
                <input type="hidden" name="id" value="<?= $u['id'] ?>">
                <button type="submit" onclick="return confirm('确认删除?')">删除</button>
            </form>
        </td>
    </tr>
    <?php endforeach; ?>
</table>

<h3>添加用户</h3>
<form method="POST">
    <input type="hidden" name="action" value="add">
    <input type="text" name="username" placeholder="用户名" required>
    <input type="email" name="email" placeholder="邮箱" required>
    <input type="password" name="password" placeholder="密码" required>
    <input type="number" name="age" placeholder="年龄">
    <button type="submit">添加</button>
</form>
逻辑代码 56 行(超过 40 行限制,仅展示)

❓ 常见问题

Q PDO 和 MySQLi 选哪个?
A PDO。它支持 12 种数据库(换数据库不用重写代码),预处理语法更简洁(:name 命名参数),是 PHP 社区公认标准。MySQLi 只适合维护老项目。
Q fetch()fetchAll() 的区别?
A fetch() 取一条(返回一次调用一行),适合处理大的结果集。fetchAll() 一次性取全部到数组里,简单但数据太多会占内存。

📖 小节

📝 作业

  1. 用 PDO 连接 myblog 数据库,查询 users 表所有数据并以表格展示。
  2. 写一个注册页面,表单包含用户名/邮箱/密码,用 PDO 预处理语句插入新用户(别忘了 password_hash)。
  3. 给第1题的表格添加每行的"删除"按钮,提交后删除对应的用户记录。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏