PHP中的官方操作数据库PDO

PHP中的官方操作数据库PDO

PDO(PHP Data Objects)是 PHP 官方推荐的数据库抽象层 ,它支持 MySQL、PostgreSQL、SQLite 等多种数据库,提供了预处理语句 (防 SQL 注入)、事务处理等高级特性,面向对象风格,代码简洁易维护,是现代 PHP 开发的首选。

开启 PDO 扩展

在使用 PDO 前,需确认 PHP 环境已开启 pdo_mysql 扩展。创建 phpinfo.php 文件:

php 复制代码
<?php
phpinfo();
?>

访问页面,搜索 pdo_mysql,若看到 PDO Driver for MySQLenabled,则说明已开启。

若未开启,修改 php.ini,取消 extension=pdo_mysql(Windows)或 extension=pdo_mysql.so(Linux)前的注释,重启 Web 服务即可。

连接 MySQL 数据库

PDO 通过DSN(数据源名称) 连接数据库,连接时建议设置 错误模式为异常字符集为 utf8mb4,这是生产环境的标准配置。

php 复制代码
new PDO(
    string $dsn,           // DSN 字符串(包含主机、数据库名、字符集)
    string $username,      // 数据库用户名
    string $password,      // 数据库密码
    array $options = []    // 可选配置数组(错误模式、属性等)
): PDO

标准连接

php 复制代码
<?php
// 数据库配置
$host = 'localhost';
$dbname = 'test_db';
$username = 'root';
$password = 'your_password';
$charset = 'utf8mb4'; // 支持 emoji,推荐

// DSN 字符串
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";

// 配置选项:设置错误模式为异常,设置默认获取方式为关联数组
$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());
}
?>

预处理语句(防 SQL 注入)

SQL 注入是最常见的安全漏洞,PDO 的 预处理语句 是解决这个问题的最佳方案。

它的原理是:先发送 SQL 模板(带占位符),再发送参数,数据与逻辑完全分离,绝对安全。

两种占位符

PDO 支持两种占位符,推荐使用命名占位符(更直观):

  1. 命名占位符:name(如 :username:age
  2. 问号占位符?(按顺序绑定参数)

使用预处理查询数据(SELECT)

php 复制代码
<?php
// 假设已连接数据库,$pdo 是 PDO 对象

// 1. 准备 SQL 模板(使用命名占位符)
$sql = "SELECT id, username, email FROM users WHERE age > :age AND city = :city";

// 2. 预处理 SQL
$stmt = $pdo->prepare($sql);

// 3. 绑定参数并执行(方式一:直接传数组给 execute,推荐)
$params = [
    ':age' => 18,
    ':city' => '北京'
];
$stmt->execute($params);

// 4. 获取结果
// fetch():获取一条数据
// fetchAll():获取所有数据
$users = $stmt->fetchAll();

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

使用预处理插入数据(INSERT)

php 复制代码
<?php
// 假设已连接数据库

// 1. 准备 SQL
$sql = "INSERT INTO users (username, email, age) VALUES (:username, :email, :age)";
$stmt = $pdo->prepare($sql);

// 2. 绑定参数并执行
$params = [
    ':username' => '张三',
    ':email' => 'zhangsan@example.com',
    ':age' => 25
];
$stmt->execute($params);

// 3. 获取插入的自增 ID(非常常用!)
$newId = $pdo->lastInsertId();
echo "数据插入成功,新记录 ID:" . $newId;
?>

使用预处理更新 / 删除数据(UPDATE/DELETE)

php 复制代码
<?php
// 假设已连接数据库

// 更新数据
$sql = "UPDATE users SET age = :age WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute([
    ':age' => 26,
    ':id' => 1
]);

// 获取受影响的行数
$affectedRows = $stmt->rowCount();
echo "更新成功,受影响行数:" . $affectedRows;

// 删除数据同理
$sql = "DELETE FROM users WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute([':id' => 10]);
echo "删除成功,受影响行数:" . $stmt->rowCount();
?>

获取结果的常用方式

PDO 提供了多种获取结果的方式,通过 fetch()fetchAll() 的参数控制:

模式常量 说明
PDO::FETCH_ASSOC 返回关联数组(键名为字段名)
PDO::FETCH_NUM 返回索引数组(键名为数字索引)
PDO::FETCH_OBJ 返回对象(属性名为字段名)
PDO::FETCH_COLUMN 返回指定列的值(配合 fetchColumn()
php 复制代码
<?php
// 假设已连接数据库
$sql = "SELECT id, username FROM users LIMIT 2";
$stmt = $pdo->prepare($sql);
$stmt->execute();

// 1. 关联数组(默认,推荐)
$usersAssoc = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo "关联数组:<pre>";
print_r($usersAssoc);
echo "</pre>";

// 2. 对象
$stmt->execute(); // 重新执行,重置指针
$usersObj = $stmt->fetchAll(PDO::FETCH_OBJ);
echo "对象:<pre>";
print_r($usersObj);
echo "</pre>";

// 3. 获取单个值(比如获取用户总数)
$sql = "SELECT COUNT(*) FROM users";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$count = $stmt->fetchColumn(); // 获取第一列的值
echo "用户总数:" . $count;
?>

事务处理(保证数据一致性)

事务用于确保一组 SQL 操作要么全部成功,要么全部失败(例如:转账操作,A 扣钱和 B 加钱必须同时成功)。PDO 的事务操作非常简洁。

方法 说明
beginTransaction() 开启事务(关闭自动提交)
commit() 提交事务(所有操作生效)
rollBack() 回滚事务(所有操作撤销)
php 复制代码
<?php
// 假设已连接数据库

try {
    // 1. 开启事务
    $pdo->beginTransaction();

    // 2. 执行操作 1:A 账户扣 100 元
    $sql = "UPDATE accounts SET balance = balance - 100 WHERE id = 1";
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    if ($stmt->rowCount() === 0) {
        throw new Exception("A 账户扣款失败");
    }

    // 3. 执行操作 2:B 账户加 100 元
    $sql = "UPDATE accounts SET balance = balance + 100 WHERE id = 2";
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    if ($stmt->rowCount() === 0) {
        throw new Exception("B 账户加款失败");
    }

    // 4. 提交事务
    $pdo->commit();
    echo "转账成功!";
} catch (Exception $e) {
    // 5. 回滚事务(如果任何一步出错)
    $pdo->rollBack();
    echo "转账失败:" . $e->getMessage();
}
?>

错误处理:三种模式

PDO 提供了三种错误处理模式,推荐使用异常模式PDO::ERRMODE_EXCEPTION),因为它能清晰地抛出错误,方便调试和处理。

模式常量 说明
PDO::ERRMODE_SILENT 静默模式(默认,不报错,只设置错误码)
PDO::ERRMODE_WARNING 警告模式(抛出 E_WARNING 警告)
PDO::ERRMODE_EXCEPTION 异常模式(抛出 PDOException 异常,推荐)

最佳实践总结

  1. 永远使用预处理语句:这是防 SQL 注入的底线,不要拼接 SQL 字符串。
  2. 设置字符集为 utf8mb4:支持 emoji,避免中文乱码。
  3. 使用异常模式PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,方便调试。
  4. 禁用模拟预处理PDO::ATTR_EMULATE_PREPARES => false,使用 MySQL 原生预处理,更安全。
  5. 合理使用 fetchAll() :如果数据量很大(比如几万条),不要用 fetchAll(),避免内存溢出,应使用 fetch() 逐行处理。
  6. 事务处理关键操作:涉及多步数据变更(如转账、订单)时,必须使用事务保证一致性。
相关推荐
小小代码狗6 分钟前
SQLi-Labs 基础注入实战教程(Less-1 ~ Less-5and Less-9)
服务器·python·php
shencz20001 小时前
在windows10里的apache2.4加php7.2的环境下上安装Laravel5.8框架
php·laravel
止语Lab2 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活2 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困2 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn2 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530142 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908402 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金2 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛2 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构