在带有 PDO 的 PHP 中,如何检查最终的 SQL 参数化查询? [复制]

Posted

技术标签:

【中文标题】在带有 PDO 的 PHP 中,如何检查最终的 SQL 参数化查询? [复制]【英文标题】:In PHP with PDO, how to check the final SQL parametrized query? [duplicate] 【发布时间】:2010-12-19 16:04:10 【问题描述】:

php 中,当使用带有参数化查询的 PDO 访问 mysql 数据库时,如何检查最终查询(在替换所有标记之后)?

有没有办法检查数据库真正执行了什么?

【问题讨论】:

我真的希望有办法做到这一点。这困扰了我一段时间。 对于 PHP >= 5.1,看看php.net/manual/en/pdostatement.debugdumpparams.php debugDumpParams() 中有一个错误 - 输出中不包含任何值。 (轻松)检查mysql 执行的查询的唯一方法是临时启用 my.cnf 中的日志(h/t 到 @JB Hurteaux) 相关:Getting raw SQL query string from PDO prepared statements 最简单和最好的方法是使用SET global general_log = 1;SET global log_output = 'table';,然后简单地从mysql 数据库中查询SELECT * FROM mysql.general_log。它显示了准备语句和在数据库上执行的实际查询。 【参考方案1】:

我不相信你可以,但我希望有人能证明我错了。

我知道你可以打印查询,它的 toString 方法会显示没有替换的 sql。如果您正在构建复杂的查询字符串,这可能会很方便,但它不会为您提供带有值的完整查询。

【讨论】:

【参考方案2】:

使用带有参数值的预处理语句不仅仅是动态创建 SQL 字符串的另一种方法。您在数据库中创建准备好的语句,然后单独发送参数值。

所以发送到数据库的可能是PREPARE ...,然后是SET ...,最后是EXECUTE ...

您将无法获得像 SELECT * FROM ... 这样的 SQL 字符串,即使它会产生相同的结果,因为实际上从未将此类查询发送到数据库。

【讨论】:

现在接受它,因为它是有道理的。那就不方便调试了…… 有没有办法可以看到实际执行的 sql 语句的“流”?也许它仍然可以提供一些帮助,总比没有好。 $stmt->debugDumpParams(); 应该会接近。【参考方案3】:

我为打印该实际查询所做的操作有点复杂,但它有效:)

在将变量分配给我的语句的方法中,我有另一个看起来有点像这样的变量:

$this->fullStmt = str_replace($column, '\'' . str_replace('\'', '\\\'', $param) . '\'', $this->fullStmt);

其中:$column 是我的令牌$param 是分配给令牌的实际值$this->fullStmt 是我的带有替换令牌的仅打印语句

它所做的只是在真正的 PDO 分配发生时用值替换标记。

我希望我没有让你感到困惑,至少为你指明了正确的方向。

【讨论】:

好的,谢谢。在看到 Ben James 的回答后,我还考虑了一个自定义函数来手动替换令牌。当有多个令牌时不是很方便,但可能有一种方法可以自动执行此操作(如果我们可以访问所有创建的绑定)并创建一个通用函数。另一方面,我真正想要的是执行前生成的查询。创建我自己的函数并不能保证我会得到相同的结果(例如:考虑到字符串参数周围插入的引号......)【参考方案4】:

我检查查询日志以查看作为准备语句执行的确切查询。

【讨论】:

【参考方案5】:

所以我想我最终会回答我自己的问题,以便为记录提供完整的解决方案。但必须感谢 Ben James 和 Kailash Badu 提供了线索。

简答 正如 Ben James 所说:。 PHP端不存在完整的SQL查询,因为query-with-tokens和参数是分开发送到数据库的。 只有在数据库端才存在完整的查询。

即使尝试在 PHP 端创建一个替换标记的函数也不能保证替换过程与 SQL 相同(棘手的东西,如标记类型、bindValue 与 bindParam,...)

解决方法 这是我详细阐述 Kailash Badu 答案的地方。 通过记录所有 SQL 查询,我们可以看到服务器上真正运行的内容。 使用 mySQL,这可以通过更新 my.cnf(或我的 Wamp 服务器中的 my.ini)来完成,并添加如下行:

log=[REPLACE_BY_PATH]/[REPLACE_BY_FILE_NAME]

只是不要在生产环境中运行它!!!

【讨论】:

请务必重启mysql 以使my.cnf 中的更改生效。运行查询后,请务必注释掉日志行,然后再次重新启动 mysql 以停止记录。 自 PHP 版本 . 7.2 可以通过$stmt->debugDumpParams(); 实现【参考方案6】:

您也许可以使用PDOStatement->debugDumpParams。见the PHP documentation。

【讨论】:

好答案,但要显示参数值you might need a patch。 +1 这可能应该被接受为答案。它允许将 SQL 与 PHP 的其他调试跟踪混合在一起,而不必查看多个位置。【参考方案7】:

我最初避免打开日志记录来监控 PDO,因为我认为这会很麻烦,但一点也不难。不需要重启 MySQL(5.1.9 之后):

在 phpMyAdmin 或您可能拥有高 db 权限的任何其他环境中执行此 SQL:

SET GLOBAL general_log = 'ON';

在终端中,跟踪您的日志文件。我的在这里:

>sudo tail -f /usr/local/mysql/data/myMacComputerName.log

您可以使用以下终端命令搜索您的 mysql 文件:

>ps auxww|grep [m]ysqld

发现PDO转义了一切,所以不能写

$dynamicField = 'userName';
$sql = "SELECT * FROM `example` WHERE `:field` = :value";
$this->statement = $this->db->prepare($sql);
$this->statement->bindValue(':field', $dynamicField);
$this->statement->bindValue(':value', 'mick');
$this->statement->execute();

因为它创造了:

SELECT * FROM `example` WHERE `'userName'` = 'mick' ;

这没有产生错误,只是一个空的结果。相反,我需要使用

$sql = "SELECT * FROM `example` WHERE `$dynamicField` = :value";

得到

SELECT * FROM `example` WHERE `userName` = 'mick' ;

完成后执行:

SET GLOBAL general_log = 'OFF';

否则你的日志会变得很大。

【讨论】:

所以,您的查询对 SQL 注入开放,恭喜 :) 其实不行,首先,pdo 没有办法以这种方式设置动态列。这是最好的方法。其次,以这种方式动态设置字段:dynamicField='userName' 不会留下任何注入空间。只要您根据白名单验证列名,就不可能注入这种构建 sql 的方法。 设置全局 log_output = 'TABLE'; SELECT * FROM mysql.general_log; 什么?不要用这个!!!这只会在您的 SQL 查询中为 SQL 注入打开一个漏洞,并消除 PDO 本身的任何意义!【参考方案8】:

我认为使用 pdo 时查看最终查询文本的最简单方法是生成特殊错误并查看错误消息。我不知道该怎么做,但是当我在使用 pdo 的 yii 框架中出现 sql 错误时,我可以看到查询文本

【讨论】:

【参考方案9】:

最简单的方法是读取mysql执行日志文件,你可以在运行时这样做。

这里有一个很好的解释:

How to show the last queries executed on MySQL?

【讨论】:

以上是关于在带有 PDO 的 PHP 中,如何检查最终的 SQL 参数化查询? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

带有条件检查的 PHP MySQL PDO TextArea Where 子句

如何在 PHP 中使用带有 PDO 的 MySQL 用户函数?

如何使用 PHP PDO 检查 MySQL 中是不是存在表? [复制]

如何在PHP下开启PDO MySQL的扩展

带有“WHERE ... IN”查询的 PDO [重复]

如何在PHP下开启PDO MySQL的扩展