简体   繁体   中英

PDO prepared statement in PHP using associative arrays yields wrong results

I am working on a very small PHP and MySQL application. I have a class called User for manipulating user data in the database which contains a createUser method as shown below:-

/**
* Creates a new user record in the users table for a new user
*   
* @return void
*/
public function createUser($user_id, $user_name, $location_id) {

    // query to execute
    $query = "INSERT INTO 
                      users(user_id,user_name,location_id)
                      VALUES(:id,:name,:location)";

    // query parameters
    $parameters = array (
        ':id' => $user_id,
        ':name' => $user_name,
        ':location' => $location_id
    );

    $databaseInteractions = new DatabaseInteractions();
    $databaseInteractions->executeUpdate($this->_connection, $query, $parameters);

}

I have another class that will contain common methods for interacting with the database called DatabaseInteractions as shown in the above code. This class contains a method called executeUpdate for executing DML queries as shown below:-

/**
 * A function for executing DML queries using a prepared statement
 * @param $connection The database connection object
 * @param $query The dml query to be executed
 * @param $parameters The input parameters for the query as a hash 
 */
public function executeUpdate($connection,$query, $parameters) {

    if ($stmt = $connection->prepare($query)) {

        //bind query parameters
        foreach ($parameters as $key => $value) {
            $stmt->bindParam($key, $value);
        }
        //begin transaction
        $connection->beginTransaction();

        $stmt->execute();
        $stmt->closeCursor();

        //commit transaction
        $connection->commit();
    }
}    

When i call the createUser method in the User class as shown below,

$user->createUser(3,"NewUser",1);

The values inserted in the database are as follows:-

   user_id   user_name  location_id
    1              1             1

I spent some time debugging the code but still can't seem to find the reason why this is happening. user_id is the PK and is an int. user_name is varchar and location_id is FK and is int.

I am a Java developer and new to PHP so any inputs related to php naming conventions, coding standards, etc are always welcome.

Answer: reference semantics gotcha.

The second parameter of bindParam is passed by reference:

Binds a PHP variable to a corresponding named or question mark placeholder in the SQL statement that was use to prepare the statement. Unlike PDOStatement::bindValue(), the variable is bound as a reference and will only be evaluated at the time that PDOStatement::execute() is called.

Therefore, when the statement is executed all three parameters will evaluate to whatever the last value of $value was -- in this case, 1 .

To solve the problem, use the optional parameter of execute instead of explicitly binding the parameters:

public function executeUpdate($connection,$query, $parameters) {
    if ($stmt = $connection->prepare($query)) {
        $connection->beginTransaction();

        $stmt->execute($parameters);
        $stmt->closeCursor();

        //commit transaction
        $connection->commit();
    }
}    

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM