PDOStatement's bindValue only working with value of "0" - php

I'm trying to get data from a MySQL database using PDO. For example, I would run blixUserGetInfo("2767207") if the user's id was 2767207.
But, whenever I run it with a User ID other than 0, blixUserGetInfo("0"), null is returned, and no errors are thrown, even though the supplied ID does exist in the database.
function blixUserGetInfo($userid){
$connection = //database connection
// replacing this with
// "SELECT * FROM `users` WHERE `id` = $userid"
// works for some reason
$statement = "SELECT * FROM `users` WHERE `id` = ?";
$prepared = $connection->prepare($statement);
$prepared->bindValue(1, $userid); //returns TRUE
$prepared->execute(); //also returns TRUE
$code = $prepared->errorCode(); //returns '00000' (no error)
return $prepared->fetch(); //returns null
}
But, If I change $statement from
"SELECT * FROM `users` WHERE `id` = ?"
to
"SELECT * FROM `users` WHERE `id` = $userid"
and remove the bindValue statement, it works as expected.
I've also tried changing the bindValue statement to bindValue(1, $userid, PDO::PARAM_INT), but still have no luck. Both of the times the statement returns true.
Why doesn't the first example work like it should? Is it a bug, am I doing something wrong, or is it expected behavior?

I really dislike this bindValue and bindParam complexity unless it's absolutely necessary. You can simplify your code using parameters for execute
$statement = "SELECT * FROM `users` WHERE `id` = ?";
$prepared = $connection->prepare($statement);
$prepared->execute(array($userid));
While i know this does not answer why bindValue is not working, it takes away having to worry about pass by reference and pass by value stuff which could be a factor in this question.
If this execute does not return an error and your table does have data for that condition than its almost surely going to return it.
Although i didn't confirm it but your act of setting the connection to null before you fetch might be the actual reason you fail to see any data. Why would you set the connection to NULL and try to fetch afterwards ?

I suggest you just not use the bindValue() function anymore.
The bindParam() function works better in this case. Use the value placeholder instead of ?'s (:[placeholder name])
$statement = "SELECT * FROM `users` WHERE `id` = :id";
$prepared = $connection->prepare($statement);
$prepared -> bindParam(":id", $value)
$prepared -> execute();

Related

MySQLi prepared statement silently failing second execution

I am attempting to make a simple system where, once you view forum thread it updates an existing array entry with the current UNIX time in the DB table that holds the user's account information. The column is set to accept MEDIUMTEXT and is set as NOT NULL.
However, while the first prepared statement works as intended and I receive the correct value, and the json_table is correctly re-encoded, the second query does not update the column data. No errors (no matter what error level I set mysqli to). Neither of the parameters I am binding are NULL and are correct.
I am stumped as to why this is happening. MySQLi magic that I haven't learned? Restrictions somewhere?
$DB = GetDatabaseConnection();
$Statement = $DB->prepare("SELECT `forum_newpost_icondata` FROM `user_accounts` WHERE `id` = ?");
print(mysqli_error($DB));
$Statement->bind_param("i", $UserLoggedIn['id']);
$Statement->bind_result($JSONTable);
$Statement->execute();
$PostData = json_decode($JSONTable);
$PostData[$ThreadID] = time();
$PostData = (string)json_encode($PostData);
$Statement->free_result();
$Statement->close();
$Reinsertion = $DB->prepare("UPDATE `user_accounts` SET `forum_newpost_icondata` = ? WHERE `id` = ?");
print(mysqli_error($DB));
$Reinsertion->bind_param("si", $PostData, $UserLoggedIn['id']);
$Reinsertion->execute();
print(mysqli_error($DB)); // Checking after execute is still blank
$DB->close();

mysqli prepared statement without bind_param

I have this code for selecting fname from the latest record on the user table.
$mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
$sdt=$mysqli->('SELECT fname FROM user ORDER BY id DESC LIMIT 1');
$sdt->bind_result($code);
$sdt->fetch();
echo $code ;
I used prepared statement with bind_param earlier, but for now in the above code for first time I want to use prepared statement without binding parameters and I do not know how to select from table without using bind_param(). How to do that?
If, like in your case, there is nothing to bind, then just use query()
$res = $mysqli->query('SELECT fname FROM user ORDER BY id DESC LIMIT 1');
$fname = $res->fetch_row()[0] ?? false;
But if even a single variable is going to be used in the query, then you must substitute it with a placeholder and therefore prepare your query.
However, in 2022 and beyond, (starting PHP 8.1) you can indeed skip bind_param even for a prepared query, sending variables directly to execute(), in the form of array:
$query = "SELECT * FROM `customers` WHERE `Customer_ID`=?";
$stmt = $db->prepare($query);
$stmt->execute([$_POST['ID']]);
$result = $stmt->get_result();
$row = $result->fetch_assoc();
The answer ticked is open to SQL injection. What is the point of using a prepared statement and not correctly preparing the data. You should never just put a string in the query line. The point of a prepared statement is that it is prepared. Here is one example
$query = "SELECT `Customer_ID`,`CompanyName` FROM `customers` WHERE `Customer_ID`=?";
$stmt = $db->prepare($query);
$stmt->bind_param('i',$_POST['ID']);
$stmt->execute();
$stmt->bind_result($id,$CompanyName);
In Raffi's code you should do this
$bla = $_POST['something'];
$mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
$stmt = $mysqli->prepare("SELECT `fname` FROM `user` WHERE `bla` = ? ORDER BY `id` DESC LIMIT 1");
$stmt->bind_param('s',$_POST['something']);
$stmt->execute();
$stmt->bind_result($code);
$stmt->fetch();
echo $code;
Please be aware I don't know if your post data is a string or an integer. If it was an integer you would put
$stmt->bind_param('i',$_POST['something']);
instead. I know you were saying without bind param, but trust me that is really really bad if you are taking in input from a page, and not preparing it correctly first.

Best way to avoid preparing the same PDO statement more than once?

Currently I save prepared statements into a private variable, because I ignore how they really work in the deepness, and do it just in case.
So the question is really simple, if I iterate over the same $PDO->prepare(), will it prepare again the same query?
foreach( $arr as $docid ) {
if( $this->dbLink === null ) { // PDO resource, saved in the object.
throw new Exception( 'Must first connect to DB' );
}
if( $this->queryCheckAccess === null ) {
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
}
else {
$result = $this->queryCheckAccess->execute(array(':id'=>$docid));
}
}
Will it matter ? Or the DB Engine / PHP is smart enough to know that it is the same prepared statement?
Thanks a lot.
----------------- EDIT --------------
I think I was misunderstood.
What I ask is what happens if I do:
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
And what happens if I do:
if( $this->queryCheckAccess === null ) {
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
}
Will the engine prepare the query 4 times in the first example? Or will notice it is the same query and just "jump" that?
Your code only prepares the query once, because after the first loop iteration, it's not NULL so it the conditional block won't run. But it's a waste of time to check the condition every time through the loop.
But to answer your question, if you prepare() the same query, it does do redundant work, even if the query is identical to the one you prepared before. So you should avoid that.
But you don't need to prepare inside the loop at all. Prepare once before you start the loop, and bind a variable to the parameter. You don't need to bind every time in the loop, just change the value of that variable.
if( $this->dbLink === null ) { // PDO resource, saved in the object.
throw new Exception( 'Must first connect to DB' );
}
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$this->queryCheckAccess->bindParam(':id' => $docidparam);
foreach( $arr as $docid ) {
$docidparam = $docid;
$result = $this->queryCheckAccess->execute();
}
I'm not sure if you can bind the variable and also use it as the loop variable, there might be a scope conflict.
Another suggestion for this query: why not just run one query to search for a list of values?
$list = implode(",", array_fill(1, count($arr), "?"));
$query = "SELECT * FROM something WHERE id IN ( $list )";
$this->queryCheckAccess = $this->dbLink->prepare($query);
$this->queryCheckAccess->execute($arr);
PS: Also you should check for errors. If you enable PDO error mode EXCEPTION, then errors will automatically throw exceptions. If you don't enable that mode, you need to check the return value of prepare() and execute(), which return false if there's an error.
I just RUN a code similar to your example, and enabled MySQL Query LOG I found that all prepare requests are sent to MySQL Server
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Test code:
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth->bindParam(1, $user);
$sth->execute();
Then, the best way is to prepare once, and Bind different values and then execute.
$sth = $dbh->prepare($sql);
$user = "test";
$sth->bindParam(1, $user);
$sth->execute();
$user = "test2";
$sth->bindParam(1, $user);
$sth->execute();
$user = "test";
$sth->bindParam(1, $user);
$sth->execute();
No, that's one of the main features of prepared statements. If you're going to run the same query multiple times but with different variables then preparing the query will give you a speed increase. Especially if you make use of transactions (requires InnoDB storage engine).
To answer the question from the title (which is quite different from questions in the body), the best way to avoid preparing the same statement more than once, apparently would be to avoid running multiple similar queries at all.
To answer the question from the question body - no, DB Engine / PHP is not "smart" enough to know that it is the same query were prepared again. With every new prepare() call another statement is created. And I would be first to hate such a "smart" behavior. The "smarter" your tool, the more unpredictable results you get.
To answer the real problem in the code, a smart developer would use a right tool to save himself a trouble.
With safeMysql whole mess will be reduced to one query and one line of code
$data = $this->dbLink->getAll('SELECT * from somth where id IN (?a)', $arr);
S0 - no multiple queries, no multiple preparations, no multiple questions.
By the way, you are losing first id with your code.
Yet you're losing all of them but last one if you don't use the result in place.

Does beginTransaction() work with prepare()?

I have the following code:
$db->beginTransaction();
$achievement_name = $db->prepare("SELECT `achievement_name` FROM `achievement_names` WHERE `id` = :a_id");
$achievement_name->bindValue(":a_id",$r['achievement_id'],PDO::PARAM_INT);
$achievement_desc = $db->prepare("SELECT `achievement_desc` FROM `achievement_names` WHERE `id` = :a_id");
$achievement_desc->bindValue(":a_id",$r['achievement_id'],PDO::PARAM_INT);
$achievement_image = $db->prepare("SELECT `image` FROM `achievement_names` WHERE `id` = :a_id");
$achievement_image->bindValue(":a_id",$r['achievement_id'],PDO::PARAM_INT);
$db->commit();
Is this possible with PDO? To have the $db->prepare() and bindValue() functions and then committing them? It doesn't seem to be working for me, because they are returning bool(false).
Transaction is using for consistent read or write data. "Preparing" doesn't read or write any data.
So, the answer is: Yes, beginTransaction() work properly with prepare(), but it useless.

Receive multiple columns from one sql request in PHP

I am working on a friend list function and I can't figure out how to correctly receive the values.
My code looks like this:
$getuid = $mysqli->prepare("SELECT `uid` FROM `users` WHERE name = ? OR name = ?");
$getuid->bind_param("ss", $user, $friend);
$getuid->execute();
$getuid->bind_result($uid);
$getuid->fetch();
$getuid->close();
$resetpass = $mysqli->prepare("INSERT INTO `friendlist` SET `friend1`=?, `friend2`=?, `accept`=0");
$resetpass->bind_param("ss", $uid[0], $uid[1]);
With the first query I get exactly two uid values back. I want to use them in the second query. It seems like bind_result is not working, neither as array nor when using two values in bind_result. How can I do this using mysqli. I can't use get_result because I'm on PHP 5.2 .
Anyone able to help me?
I think you need something like this. I have not tested it and there are probably even better ways to do this. I just tried the quickest change i could make to your original code to get it to work.
$query = "SELECT uid FROM users WHERE name = '".$user."' OR name = '".$friend."'";
$getuid = $mysqli->query($query);
if($uid = $getuid->fetch_assoc())
{
$query = "INSERT INTO friendlist SET friend1= '".$uid['uid'][0]."', friend2='".$uid['uid'][1]."', accept=0";
$mysqli->query($query)
}
$getuid->close();
Okay I finally understood the concept of fetch.
In order to receive all the values I have to retrieve them in a while-loop.
Here is the solution:
$getuid = $mysqli->prepare("SELECT `uid` FROM `users` WHERE name = ? OR name = ?");
$getuid->bind_param("ss", $user, $friend);
$arra = array();
$getuid->execute();
$getuid->bind_result($uid);
while ($getuid->fetch()) {
$arra[] = $uid;
}
Now I can call the array values using $arra[0] and $arra[1]

Categories