应使用 pdo::query() 执行完全静态、无用户输入的 sql(如 "select * from config where key = 'site_name'");一旦含变量,必须改用 prepare() + execute() 防注入。

什么时候该用 PDO::query() 而不是 PDO::prepare()
当你执行的是完全静态、不带任何用户输入的 SQL(比如 "SELECT * FROM config WHERE key = 'site_name'"),直接用 query() 最省事。它内部会编译并立即执行,返回一个可遍历的 PDOStatement 对象。
但只要语句里出现变量——哪怕只是 URL 参数、表单提交值或配置项——就必须换用 prepare() + execute(),否则就是裸奔式 SQL 注入风险。
-
query()不接受参数绑定,拼字符串是唯一方式,极易出错 - 即使你手动
addslashes()或htmlspecialchars(),也防不住绕过型注入(如宽字节、十六进制编码) - MySQL 8.0+ 默认禁用某些 DDL 操作,
query()执行ALTER TABLE可能静默失败,而预处理能暴露真实错误
PDO::exec() 只能用于 INSERT/UPDATE/DELETE?
严格来说,exec() 确实只适合无结果集的写操作:它返回受影响行数(int)或 false,且**不会返回任何数据行**。误用它查数据,你会拿到空结果却以为“查到了 0 条”。
典型翻车现场:
- 用
$pdo->exec("SELECT id FROM users")—— 返回0,但其实语句根本没执行(PDO 规范明确说它不处理SELECT) - 用
$pdo->exec("DELETE FROM logs")却检查返回值是否为true—— 错!应始终用===判断:if ($count === false),因为删 0 行也返回0 - 在事务中混用
exec()和query(),可能因连接状态不同步导致部分操作未回滚
为什么 prepare() 后必须调 execute() 才能取到数据?
prepare() 只是把 SQL 发给数据库做语法解析和执行计划缓存,它不碰数据;真正触发查询的是 execute()。漏掉这一步,后续所有 fetch()、fetchAll()、rowCount() 都是空转。
常见症状:
-
rowCount()总是返回0,即使表里有匹配数据 -
fetchAll()返回空数组,fetch()返回null - 错误地认为
IS NOT NULL条件失效,其实是根本没执行
正确链路永远是:prepare() → execute() → 数据提取方法
命名参数和问号参数能不能混用?
不能。PDO 明确禁止在同一语句中同时使用 :name 和 ?。选一种风格就到底。
差异点很实际:
- 命名参数(
:user_id)适合复杂语句,参数多时易读,且可复用同一参数多次(如WHERE a = :x AND b > :x) - 问号参数(
?)位置敏感,传参数组必须严格按顺序,但兼容性略好(尤其老驱动) - PHP 7.4+ 支持
??转义字面问号,但仅限于模拟预处理模式;真要传?字符,得改用命名参数或关闭模拟
最易忽略的一点:占位符只能代表完整值,不能拆开用。比如 IN(?) 绑定 ['a','b'] 是无效的,必须动态拼 IN(?, ?) 并传对应数量参数。











