PDO prepared statement in PHP using associative arrays yields wrong results - php

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

Related

Method some_method() always returns false

I have following two classes called One and Two respectively. Class One has database stuff in it. Where method connect() returns a variable (a property) that is the database handler ($dbh). I have used connect() literally hundreds of times and it works as anticipated. Here too, (in method query()) it should work correctly. Now the work the method query() has to do is to prepare a statement ($query in this case) and execute it, after which it fetches data as an associative array and returns the data. I call this method in multiple methods, and works fine in all except in the method some_method() in class Two.
class One {
protected function query($sql, $params=[]){
$query = $this->connect()->prepare($sql); // Method connect() returns database handler ($dbh)
$query->execute($params);
$storage = $query->fetch(); // Fetch as an associative array
return $storage;
}
}
Now here in some_method() in class Two, method query()(from class One), is called where a variable $sql and also an associative array $params are declared as shown in the code below. All this method has to do is to call the method query(), with the parameters $sql and $params, and store the result in $storage. If $storage==false, return false and if $storage has some value other than false, return true. And this result (i.e., true or false) depends on if the database has an entry in the column class_date as specified in $params['date']. Now here comes the problem, irrespective of if the database has that value in that column, the method some_method(), always returns false. I don't know what mistake I'm doing here. Can someone point it out please.
class Two extends class One{
public function some_method($date){
$sql = 'SELECT class_date FROM table_name WHERE id=:id AND class_date=:date';
$params = [
'id' => $this->user_id,
'date' => $date
];
$array = $this->query($sql, $params);
return ($array==false) ? false : true;
}
}

PHP7/PDO: Returning PDOStatement Object and fetch data later

I am currently in the process of updating my web application from PHP 5.6 to PHP 7, therefore I need to alter the database abstraction class that has been using the old MySQL-Extension.
The class has a method "Query()" where SQL statements are being prepared and executed and a method "Fetch()" which fetches a single row and returns it. See the simplified versions:
function Query($query='')
{
$result = $this->connection->Prepare($query);
$result->Execute();
return $result;
}
function Fetch($resource=null)
{
return $resource->Fetch(PDO::FETCH_ASSOC);
}
So basically what I am trying to do is this:
$statement = Query('SELECT * FROM Test');
$firstRow = Fetch($statement);
print_r($statement) shows me this:
PDOStatement Object
(
[queryString] => SELECT * FROM Test
)
But when I call "Fetch", there is no row returned. Can someone tell if it's possible to return PDOStatement objects and pass them into other functions/methods for "later use"?
PS: Of course I know that this is not best practice but I am trying to avoid having to alter thousands of lines of code in the application.

PDO clear bound parameters

I create a sub object of "shortcut" methods such as return_all and other methods to run with PDO as a database object.
In short, all of my queries (be it insert, update, delete or select) goes through 2 different methods, the last one I call execute which looks like this:
/*
* execute_prepared_sql() will prepare SQL based on the users parameters:
*
*/
private function execute_prepared_sql() {
if ($this->prepare_sql() == true) {
try {
$this->stmt = $this->pdo->prepare($this->sanitized_sql);
$this->stmt->execute($this->bound_parameters);
} catch (PDOException $e) {
set_debug($e->getMessage(), true);
return false;
}
return true;
}
return false;
}
I then have methods which looks like this:
public function return_active_partners() {
if ($this->select(array("query_tables" => $this->table_name, "conditions" => array("AND" => array("partners.status" => 1))))) {
$this->stmt->setFetchMode(PDO::FETCH_ASSOC);
return $this->stmt->fetchAll();
}
}
The above method will then effectively return the results ($this->select() calls the execute_prepared_sql() method.
This all works perfectly fine, but I have this extremely annoying issue where at times (and really at random) I get a "SQLSTATE[HY093]: Invalid parameter number: no parameters were bound" error. It appears to happen a lot more when I do 2 queries after eachother (say I delete a row and then return a result set of the remaining rows)
What appears to happen is the bound parameters don't get removed during the execute_prepared_sql() call. I was under the impression that when execute() is called all bound parameters would be "reset" or cleared, but this appears to not be the case.
My question is, how can I clear any bound parameters a PDO statement may have stored to ensure I don't use the same parameters twice? Or is there something else you can see which I'm doing wrong which may be the issue?

Working with dynamic prepared statements in PDO

Sometimes depending on which user type if viewing my page, I need to add in a JOIN, or even just limit the results. Is there a cleaner way of going about it? Should I have separate statements for each type of request instead? What is more "proper"?
Here is what my code ends up looking like:
// Prepare statement
$stmt = $this->db->prepare('
SELECT *
FROM Documents
LEFT JOIN Notes ON ID = D_ID
'.($user_id ? "INNER JOIN Users ON UID = ID AND UID = :userid" : '')."
". ($limit ? 'LIMIT :offset, :limit' : '')
);
// Bind optional paramaters
if ($user_id) $stmt->bindParam(':userid', $user_id, DB::PARAM_INT);
if ($limit)
{
$stmt->bindParam(':offset', $limit[0], DB::PARAM_INT);
$stmt->bindParam(':limit', $limit[1], DB::PARAM_INT);
}
Maybe just wrap the insert strings into their own methods for clarity, like getUserInsertString($user_id), and try to make your quote use more consistent.
Also, are you testing whether $user_id and $limit are defined just by going if ($user_id)? If so, if you had error reporting turned to all, you would get a bunch of undefined variable warnings. You may want to consider using if (isset($user_id)) instead.
I'd create separate (protected) functions, those return a prepared statement that only needs to be executed.
/**
* #returns PDOStatement
*/
protected function prepareStatementForCase1(PDO $dbObject,Object $dataToBind){...}
/**
* #returns PDOStatement
*/
protected function prepareStatementForCase2(PDO $dbObject,Object $dataToBind){...}
Then, I would decide outside, which one has to be called.
You can rebuild, maintain and read the code more easily.
Example:
class Document{
protected $dbObject;
public function __construct(PDO $dbObject){
$this->dbObject=$dbObject;
}
public function doQuery($paramOne,$paramTwo,...){
$logicalFormulaOne=...; // logical expression here with parameters
$logicalFormulaTwo=...; // logical expression here with parameters
if($logicalForumlaOne){
$dbStatement=$this->prepareStatementForCase1($dataToBind);
}else if($logicalFormuleTwo){
$dbStatement=$this->prepareStatementForCase2($dataToBind);
}
$dbResult=$dbStatement->execute();
}
protected function prepareStatementForCase1(Object $dataToBind){
$dbStatement=$this->dbObject->prepare("query string");
$dbStatement->bindParam(...);
return $dbStatement;
}
}
But I would not advice this, when your PDOResult object represents different type of database tuples, or when you return more rows in one of the cases.
What I usually do is that I create a class which represents (in your example) a Document. Only one. I can insert, delete, select, modify by its fields, and handle one item. When I need to (for example) fetch more of them, I create a new class, e.g. DocumentList, which handles a collection of documents. This class would give me an array of Document objects when it fetches more of them.

How can I retrieve the number of rows deleted with PDO?

Okay, so I have been using a PDO wrapper for a project I'm working on, and I'm trying to find out whether a DELETE query was successful or not. Here is the code I am using:
/**
* A pretty straight-forward query to delete a row from the verification
* table where user_id is $user_id and code is $code
*/
$result = $this->database->query("DELETE FROM verification " .
"WHERE user_id = %u AND code = %s",
$user_id,
$code);
/**
* This function will grab the PDO's exec() return, which should
* return the number of rows modified.
*/
if($this->database->getNumAffected($result) > 0)
return true;
else
return false;
The problem is, whether the DELETE query actually deletes a row or not, $this->database->getNumAffected($result) always returns '0'.
You can check out the wrapper, but basically $this->database->getNumAffected($result) simply returns exactly the same value PDO::exec() would return.
I tried this code without the wrapper (directly into PDO,) and I had the same problem but reverse: it always returned '1' (whether a row was deleted or not.)
Any help would be greatly appreciated.
EDIT: Based on this SO question, I'm doing everything right... I don't understand why this isn't working.
$query = $this->database->prepare("DELETE FROM verification WHERE user_id = :user_id AND code = :code", array('user_id' => $user_id, 'code' => $code));
$query->execute();
if ($query->rowCount() > 0) {
return TRUE;
}
return FALSE;
It doesn't work as you expect because the 'wrapper' that you're using doesn't ever use PDO::exec() - it wraps everything in a PDO statement. According to a quick read of the source code for version 2.2.6 of the 'database' class from the URL you provided, the 'query' method should return an array which contains the statement handle:
502 $statement = $this -> getDatabaseConnection () -> prepare ( $query );
...
587 $ret = array ( $statement, func_get_args (), $lastIndex );
588
589 return ( $ret );
So, assuming your $this->database->query() is calling this database class' query method, you should be able to do $result[0]->rowCount().
Note that your assertion to the earlier response that "the wrapper that [you are] using uses a different version of rowCount() because of an error that exists with the rowCount() function" is not true - the wrapper implements a numRows, but this is not the same thing as PDOStatement::rowCount(), which is intact inside of the statement handle returned from database::query().

Categories