PHP PDO BindParam/BindValue Double Quote Bug

2.4k views Asked by At

I'm getting very frustrating results when trying to bind parameters to a PDO prepared statement.

The result is this error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''items'' at line 1

The error clearly shows that the table 'items' is being quoted with single quotes when it shouldn't be. I've put together a little test below. As you can see, I'm not placing the parameters in quotes in the statement to begin with, which would probably be most people's initial guess. I know it has something to do with the BindParam/BindValue (I've tried both with same results) function because if you bypass the bindParams function by setting $params to null and replacing ':table' with 'items' in the statement, it works perfectly fine.

<?php

echo 'started test...';

//connect to database

try {
    $dbHandle = new PDO('mysql:dbname=mydatabase;host=mysql.mywebsite.com', 'myuser', 'mypass');   
} catch (PDOException $e) {
    echo 'Database connection failed: ' . $e->getMessage();    
}

//print out the contents of table 'items'

print_r(query("SELECT * FROM :table", array("table" => "items"), $dbHandle));

//the query() function used above

function query($query_str, $params = null, $dbHandle) {

    $stmt_obj = $dbHandle->prepare($query_str);

    if($params != null) {           
        bindParams($stmt_obj, $params);
    }

    $stmt_obj->execute();

    //debug stuff
    echo '<pre>';   
    echo 'ERROR: ';
    $error = $stmt_obj->errorInfo();        
    echo $error[2].'<br /><br />';
    echo 'DEBUG DUMP:<br />';
    $stmt_obj->debugDumpParams();
    echo '</pre>';

    if (preg_match("/SELECT/i", $query_str)) {
        $result = array();      
        while ($row = $stmt_obj->fetch(PDO::FETCH_ASSOC)) {         
            array_push($result, $row);
        }
        unset($stmt_obj);
        return $result;
    }   

}

function bindParams($stmt, $params) {   
    if(is_object($stmt) && ($stmt instanceof PDOStatement))
    {           
        foreach($params as $key => $value)
        {            
            if(is_int($value)) {
                $param = PDO::PARAM_INT; 
            } elseif(is_bool($value)) {
                $param = PDO::PARAM_BOOL;
            } elseif(is_null($value)) {
                $param = PDO::PARAM_NULL;
            } elseif(is_string($value)) {
                $param = PDO::PARAM_STR;
            } else {
                $param = FALSE;
            }
            if($param) {                                                        
                $stmt->bindValue(":$key", $value, $param);                  
            }
        }
    }

Anyone care to put me out of my misery and point out something really obvious that I'm missing?

1

There are 1 answers

1
Francis Avila On BEST ANSWER

The symbols in prepared statements are only placeholders for values, not identifiers, so you can't create a prepared statement with a dynamic column or table name. All database references need to be resolved at prepare-time. Even if this were not true, how would PDO/MySQL know whether the :table placeholder was meant to be a literal or an identifier? They have different quoting rules.

This restriction is common to prepared statements in all database systems and all APIs. It is certainly not a PDO bug.