mysql菜鸟教程
18.3 使用PHP连接MySQL:PDO
在PHP中,有多种方式连接MySQL数据库,例如 mysqli 扩展和 PDO(PHP Data Objects)。其中,PDO 提供了一个统一的轻量级接口,使得无论你使用哪种数据库(MySQL、PostgreSQL、SQLite等),代码风格都保持一致,同时它支持预处理语句、事务等高级特性,是现代PHP开发中推荐的方式。
一、什么是PDO?
PDO(PHP Data Objects)是一个数据库访问抽象层。它并不直接提供数据库操作的方法,而是定义了一套统一的接口,由各个数据库的驱动去实现。因此,使用PDO可以轻松切换数据库后端,只需更改连接字符串和少量配置,而无需重写大量代码。
PDO的主要优点:
数据库无关性:代码可移植性强。
预处理语句:有效防止SQL注入。
事务支持:便于保证数据一致性。
异常处理:可通过异常捕获错误,更符合现代编程习惯。
二、启用PDO扩展
通常情况下,PHP默认已启用PDO扩展和MySQL驱动。你可以通过创建 phpinfo() 页面查看是否已启用 pdo_mysql 扩展。若未启用,需要在 php.ini 中取消以下行的注释(或添加):
extension=pdo_mysql
然后重启Web服务器。
三、建立数据库连接
使用PDO连接MySQL需要创建一个 PDO 类的实例,构造函数参数包括:
DSN(数据源名称):包含数据库类型、主机、端口、数据库名等信息。
用户名:数据库用户名。
密码:数据库密码。
选项:可选,一个数组,用于配置PDO行为。
连接示例
<?php
$dsn = 'mysql:host=localhost;port=3306;dbname=testdb;charset=utf8mb4';
$username = 'root';
$password = 'your_password';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 抛出异常
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 默认获取关联数组
PDO::ATTR_EMULATE_PREPARES => false, // 禁用预处理语句模拟,使用原生预处理
];
try {
$pdo = new PDO($dsn, $username, $password, $options);
echo "数据库连接成功!";
} catch (PDOException $e) {
die("连接失败:" . $e->getMessage());
}解释:
DSN中的 charset=utf8mb4 指定字符集,支持emoji等。
选项 ERRMODE_EXCEPTION 使PDO在错误时抛出异常,便于集中处理。
DEFAULT_FETCH_MODE 设置默认获取结果集的模式,常见的有 FETCH_ASSOC(关联数组)、FETCH_NUM(索引数组)、FETCH_OBJ(对象)。
EMULATE_PREPARES 设为 false 可以确保使用MySQL原生的预处理,提高安全性。
四、执行查询并获取结果
1. 执行查询(SELECT)
使用 query() 方法执行SQL语句,返回一个 PDOStatement 对象,然后通过 fetch() 或 fetchAll() 获取结果。
<?php
$sql = "SELECT id, name, email, age FROM users";
$stmt = $pdo->query($sql);
// 获取所有行,返回关联数组(因为设置了默认FETCH_MODE)
$users = $stmt->fetchAll();
foreach ($users as $user) {
echo $user['id'] . ': ' . $user['name'] . ' - ' . $user['email'] . '<br>';
}如果只需要一行,可以使用 fetch():
$stmt = $pdo->query("SELECT * FROM users WHERE id = 1");
$user = $stmt->fetch(); // 返回一行,或 false2. 执行修改操作(INSERT、UPDATE、DELETE)
使用 exec() 执行无结果集返回的SQL,返回受影响的行数。
<?php $sql = "DELETE FROM users WHERE age < 18"; $affectedRows = $pdo->exec($sql); echo "删除了 " . $affectedRows . " 行。";
但 exec() 不应对用户输入使用,因为它不支持参数绑定,存在SQL注入风险。安全的做法是使用预处理语句。
五、预处理语句(Prepared Statements)
预处理语句是防止SQL注入的最有效手段。它将SQL模板和参数分开处理,确保参数不会被解释为SQL代码。
使用预处理执行INSERT
<?php $sql = "INSERT INTO users (name, email, age) VALUES (:name, :email, :age)"; $stmt = $pdo->prepare($sql); $stmt->execute([ ':name' => '李四', ':email' => 'lisi@example.com', ':age' => 22 ]); echo "插入成功,新ID:" . $pdo->lastInsertId();
使用位置占位符(?)
$sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)"; $stmt = $pdo->prepare($sql); $stmt->execute(['王五', 'wangwu@example.com', 25]);
执行SELECT并绑定参数
$sql = "SELECT * FROM users WHERE age > :age"; $stmt = $pdo->prepare($sql); $stmt->execute([':age' => 18]); $users = $stmt->fetchAll();
绑定参数并指定类型
可以使用 bindValue() 或 bindParam() 更精细地控制参数类型。
$stmt = $pdo->prepare("SELECT * FROM users WHERE age > :age");
$stmt->bindValue(':age', 18, PDO::PARAM_INT);
$stmt->execute();六、事务处理
当需要执行多条语句,并确保它们全部成功或全部失败时,可以使用事务。
<?php
try {
$pdo->beginTransaction(); // 开启事务
$pdo->exec("UPDATE accounts SET balance = balance - 100 WHERE user_id = 1");
$pdo->exec("UPDATE accounts SET balance = balance + 100 WHERE user_id = 2");
$pdo->commit(); // 提交事务
echo "转账成功!";
} catch (Exception $e) {
$pdo->rollBack(); // 回滚事务
echo "转账失败:" . $e->getMessage();
}七、错误处理模式
PDO支持三种错误模式:
PDO::ERRMODE_SILENT:默认模式,不主动报错,需通过 errorCode() 和 errorInfo() 检查。
PDO::ERRMODE_WARNING:产生警告,不影响代码执行。
PDO::ERRMODE_EXCEPTION:抛出异常,推荐使用,便于统一捕获。
通过 setAttribute() 或构造函数选项设置。
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
八、完整示例:简单的用户管理
下面是一个包含增删改查的完整示例,演示如何使用PDO操作MySQL。
<?php
// config.php - 数据库配置
$config = [
'dsn' => 'mysql:host=localhost;dbname=testdb;charset=utf8mb4',
'username' => 'root',
'password' => 'your_password',
'options' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
];
// 创建PDO连接
try {
$pdo = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
} catch (PDOException $e) {
die("数据库连接失败:" . $e->getMessage());
}
// 查询所有用户
function getUsers($pdo) {
$stmt = $pdo->query("SELECT id, name, email, age FROM users");
return $stmt->fetchAll();
}
// 根据ID查询用户
function getUserById($pdo, $id) {
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
// 添加用户
function addUser($pdo, $name, $email, $age) {
$sql = "INSERT INTO users (name, email, age) VALUES (:name, :email, :age)";
$stmt = $pdo->prepare($sql);
return $stmt->execute([':name' => $name, ':email' => $email, ':age' => $age]);
}
// 更新用户
function updateUser($pdo, $id, $name, $email, $age) {
$sql = "UPDATE users SET name = :name, email = :email, age = :age WHERE id = :id";
$stmt = $pdo->prepare($sql);
return $stmt->execute([':id' => $id, ':name' => $name, ':email' => $email, ':age' => $age]);
}
// 删除用户
function deleteUser($pdo, $id) {
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
return $stmt->execute([$id]);
}
// 示例使用
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 处理表单提交,此处省略表单验证
$name = $_POST['name'];
$email = $_POST['email'];
$age = $_POST['age'];
addUser($pdo, $name, $email, $age);
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
if (isset($_GET['delete'])) {
deleteUser($pdo, $_GET['delete']);
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<title>用户管理</title>
</head>
<body>
<h1>用户列表</h1>
<table border="1">
<tr>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>年龄</th>
<th>操作</th>
</tr>
<?php foreach (getUsers($pdo) as $user): ?>
<tr>
<td><?= $user['id'] ?></td>
<td><?= htmlspecialchars($user['name']) ?></td>
<td><?= htmlspecialchars($user['email']) ?></td>
<td><?= $user['age'] ?></td>
<td>
<a href="?delete=<?= $user['id'] ?>" onclick="return confirm('确定删除?')">删除</a>
</td>
</tr>
<?php endforeach; ?>
</table>
<h2>添加用户</h2>
<form method="post">
<label>姓名: <input type="text" name="name" required></label><br>
<label>邮箱: <input type="email" name="email" required></label><br>
<label>年龄: <input type="number" name="age" required></label><br>
<input type="submit" value="添加">
</form>
</body>
</html>九、注意事项
永远不要相信用户输入:始终使用预处理语句 + 参数绑定。
选择合适的获取模式:根据需求选择关联数组、对象等。
关闭连接:PDO对象销毁时自动关闭连接,通常无需手动关闭,但在长生命周期脚本中可设为 null 以释放资源。
处理异常:使用 try-catch 捕获 PDOException 并合理处理错误。
字符集:在DSN中指定 charset=utf8mb4,避免中文乱码。
小结
PDO 提供了一个统一的数据库访问接口,支持多种数据库。
使用 PDO 的预处理语句可以有效防止SQL注入。
通过 setAttribute 可以设置错误模式和默认获取方式。
事务处理使多个操作保持原子性。
掌握PDO是PHP现代开发的基础技能之一。

发表评论
所有评论