
PHP PDO 绑定参数本身不难,但几个关键细节没注意,很容易导致查询失败、数据截断、类型失真,甚至埋下 SQL 注入隐患。
绑定参数必须在 prepare 之后、execute 之前
prepare 返回的是 PDOStatement 对象,只有它才支持 bindParam / bindValue。直接对 PDO 实例调用会报错。
- ✅ 正确:$stmt = $pdo->prepare("SELECT * FROM user WHERE id = ?");
$stmt->bindValue(1, $id, PDO::PARAM_INT);
$stmt->execute(); - ❌ 错误:$pdo->bindValue(1, $id); // Fatal error
bindParam 和 bindValue 的行为差异要分清
bindParam 是引用绑定,变量值在 execute 时才读取;bindValue 是值绑定,调用时立即复制当前值。循环中复用同一语句时尤其要注意。
- 如果循环中反复修改同一个变量再 execute,用 bindParam 可能拿到最后一次赋值(因为是引用)
- 多数场景推荐用 bindValue,避免意外覆盖;需要延迟取值(如回调生成值)才考虑 bindParam
- 对于 NULL 值,bindValue 能正确传递,bindParam 在某些 PHP 版本中可能失败,建议统一用 bindValue 处理 NULL
占位符类型与参数类型需合理匹配
PDO 不强制类型转换,但类型声明影响底层驱动处理方式(尤其是整数、布尔、NULL)。错误的类型可能导致隐式截断或比较异常。
立即学习“PHP免费学习笔记(深入)”;
- 整数字段:显式用 PDO::PARAM_INT,避免字符串形式传入被当作文本处理
- 字符串字段:默认 PDO::PARAM_STR 即可,超长内容不会自动截断(由字段定义和 MySQL sql_mode 决定)
- 布尔值:MySQL 中通常存为 TINYINT(1),用 PDO::PARAM_BOOL 会转成 0/1,但注意 PHP true/false 与数据库 1/0 映射是否符合预期
- 大文本(TEXT/BLOB):无需特殊类型,PDO::PARAM_STR 足够;若需流式写入,才用 PDO::PARAM_LOB
命名参数与问号参数不可混用
同一个 SQL 字符串里只能选一种占位符风格。混用会导致 prepare 失败或参数绑定失效。
- ✅ 支持::name 和 :email 全部命名,或 ? ? ? 全部位置
- ❌ 禁止:WHERE name = :name AND age > ? —— 这种写法在绝大多数 PDO 驱动中不被支持
- 命名参数重复使用没问题(如 WHERE a=:x AND b=:x),但问号参数只按顺序一一对应,不能跳过或复用











