Getting raw SQL query string from PDO prepared statements

前端 未结 16 877
心在旅途
心在旅途 2020-11-22 06:56

Is there a way to get the raw SQL string executed when calling PDOStatement::execute() on a prepared statement? For debugging purposes this would be extremely useful.

相关标签:
16条回答
  • 2020-11-22 07:06

    preg_replace didn't work for me and when binding_ was over 9, binding_1 and binding_10 was replaced with str_replace (leaving the 0 behind), so I made the replacements backwards:

    public function interpolateQuery($query, $params) {
    $keys = array();
        $length = count($params)-1;
        for ($i = $length; $i >=0; $i--) {
                $query  = str_replace(':binding_'.(string)$i, '\''.$params[$i]['val'].'\'', $query);
               }
            // $query  = str_replace('SQL_CALC_FOUND_ROWS', '', $query, $count);
            return $query;
    

    }

    Hope someone finds it useful.

    0 讨论(0)
  • 2020-11-22 07:11

    A bit late probably but now there is PDOStatement::debugDumpParams

    Dumps the informations contained by a prepared statement directly on the output. It will provide the SQL query in use, the number of parameters used (Params), the list of parameters, with their name, type (paramtype) as an integer, their key name or position, and the position in the query (if this is supported by the PDO driver, otherwise, it will be -1).

    You can find more on the official php docs

    Example:

    <?php
    /* Execute a prepared statement by binding PHP variables */
    $calories = 150;
    $colour = 'red';
    $sth = $dbh->prepare('SELECT name, colour, calories
        FROM fruit
        WHERE calories < :calories AND colour = :colour');
    $sth->bindParam(':calories', $calories, PDO::PARAM_INT);
    $sth->bindValue(':colour', $colour, PDO::PARAM_STR, 12);
    $sth->execute();
    
    $sth->debugDumpParams();
    
    ?>
    
    0 讨论(0)
  • 2020-11-22 07:15

    Added a little bit more to the code by Mike - walk the values to add single quotes

    /**
     * Replaces any parameter placeholders in a query with the value of that
     * parameter. Useful for debugging. Assumes anonymous parameters from 
     * $params are are in the same order as specified in $query
     *
     * @param string $query The sql query with parameter placeholders
     * @param array $params The array of substitution parameters
     * @return string The interpolated query
     */
    public function interpolateQuery($query, $params) {
        $keys = array();
        $values = $params;
    
        # build a regular expression for each parameter
        foreach ($params as $key => $value) {
            if (is_string($key)) {
                $keys[] = '/:'.$key.'/';
            } else {
                $keys[] = '/[?]/';
            }
    
            if (is_array($value))
                $values[$key] = implode(',', $value);
    
            if (is_null($value))
                $values[$key] = 'NULL';
        }
        // Walk the array to see if we can add single-quotes to strings
        array_walk($values, create_function('&$v, $k', 'if (!is_numeric($v) && $v!="NULL") $v = "\'".$v."\'";'));
    
        $query = preg_replace($keys, $values, $query, 1, $count);
    
        return $query;
    }
    
    0 讨论(0)
  • 2020-11-22 07:16

    You can extend PDOStatement class to capture the bounded variables and store them for later use. Then 2 methods may be added, one for variable sanitizing ( debugBindedVariables ) and another to print the query with those variables ( debugQuery ):

    class DebugPDOStatement extends \PDOStatement{
      private $bound_variables=array();
      protected $pdo;
    
      protected function __construct($pdo) {
        $this->pdo = $pdo;
      }
    
      public function bindValue($parameter, $value, $data_type=\PDO::PARAM_STR){
        $this->bound_variables[$parameter] = (object) array('type'=>$data_type, 'value'=>$value);
        return parent::bindValue($parameter, $value, $data_type);
      }
    
      public function bindParam($parameter, &$variable, $data_type=\PDO::PARAM_STR, $length=NULL , $driver_options=NULL){
        $this->bound_variables[$parameter] = (object) array('type'=>$data_type, 'value'=>&$variable);
        return parent::bindParam($parameter, $variable, $data_type, $length, $driver_options);
      }
    
      public function debugBindedVariables(){
        $vars=array();
    
        foreach($this->bound_variables as $key=>$val){
          $vars[$key] = $val->value;
    
          if($vars[$key]===NULL)
            continue;
    
          switch($val->type){
            case \PDO::PARAM_STR: $type = 'string'; break;
            case \PDO::PARAM_BOOL: $type = 'boolean'; break;
            case \PDO::PARAM_INT: $type = 'integer'; break;
            case \PDO::PARAM_NULL: $type = 'null'; break;
            default: $type = FALSE;
          }
    
          if($type !== FALSE)
            settype($vars[$key], $type);
        }
    
        if(is_numeric(key($vars)))
          ksort($vars);
    
        return $vars;
      }
    
      public function debugQuery(){
        $queryString = $this->queryString;
    
        $vars=$this->debugBindedVariables();
        $params_are_numeric=is_numeric(key($vars));
    
        foreach($vars as $key=>&$var){
          switch(gettype($var)){
            case 'string': $var = "'{$var}'"; break;
            case 'integer': $var = "{$var}"; break;
            case 'boolean': $var = $var ? 'TRUE' : 'FALSE'; break;
            case 'NULL': $var = 'NULL';
            default:
          }
        }
    
        if($params_are_numeric){
          $queryString = preg_replace_callback( '/\?/', function($match) use( &$vars) { return array_shift($vars); }, $queryString);
        }else{
          $queryString = strtr($queryString, $vars);
        }
    
        echo $queryString.PHP_EOL;
      }
    }
    
    
    class DebugPDO extends \PDO{
      public function __construct($dsn, $username="", $password="", $driver_options=array()) {
        $driver_options[\PDO::ATTR_STATEMENT_CLASS] = array('DebugPDOStatement', array($this));
        $driver_options[\PDO::ATTR_PERSISTENT] = FALSE;
        parent::__construct($dsn,$username,$password, $driver_options);
      }
    }
    

    And then you can use this inherited class for debugging purpouses.

    $dbh = new DebugPDO('mysql:host=localhost;dbname=test;','user','pass');
    
    $var='user_test';
    $sql=$dbh->prepare("SELECT user FROM users WHERE user = :test");
    $sql->bindValue(':test', $var, PDO::PARAM_STR);
    $sql->execute();
    
    $sql->debugQuery();
    print_r($sql->debugBindedVariables());
    

    Resulting in

    SELECT user FROM users WHERE user = 'user_test'

    Array ( [:test] => user_test )

    0 讨论(0)
  • 2020-11-22 07:17

    You can use sprintf(str_replace('?', '"%s"', $sql), ...$params);

    Here is an example:

    function mysqli_prepared_query($link, $sql, $types='', $params=array()) {
        echo sprintf(str_replace('?', '"%s"', $sql), ...$params);
        //prepare, bind, execute
    }
    
    $link = new mysqli($server, $dbusername, $dbpassword, $database);
    $sql = "SELECT firstname, lastname FROM users WHERE userage >= ? AND favecolor = ?";
    $types = "is"; //integer and string
    $params = array(20, "Brown");
    
    if(!$qry = mysqli_prepared_query($link, $sql, $types, $params)){
        echo "Failed";
    } else {
        echo "Success";
    }
    

    Note this only works for PHP >= 5.6

    0 讨论(0)
  • 2020-11-22 07:19

    Somewhat related... if you are just trying to sanitize a particular variable you can use PDO::quote. For example, to search for multiple partial LIKE conditions if you're stuck with a limited framework like CakePHP:

    $pdo = $this->getDataSource()->getConnection();
    $results = $this->find('all', array(
        'conditions' => array(
            'Model.name LIKE ' . $pdo->quote("%{$keyword1}%"),
            'Model.name LIKE ' . $pdo->quote("%{$keyword2}%"),
        ),
    );
    
    0 讨论(0)
提交回复
热议问题