Retrieve (or simulate) full query from PDO prepared statement

后端 未结 5 1670
孤城傲影
孤城傲影 2020-12-01 14:50

I stumbled upon this question from two years ago.

Is there a way to get the raw SQL string executed when calling PDOStatement::execute() on a prepare

相关标签:
5条回答
  • 2020-12-01 15:15

    Afaik, PDO doesn't really expose it to you. On development servers, you could enable the general query log for MySQL (if that's what you use), with possibly more control with sql_log_off, which does require the SUPER privilege.

    0 讨论(0)
  • 2020-12-01 15:24

    If you can't get it from PDO itself, consider using a wrapper class just for PDOStatement::execute() which will log the SQL query and values, and then call execute() on the statement. You will have to refactor your code to use the new class.

    As a sidenote, I see that PDOStatement has a class variable $queryString that holds the query being used. The values will have to be retrieved from whatever's passed into execute() or bindParam().

    First some utility functions for logging:

    //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
    }
    

    Option 1: wrapper class around 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) ;
    
        }
    
    }
    

    Then your code will have to be:

    $stm = $db->prepare("SELECT * FROM table2 WHERE id = ?") ;
    
    $res = My_PDO_Utils::execute($stm, array(79)) ;
    

    instead of

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

    Thinking some more about it, you could take it one further:

    Option 2: Extend PDO and PDOStatement

    If you want to be adventurous you can extend PDOStatement to do the logging for you, and PDO to return your extended PDOStatement class. This will require the least possible refactoring, ie just change new PDO() to new MY_PDO(), but could get tricky in its implementation as you would need to explicitely define any PDOStatement functionality you need in MY_PDOStatement so it gets called properly.

    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) ;
        }
    
    
    }
    

    But now your code can be:

    $db = new My_PDO($dsn, $user, $pass) ;
    
    $stm = $db->prepare("SELECT * FROM table2 WHERE id = ?") ;
    
    $res = $stm->execute(array(79)) ;    
    $row = $stm->fetchAll() ;
    
    0 讨论(0)
  • 2020-12-01 15:27

    The best approach in my opinion is using the mysql log to show the last queries ran as getting them directly in php is a drag.

    From How to show the last queries executed on MySQL? first answer:

    Additionally, for those blessed with MySQL >= 5.1.12:

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

    Take a look at the table mysql.general_log If you prefer to output to a file:

    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';
    

    I prefer this method because:

    you're not editing the my.cnf file and potentially permanently turning on logging you're not fishing around the filesystem looking for the query log - or even worse, distracted by the need for the perfect destination. /var/log /var/data/log /opt /home/mysql_savior/var restarting the server leaves you where you started (log is off) For more information, see MySQL 5.1 Reference Manual - Server System Variables - general_log

    0 讨论(0)
  • 2020-12-01 15:28

    The following static method takes a PDO query template (an SQL query with ? and/or :name placeholders) and interpolates the parameters:

    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;
    }
    

    As indicated by the method name, you should use this for debugging purposes only.

    0 讨论(0)
  • 2020-12-01 15:32

    I believe this is mentioned in the original question that was reference in this one. However there is actually supposed to be a method for retrieving this data.

    PDOStatement::debugDumpParams

    However it isn't currently working as documented. There is a bug report and patch submitted for it here http://bugs.php.net/bug.php?id=52384 in case anyone is interested in voting on it. Until it's fixed it seems like you are left to use query logging or setting a custom statement class using the PDO::ATTR_STATEMENT_CLASS attribute.

    0 讨论(0)
提交回复
热议问题