PHP PDO 数据库入门:连接、查询与预处理语句

做动态网站绕不开数据库。PHP 操作 MySQL 推荐用 PDO 扩展——它是一套统一的数据库接口,将来换数据库时代码基本不用大改,而且自带的预处理语句能有效防 SQL 注入。这篇带你从连接到增删改查走一遍完整流程。

建立连接

连接数据库就是创建一个 PDO 对象,把连接信息写在 DSN 里:

<?php
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8mb4';
$user = 'root';
$pass = 'your_password';

try {
    $pdo = new PDO($dsn, $user, $pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    echo '连接成功';
} catch (PDOException $e) {
    echo '连接失败:' . $e->getMessage();
}

两个细节值得养成习惯:一是把错误模式设为 ERRMODE_EXCEPTION,出问题时会抛异常,方便定位;二是用 try/catch 包住连接过程,避免把数据库账号信息直接暴露在报错页面上。

查询数据:query 与 fetch

查询用 query() 发 SQL,结果用 fetchAll() 一次性取回来:

<?php
$stmt = $pdo->query('SELECT id, name, score FROM students ORDER BY score DESC');
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($rows as $row) {
    echo $row['id'] . ' ' . $row['name'] . ' ' . $row['score'] . PHP_EOL;
}
// 输出类似:
// 3 王小虎 96
// 1 张三 88

PDO::FETCH_ASSOC 表示以关联数组返回,直接用列名取值,比默认的数字下标可读性好得多。

预处理语句:防注入的关键

如果 SQL 里要用到用户输入的数据,千万不要直接拼字符串,正确姿势是预处理:

<?php
// 有风险的做法(仅作对比,别这么写):
// $pdo->query("SELECT * FROM students WHERE name = '$name'");

// 正确的做法:
$stmt = $pdo->prepare('SELECT id, name FROM students WHERE score > :score');
$stmt->bindValue(':score', 60, PDO::PARAM_INT);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

echo '共 ' . count($rows) . ' 人及格'; // 输出:共 32 人及格

prepare 先把 SQL 骨架发给数据库,bindValue 再把参数单独送过去。用户输入从头到尾不会被当成 SQL 代码解析,单引号之类的特殊字符也就造不成破坏了。

写入数据:增删改同样用预处理

INSERT、UPDATE、DELETE 也推荐预处理写法,参数用冒号占位符:

<?php
$stmt = $pdo->prepare('INSERT INTO students (name, score) VALUES (:name, :score)');
$stmt->bindValue(':name', '李雷', PDO::PARAM_STR);
$stmt->bindValue(':score', 92, PDO::PARAM_INT);
$stmt->execute();

echo '新同学的 id 是 ' . $pdo->lastInsertId(); // 输出:新同学的 id 是 12

新插入行的自增 id 可以用 lastInsertId() 拿到,写注册、发帖这类功能时会经常用到它。想知道这条语句到底影响了几行,可以在 execute() 之后读 $stmt->rowCount()。

小结

这篇走通了 PDO 的基本流程:用 DSN 建连接并开启异常模式;用 query 加 fetchAll 做简单查询;用 prepare 加 bindValue 处理带参数的读写。记住一条原则:只要 SQL 里有变量,就上预处理语句,这个习惯能挡掉绝大多数注入风险。