最稳妥方案是用PDO::ATTR_STATEMENT_CLASS注入继承PDOStatement的自定义类,在execute()中记录$this->queryString(需启用PDO::ATTR_EMULATE_PREPARES=true才能看到带值SQL),或改用PDO::query()/exec()包装器记录原始SQL。

怎么让 PDO 自动记录所有执行的 SQL 语句
不能靠 PDOStatement 拦截——它本身不提供钩子,也不暴露原始 SQL。真正能记录的,是 PDO 实例在 prepare/execute 阶段的行为,得从构造和调用链下手。
最稳妥的做法:用 PDO::ATTR_STATEMENT_CLASS 注入自定义语句类,把 execute() 和 prepare() 包一层。不是“拦截”,而是“替换实现”。
- 必须继承
PDOStatement,否则 PDO 内部会拒绝注册 - 重写
execute()时,$this->queryString是预处理后的完整 SQL(含参数值,如果用了 emulated prepare) - 若启用了
PDO::ATTR_EMULATE_PREPARES = true(默认 MySQL),才能拿到带值的 SQL;关掉的话只能拿到带问号的模板 - 日志建议写到
error_log()或 PSR-3 logger,别直接 echo/print,避免干扰响应
PDO::ATTR_STATEMENT_CLASS 的正确写法和常见报错
写错类名、没声明构造函数、忘记 extends PDOStatement,都会导致 PDOException: SQLSTATE[HY000]: General error 或静默失败。
正确结构示例:
class LoggingPDOStatement extends PDOStatement
{
protected $logger;
<pre class="brush:php;toolbar:false;">protected function __construct($logger) {
$this->logger = $logger;
}
public function execute($bound_params = null) {
if ($this->queryString) {
$sql = $this->queryString;
// 注意:只有 emulate_prepared=true 时,$sql 才含实际参数值
error_log('[SQL] ' . $sql);
}
return parent::execute($bound_params);
}
}
- 类必须是 public,且构造函数参数数量要匹配(PDO 内部调用时只传 0 个参数,所以构造函数需设默认值或空参)
- 注册时第二个参数必须是 数组:
[$logger],不能是对象实例本身 - 如果类文件未提前 require,会报
Class 'LoggingPDOStatement' not found,且错误不提示具体哪行
为什么开了 emulate_prepared=false 就看不到真实参数
因为底层走的是 MySQL 原生预处理协议,SQL 模板和参数是分开发送的。PDO 在 PHP 层根本拿不到拼好的语句,$this->queryString 永远只是 SELECT * FROM user WHERE id = ?。
- 想看真实 SQL,必须开
PDO::ATTR_EMULATE_PREPARES = true(MySQL 默认就是 true) - 但开启后有兼容风险:某些复杂 SQL(如含变量占位符的存储过程调用)可能解析失败
- PostgreSQL 默认不支持 emulate,所以该方案对 pgsql 无效;得改用
PDO::ATTR_CURSOR+ 日志代理层,或上pg_query_log - 生产环境慎开 emulate,尤其当 SQL 来自不可信输入时,可能引入语法解析绕过风险
更轻量的替代方案:用 PDO::exec() 和 PDO::query() 包装器
如果你只关心简单查询(非预处理场景),绕过 PDOStatement 更省事。直接封装 PDO 实例的方法即可。
- 对
query():可直接记录$sql参数,无需处理参数绑定 - 对
exec():同样记录原始字符串,适合 DDL/DML 一次性语句 - 缺点:无法覆盖
prepare()->execute()链路,漏掉大量业务查询 - 优点:零侵入、无继承、不依赖 emulate 设置,PHP 5.6+ 全版本稳
关键点就一个:别迷信“拦截”,PDO 没给你留钩子;要么换实现(statement class),要么换位置(包装 PDO 方法),没有第三条路。日志时机和 SQL 可见性永远受制于 prepare 模式,这点容易被忽略。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











