Proper way to bind an array to prepared statements? - php

This has probably been asked a handful of times already, but I'm having a hard time understanding how to bind an array to a mysqli prepared statement.
What I'm trying to do is query for a list of user id's in a message thread, and then insert into the messages table so the user can be notified of a new message reply.
This is what I've tried:
//now check which users are in the message thread
$stmt2 = $mysqli->prepare("SELECT DISTINCT user_id_2
FROM uc_user_messages
WHERE thread_id = ?");
$stmt2->bind_param("i", $this->thread_id_clean);
$stmt2->bind_result($user_2_id);
$stmt2->execute();
$stmt3 = $mysqli->prepare("
INSERT INTO `users`.`uc_user_messages`(
`id` ,
`message_id` ,
`user_id_2` ,
`read` ,
`thread_id`
)
VALUES (
NULL , ?, ?, ?, ?
);
");
//now insert the message into the user_messages table so the user can be notified of a new message
while ($row = $stmt2->fetch()){
$stmt3->bind_param("iiii", $inserted_id, $user_2_id, $read, $this->thread_id_clean );
$stmt3->execute();
}
What am I doing wrong? I've tried putting the prepared statement inside the loop too, but I keep on getting this error:
Fatal error: Call to a member function bind_param() on a non-object
I've also ran the query manually with test data, so I know it's the loop that's causing the value not to bind correctly.
Edit: I should add that the select query is working fine and that it's the insert query that is causing an error.

Fatal error: Call to a member function bind_param() on a non-object
This means that $stmt2 or $stmt3 is not an object. The prepare() function returns false if there was any problem parsing or validating the query. But false->bind_param() is just not going to work.
This is a very common mistake made by many MySQL developers. The solution is you have to check that prepare() returned success and not false.
Example:
$stmt2 = $mysqli->prepare("...");
if ($stmt2 === false) {
// do something to report $mysqli->error, and return
}
Also see http://php.net/manual/en/mysqli.error.php

once a var has been bound to your prep statement, it stays bound.
That means that cou call bind_param only once (outside of the loop), and then just repeat the two-step-loop "change var values / execute".
php.net: Check section #3 INSERT prepared once, executed multiple times
Also be sure that the quotes you used for db and table names don't interfere with PHPs backtick operator. (In that specific case, you can ommit those quotes.)

You are not checking for the errors. Do it the way explained here: https://stackoverflow.com/a/15447204/285587
Most likely the error is something like "Commands out of sync" one. You have to use store_result() on the first query to avoid that. Strangely, Bill has a perfect answer which is first on google search - you can refer to it for the details.
I only can't get which arrays you are talking about.

Related

Attempting to perform an update/insert query using php prepared statement and mysqli but to no avail

I'm trying to execute the following code as a prepared statement using PHP:
private function prepQuery(){
$this->prepSQL = $this->con->prepare('
IF EXIST(SELECT * FROM tablename WHERE reference=?)
UPDATE tablename
SET
column1=?,
column2=?,
column3=?
WHERE reference=?
ELSE
INSERT INTO tablename(
column1,
column2,
column3,
column4
) VALUES(?, ?, ?, ?);
');}
The code executes in an object context where the connection object is a private field variable in the class. The function should initialize the $prepSQL field variable to be an object but I get the following error:
Fatal error: Uncaught Error: Call to a member function bind_param() on boolean
The purpose of the code is to read a file which gets updated regularly(The file size and field data is out of my control so using prepared statements would be the best choice for speed and security). The code should check if the record exist in the database and if so, update it to contain the changes made. Else, insert the new row from the file into the database.
However, it doesn't complete as it says that the $prepSQL variable is a boolean and cannot call the bind_param method on it.
I tried executing the sql manually and that works fine. If I use a normal insert statement in the code, it works fine. But when run as is below it doesn't work. I'm really not sure what I'm doing wrong here.
Your query contains an error. And you are trying to run bind_param on something that didn't work.
You are probably doing:
$this->prepSQL = $this->con->prepare('...');
$this->prepSQL->bind_param()
The prepare() method can return false and you should check for that. As for why it returns false, perhaps the table name or column names (in SELECT or WHERE clause) are not correct?
Also, consider use of something like $this->db->conn->error_list to examine errors that occurred parsing the SQL. (I'll occasionally echo the actual SQL statement strings and paste into phpMyAdmin to test, too, but there's definitely something failing there.)
That said while not an answer to the actual question DO listen to what other comments said:
Using INSERT INTO ... ON DUPLICATE KEY UPDATE ... is much easier than your IF ELSE statement.
https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html

PHP / mysqli: Prepared Statements with num_rows constantly returning nothing

In my test-surroundings there is a database containing some Person Information (Name, E-Mail, Adress etc.). These Informations can be inserted by anyone into the database via a form. In the background they are inserted with a parameterized INSERT into the database after submission.
What I now would like to do is to detect if some person tries to insert the same values into the database again, and if he does, not inserting the new values and instead showing an error message. (So every person name in the database is unique, there are no multiple rows linked to one name).
I had a numerous number of ideas on how to accomplish this. My first one was to use a query like REPLACE or INSERT IGNORE, but this method would not give me feedback so I can display the error message.
My second attempt was to first do a SELECT-query, checking if the row already exists, and if num_rows is greater than 0, exit with the error message (and else do the INSERT-part). For this to work I will have to use parameterized queries for the SELECT too, as I´m putting some user input into it. Figuring that parameterized queries need special functions for everything you could normally do with way less lines of code, I researched in the internet on how to get num_rows from my $statement parameterized-statement-object. This is what I had in the end:
$connection = new mysqli('x', 'x', 'x', 'x');
if (mysqli_connect_error()) {
die("Connect Error");
}
$connection->set_charset("UTF-8");
$statement = $connection->stmt_init();
$statement = $connection->prepare('SELECT Name FROM test WHERE Name LIKE ?');
flags = "s";
$statement->bind_param($flags, $_POST["person_name"]);
$statement->execute();
$statement->store_result();
$result = $statement->get_result(); //Produces error
if ($result->num_rows >= 1) {
$output = "Your already registered";
} else {
$output = "Registering you...";
}
exit($output);
After all, I can´t get why mysqli still won´t give me num_rows from my statement. Any help is appreciated, thanks in advance!
Oh, and if you guys could explain to me what I have to do to get affected_rows,that would be awesome!
EDIT: I know I could to this by using unique constraints. I also found out that I can find out if INSERT IGNORE skipped the INSERT or not. But that won´t answer my complete question: Why does the SELECT num_rows alternative not work?
ANOTHER EDIT: I changed the code snippet to what I now have. Although my mysql(i)-version seems to be 5.6.33 (I echo´d it via $connection->server_info) get_result() produces the following error message:
Fatal error: Call to undefined method mysqli_stmt::get_result() in X on line X (line of get_result)
The behaviour of mysqli_num_rows() depends on whether buffered or unbuffered result sets are being used. For unbuffered result sets, mysqli_num_rows() will not return the correct number of rows until all the rows in the result have been retrieved. Note that if the number of rows is greater than PHP_INT_MAX, the number will be returned as a string.
Also make sure that you declare ->store_result() first. Moreover the function doesn't work with LIMIT used jointly with SQL_CALC_FOUND_ROWS. If you want to obtain the total rows found you must do it manually.
EDIT:
If nothing from the suggestions does not work for you, then I would propose to rewrite your SQL query:
SELECT `Name`, (SELECT COUNT(*) FROM `Persons`) AS `num_rows` FROM `Persons` WHERE `Name` LIKE ?
This query will return the total number from your Persons table, as well as Name, if exist.

How to close the result set in mysqli prepared statement, but keep the statement - without buffering entire result?

I'm trying to use multiple prepared statements with the mysqli driver in PHP and getting the infamous error:
Commands out of sync; you can't run this command now
Although I think this database API is atrociously brain-damaged, I do understand why I'm getting the error, as described by this question.
Several solutions are offered in this and various other answers I've found laying around, the only ones I've seen that seem to work involve
$stmt->store_result()
which buffers the entire result set.
However, I don't want to buffer the entire result set - I want to discard it without using (a large amount of) memory to store it!
All I want to do, is create a prepared statement, use bind_param() and bind_result() and fetch() to get some of the results, and then somehow close the results so that I can reuse the statment later and run other queries until then.
I've tried just about every way of getting the actual result object being used from the statement and explicitly closing that, and I've tried calling next_result() until it returns null. Neither of those solutions prevents that error from appearing.
So, how can I close a result set and execute other prepared statements after a fetch(), without trashing the executed statement and having to re-parse the query?
$q = $dbc->prepare("SELECT id, typename, storageclass, tablename FROM _dbtype WHERE typename=?");
$q->bind_param("s", $typeName);
$q->bind_result($id,$typeName,$storageClass,$tableName);
$q->execute();
$q->fetch(); // ie. only once
... <something that does not destroy $q> ...
$q2 = $dbc->prepare("SELECT id, name FROM _storagetype");
see mysqli_stmt::free_result()
(and http://dev.mysql.com/doc/refman/5.7/en/mysql-stmt-free-result.html for the hint that "If there is a cursor open for the statement, mysql_stmt_free_result() closes it.")
<?php
mysqli_report(MYSQLI_REPORT_STRICT|MYSQLI_REPORT_ALL);
$mysqli = new mysqli('localhost', 'localonly', 'localonly', 'test');
if ($mysqli->connect_errno) {
trigger_error( sprintf('mysqli connect error (%d) %s', $mysqli->connect_errno, $mysqli->connect_error), E_USER_ERROR);
die;
}
$mysqli->query('CREATE TEMPORARY TABLE sofoo ( id int auto_increment, primary key(id))');
$mysqli->query('INSERT INTO sofoo VALUES (),(),(),(),(),()');
$stmtA = $mysqli->prepare('SELECT id FROM sofoo WHERE id>1');
$stmtB = $mysqli->prepare('SELECT id FROM sofoo WHERE id<10 ORDER BY id DESC');
$stmtA->execute();
$stmtA->bind_result($id);
$stmtA->fetch(); echo $id, PHP_EOL;
$stmtA->free_result(); // without this the script ends with "Fatal error: Uncaught mysqli_sql_exception: Commands out of sync;"
$stmtB->execute();
$stmtB->bind_result($id);
$stmtB->fetch(); echo $id, PHP_EOL;
If there are multiple result sets (i.e. if you have to use next_result()) you must call free_result()for each result set separately.

PHP - Call to a member function bind_param() on a non-object - Prepared Statement [duplicate]

This question already has an answer here:
What to do with mysqli problems? Errors like mysqli_fetch_array(): Argument #1 must be of type mysqli_result and such
(1 answer)
Closed 6 years ago.
I am trying to bind a variable in this prepared statement, but i keep receiving the error:
Call to a member function bind_param() on a non-object
The function is called, and variables are passed to it. When i change the function to just echo the variable, the variable prints on the page fine, but if i try to bind it here i receive the error. can anyone help?
//CALL FROM PAGE ONE
check($username);
//FUNCTION ON PAGE 2
function check($username){
$DBH = getDBH();
$qSelect = $DBH->prepare("SELECT * FROM users WHERE username = ?");
$qSelect->bind_param("s", $username);
}
i know the function is not completely written here, but that shouldn't be a problem. I don't understand why i am receiving this error.
Well, one reason prepare() can fail is if the sql statement sent to it is not valid in the current DB.
prepare() will then return false.
Eg - if the table name is not correct or one or more field in the query does not exist.
as the error-message says, $qSelect seems to be not an object. try to debug this by using var_dump($qSelect); right after your prepare-call. also check if getDBH() returns what you need.
sounds like the prepare-call fails (don't know why) and so it returns false - false is not an object, so you can't call bind_param() on that.
EDIT: you havn't given the info, but it looks like you're using PHP's PDO. In that case, take a look at the documentation.
If the database server successfully
prepares the statement, PDO::prepare()
returns a PDOStatement object. If the
database server cannot successfully
prepare the statement, PDO::prepare()
returns FALSE or emits PDOException
(depending on error handling).
You should configure your server to return those PDO-Exceptions, which would tell you why the prepare call fails.
i'm using the mysqli approach as well and got the same error when I created another instance of mysqli before closing the first instance. So its important to use close() before starting the same piece of code. For example:
$DBH = getDBH();
$qSelect = $DBH->prepare("SELECT * FROM users WHERE username = ?");
$qSelect->bind_param("s", $username);
$qSelect->close(); // <--- use close before calling the same function( wich contains $DBH code) again;
It appears that prepare is quite dumb. It doesn't rely query entirely into the MySQL side, by this, I mean, if in your query, you have a table that happens to have the same name of a keyword, say "user", "order", ..., it just doesn't recognize it as a table, but rather as what the keyword commands actually do, so the query turns out to be a mess and the prepare just fail.
To fix this is simple, you have to type it in the "correct" way adding "`" in both sides of the table name. Example:
`user`, `order`, `...`
It's correct, yet, I find it silly from prepare to have this behavior.
I am trying to help other people with little experience in PHP like me.
In my case, this error occurred because I had an SQL syntax error. The console stack trace did not show the problem.
When I fixed the SQL, the error was gone.
Check the permissions of the user in database. User without "insert" permission causes "Call to a member function bind_param() on a non-object" message error too, when trying to insert.

2nd MySQLi Query causes a "Call to a member function execute() on a non-object"

I've got a strange problem. There are 2 queries.
$sql = "SELECT `content_auftrag_id`
FROM `content`
WHERE `content_id` = '".$content_id."' LIMIT 1";
$ergebnis = $db->prepare( $sql );
$ergebnis->execute();
$ergebnis->bind_result( $content_auftrag_vorhanden );
$content_auftrag_id = "test";
$sql = "UPDATE `content` SET `content_auftrag_id` = '".$content_auftrag_id."'
WHERE `content_id` = '".$content_id."'";
$ergebnis2 = $db->prepare( $sql );
$ergebnis2->execute();
When I use them both, then an error occurs for the second one. If I only run the the second, then it works fine. How can it be that both together cause an error?
All variables are there and correct.
Thanks!
Okay, I think you didn't quite got the idea behind PreparedStatements. You shouldn't directly insert the parameters in your SQL-String, but use ?-placeholders and bind them in the Query using the bind_param()-method.
Your error seams to appear here:
$ergebnis2 = $db->prepare( $sql );
This function returns false if it wasn't successful. You should check if the value of ergebnis2 is not false.
Also, you should use the error-method to see the last appeared MySQL-Error.
You can only work on one prepared query at a time, so to speak. See Mysqli::execute() method:
"When using mysqli_stmt_execute(), the mysqli_stmt_fetch() function must be used to fetch the data prior to performing any additional queries."
You can also use the store_result() method to remove this block as well to perform the next query.
Also, take heed from those who warn you about abusing prepared statements like your example. Though it works without error if you don't actually have any parameters to bind to, it basically throws sql injection prevention out the window.
If $ergebnis2 is 'not an object', then I guess it must be false. Which means the prepare() call failed for whatever reason.
What does $db->error return after you have called the 2nd prepare?
Always check your return values, its basic debugging

Categories