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>
❓ 常见问题
Q PDO 和 MySQLi 选哪个?
A PDO。它支持 12 种数据库(换数据库不用重写代码),预处理语法更简洁(
:name 命名参数),是 PHP 社区公认标准。MySQLi 只适合维护老项目。Q
fetch() 和 fetchAll() 的区别?A
fetch() 取一条(返回一次调用一行),适合处理大的结果集。fetchAll() 一次性取全部到数组里,简单但数据太多会占内存。📖 小节
- PDO 是 PHP 官方推荐的数据库抽象层,支持 12 种数据库
- 连接选项三项标配:ERRMODE_EXCEPTION / FETCH_ASSOC / EMULATE_PREPARES=false
query()执行 SELECT,fetchAll()取全部,fetch()取一条- 预处理语句(prepare/execute)防 SQL 注入
:name命名占位符比?更清晰lastInsertId()获取最后插入的 ID,rowCount()受影响行数
📝 作业
- 用 PDO 连接 myblog 数据库,查询 users 表所有数据并以表格展示。
- 写一个注册页面,表单包含用户名/邮箱/密码,用 PDO 预处理语句插入新用户(别忘了
password_hash)。 - 给第1题的表格添加每行的"删除"按钮,提交后删除对应的用户记录。