从PDO预处理语句中检索(或模拟)完整查询

时间:2010-09-20 18:54:40

标签: php pdo prepared-statement

两年前我偶然发现了this question

  

有没有办法在预准备语句上调用PDOStatement :: execute()时获取原始SQL字符串?出于调试目的,这将非常有用。

获胜的回答表明

  

[...]你也可以得到你想要的东西   设置PDO属性   PDO :: ATTR_EMULATE_PREPARES。在这   模式,PDO将参数插入到   SQL查询并发送整个   执行()时查询。

但它没有提到如何获得生成的查询字符串。我知道这是一个糟糕的主意,但在调试模式下这并不会让我感到困扰。有人知道怎么做吗?

PS如果有某种方式我可以重新开放/引起对原来两年前主题的关注,而不是打开一个新主题,请告诉我。

5 个答案:

答案 0 :(得分:13)

我相信在这个问题中引用的原始问题中提到了这一点。然而 实际上应该是一种检索这些数据的方法。

PDOStatement::debugDumpParams

然而,它目前没有记录在案。如果有人有兴趣投票,可以在此处http://bugs.php.net/bug.php?id=52384提交错误报告和补丁。在修复之前,您似乎只能使用查询日志记录或使用PDO :: ATTR_STATEMENT_CLASS属性设置自定义语句类。

答案 1 :(得分:2)

Afaik,PDO并没有真正向您揭露它。在开发服务器上,您可以启用MySQL的常规查询日志(如果这是您使用的),可能需要更多的sql_log_off控制,这需要SUPER权限。

答案 2 :(得分:2)

如果您无法从PDO本身获取它,请考虑使用仅用于PDOStatement::execute()的包装类,它将记录SQL查询和值,然后在语句上调用execute()。您将不得不重构代码以使用新类。

作为旁注,我看到PDOStatement有一个类变量$queryString,用于保存正在使用的查询。必须从传递到execute()bindParam()的任何内容中检索这些值。

首先是一些用于记录的实用程序函数:

//removes newlines and extra spaces from print_r output
function str_squeeze($str) {

    if (is_array($str)) {
        $str = print_r($str, true);
    }

    $str = preg_replace('/[(\r)?\n|\t]/', ' ', $str);
    $str = trim(ereg_replace(' +', ' ', $str));
    return $str;
}

function logger($str) {
    //log it somewhere
}

选项1:围绕PDOStatement的包装类

class My_PDO_Utils {

    public static function execute(PDOStatement &$stm, $values = array()) {
        logger("QUERY: " . $stm->queryString . ", values = " . str_squeeze($values)) ;
        return $stm->execute($values) ;

    }

}

然后你的代码必须是:

$stm = $db->prepare("SELECT * FROM table2 WHERE id = ?") ;

$res = My_PDO_Utils::execute($stm, array(79)) ;

而不是

$res = $stm->execute(array(79)) ;

进一步思考它,你可以更进一步:

选项2:扩展PDO和PDOStatement

如果你想要冒险,可以扩展PDOStatement为你做日志记录,PDO返回扩展的PDOStatement类。这将需要尽可能少的重构,即只需将new PDO()更改为new MY_PDO(),但在实现中可能会变得棘手,因为您需要在MY_PDOStatement中明确定义所需的任何PDOStatement功能,以便正确调用它。

class My_PDO extends PDO {

    public function prepare($sql, $options = array()) {

        //do normal call
        $stm = parent::prepare($sql, $options) ;

        //encapsulate it in your pdostatement wrapper
        $myStm = new My_PDOStatement() ;
        $myStm->stm = $stm ;

        return $myStm ;

    }

}

class My_PDOStatement extends PDOStatement {

    /**
     *
     * @var PDOStatement
     */
    public $stm ;

    public function execute($values) {

        logger("QUERY: " . $this->stm->queryString . ", values = " . str_squeeze($values)) ;
        return $this->stm->execute($values) ;

    }

    public function fetchAll($fetch_style = PDO::FETCH_BOTH, $column_index = 0, $ctor_args = array()) {
        return $this->stm->fetchAll($fetch_style, $column_index, $ctor_args) ;
    }


}

但现在你的代码可以是:

$db = new My_PDO($dsn, $user, $pass) ;

$stm = $db->prepare("SELECT * FROM table2 WHERE id = ?") ;

$res = $stm->execute(array(79)) ;    
$row = $stm->fetchAll() ;

答案 3 :(得分:1)

在我看来,最好的方法是使用mysql日志来显示最后运行的查询,因为直接在php中获取它们是一种拖累。

How to show the last queries executed on MySQL?第一个回答:

此外,对于那些有MySQL> = 5.1.12:

的人
SET GLOBAL log_output = 'TABLE';
SET GLOBAL general_log = 'ON';

看一下mysql.general_log表 如果您希望输出到文件:

SET GLOBAL log_output = "FILE"; which is set by default.
SET GLOBAL general_log_file = "/path/to/your/logfile.log"
SET GLOBAL general_log = 'ON';

我更喜欢这种方法,因为:

您没有编辑my.cnf文件,可能会永久打开日志记录 你不是在寻找查询日志的文件系统周围钓鱼 - 或者更糟糕的是,由于需要完美的目的地而分散注意力。 / var / log / var / data / log / opt / home / mysql_savior / var 重新启动服务器会使您离开原点(日志已关闭) 有关更多信息,请参阅MySQL 5.1参考手册 - 服务器系统变量 - general_log

答案 4 :(得分:0)

以下静态方法采用PDO查询模板(带有?和/或:name占位符的SQL查询)并插入参数:

static public function getDebugFullQuery($query, $params = array()){
    if(is_array($params) && count($params)){

        $search = [];
        $replace = [];

        foreach($params as $k => $p){
            $pos = strpos($query, ":{$k}");
            if($pos !== false){
                $query = substr($query, 0, $pos) . "%!-!{$k}!-!%" . substr($query, $pos + strlen($k) + 1);
            }
            else {
                $pos = strpos($query, "?");
                if($pos !== false){
                    $query = substr($query, 0, $pos) . "%!-!{$k}!-!%" . substr($query, $pos + 1);
                }
                else {
                    break;
                }
            }

            $search[] = "%!-!{$k}!-!%";
            $replace[] = "'" . str_replace(array("\r", "\n", "'"), array("\\\\r", "\\\\n", "\\'"), $p) . "'";
        }

        if(count($search)){
            $query = str_replace($search, $replace, $query);
        }
    }

    return $query;
}

如方法名所示,您应该仅将其用于调试目的。