Sorry if this seems a really stupid question, but I'm struggling to get to grips with changing from Mysql to Mysqli and prepared statements.
So in mysql, I would have done this:
$q=('SELECT * FROM table WHERE field="'.$variable.'"');
$result = mysql_query($q);
I now know this is not good. So I now have the below:
$stmt = $mysqli->prepare('SELECT * FROM table WHERE field=? LIMIT 1');
$stmt->bind_param('s', $variable);
$stmt->execute();
Problem is that the query doesn't work. Say the ? is actually "tree". So the query becomes:
'SELECT * FROM table WHERE field=tree LIMIT 1'
If I tried to run that query in say phpmyadmin I get "Unknown column tree in where clause". Obviously if I put quotes around it then it works, hence the original query. So how can I get this to work if I can't use quotes, since then you are looking for the literal question mark?
For reference I am then using this code:
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
As I can't use get_result() which is very annoying. I have PHP version 5.4, and even the mysqlnd driver, but can't enable it as I'm on a VPS and my host says it might affect other sites on that server. Consequently what is actually just two lines in MySql is actually now something like 15 lines in the 'improved' mysqli. Great.
Any help would be appreciated!
This:
$stmt = $mysqli->prepare('SELECT * FROM table WHERE field=? LIMIT 1');
$stmt->bind_param('s', $variable);
is not equivalent to this:
SELECT * FROM table WHERE field=tree LIMIT 1
Prepared statement placeholders are not the same as copy and pasting in values. You are binding the value "tree" as a string here, the database will actually understand this. The ? is not simply being replaced by the bound value, the database understands the difference between your query structure with its placeholders and the values you're binding into them. Binding the parameter this way is equivalent to running:
SELECT * FROM table WHERE field='tree' LIMIT 1
Consequently what is actually just two lines in MySql is actually now something like 15 lines in the 'improved' mysqli. Great.
Mysqli is not intended to be used as is. It is but a building material for the higher level library. When used wisely, it can give you data in one line:
$data = $db->getAll('SELECT * FROM table WHERE field=?s', $variable);
(BTW, the same goes for the old mysql ext as well)
Related
$queryString = 'SELECT * FROM n8593370.items AND Suburb = :suburb AND Name LIKE \'%:name%\'';
$stmt = $pdo->prepare($queryString);
$stmt->bindValue(':suburb', $suburb);
$stmt->bindValue(':name', $name);
$stmt->execute();
$results = $stmt->fetchAll();
I am getting the above mentioned error can not for the life of my figure out why.
I am certain I only have 2 variables to bind and that I indeed bind both.
When I perform this with each conditional on their own i.e. WHERE 1 = 1 AND Suburb = :suburb or WHERE 1 = 1 AND Name LIKE \'%:name%\' I do not have any errors.
ALTHOUGH the latter of the two conditionals does not return any results, however when I test it in MySQL Workbench it works as I expect it.
Can anybody shed some light on the issue?
When using placeholder values it's important to leave any and all escaping out of the query. The value itself should be bare, PDO will take care of the escaping for you if you're disciplined about using placeholder values.
Specify it this way:
"...name LIKE :name..."
Then you bind this way:
$stmt->bindValue(':name', "%$name%");
I'm trying to set up a dynamic MySQL query, using mysqli prepared statements. I need to compare multiple values (unknown how many) to a single column.
The following works:
$qs = '?,?';
$query = 'SELECT r_id FROM sometable WHERE someindex IN ('.$qs.')';
$statement = $db_conn->prepare($query);
call_user_func_array(array($statement, 'bind_param'), array('ss', 'Something', 'somethingelse'));
I get a result back from the DB, and can do as I please with the return. BUT, the following does not work:
$qs = '?,?';
$query = 'SELECT r_id FROM sometable WHERE someindex IN ('.$qs.')';
$statement = $db_conn->prepare($query);
$test = array('ss', 'Something', 'something else');
call_user_func_array(array($statement, 'bind_param'), $test);
With the only difference being the assignment of $test, instead of creating the array directly within call_user_func_array.
Similarly:
$one = 'pizza';
$two = 'pie';
call_user_func_array(array($statement, 'bind_param'), array('ss', $one, $two));
also does not work. The statement doesn't even execute.
If I can't put variable names directly into the statement, I can't have the dynamic queries. What's going on? Is there a fix for this, or maybe another (equally simple) way I can run a query on a single column, with an unknown number of terms? References or pointers (i.e. &$one) do not work either.
There seems to be a couple known issues with call_user_func_array back in 2012, or around there, but I can't find anything more recent. I am using PHP 5.5
EDIT 1
Known issue, back in 2007 here
I am writing a pretty basic piece of code to fetch one or (in most cases) multiple rows from a mysql database.
function getschema($mysqli){
$id = $_SESSION['user_id'];
$query = $mysqli->prepare("SELECT a.naam
FROM schemes AS a, aankoop AS b
WHERE b.aankoop_username_id = :userid && b.aankoop_schema_id = a.id");
$query->bind_param(':userid', $id, PDO::PARAM_INT);
$query->execute();
$result = $query->fetchAll();
echo ($result);
}
I get the user id from the session and pull the data with the query in the prepared statement.
This statement is correct. I tried it in phpmyadmin and it returns the correct values.
Now I want to use this function in my HTML like so...
<?php echo getschema($mysqli); ?>
But my code does not return a thing, it even messes up the layout of my html page where I want to show the code.
I think it probably is something with the fetchAll command. I also tried the PDO::Fetch_ASSOC but that did not work either.
In addition, I cannot see the php errors, even when they are enabled in the php.ini file.
Here's what's going on; you're mixing MySQL APIs/functions and those do not intermix.
Replace the :userid (PDO) bind in b.aankoop_username_id = :userid with a ? placeholder
b.aankoop_username_id = ?
Then this line:
$query->bind_param(':userid', $id, PDO::PARAM_INT);
Replace :userid by $id and remove , PDO::PARAM_INT but adding i
$query->bind_param("i", $id);
Sidenote: Make sure that column is int type. If not, use s instead of i.
Replace the line for fetchAll with the loop as outlined in AbraCadaver's answer.
You can't mix MySQL APIs/function, read the following on Stack:
Can I mix MySQL APIs in PHP?
Read up on mysqli with prepared statements and how it works:
http://www.php.net/manual/en/mysqli.quickstart.prepared-statements.php
Checking for errors would have outlined the errors.
http://php.net/manual/en/mysqli.error.php
http://php.net/manual/en/function.error-reporting.php
Instead of echo ($result); do return $result; in your function.
Then to use it you have to loop over the array of rows and echo the column that you want:
foreach(getschema($mysqli) as $row) {
echo $row['naam'];
}
Or assign the function return to a variable and loop over that:
$rows = getschema($mysqli);
I have an issue, I'm looping threw a set of values and then creating a PDO mySql query with every loop, now the problem is the first query is executing and returning results, but the second upwards aren't returning results. If I manually execute the queries on the server they return results. This is weird, maybe I'm doing something wrong here. My code below
if($num_results > 0){
for($i=0;$i<$num_results;$i++){
$sql_sub = "SELECT * FROM menu_config WHERE client_id =".$client_id ." AND id =".$data[$i]['root_menu_id'];
$results_s = $pdo->query($sql_sub);
$data_s = $results_s->fetchAll(PDO::FETCH_ASSOC);
$sub_menu_title = "<strong>".$data[$i]['title']."</strong>";
if(empty($data_s[0]['title'])){
$main_menu_title = '<span style="color:#FF0000;font-weight:bold;">No Main Menu Assigned to Sub Menu</span>';
}else{
$main_menu_title = $data_s[0]['title'];
}
$men_title = $data[$i]['title']
}
}
(this may be a little more than you asked for)
You seem to be missing out on some good things that prepared statements do.
First off, you don't usually want to pass the values directly into the query. (sometime's it's necessary, but not here). By doing that, you take out all the good stuff that protects from sql injection. Instead you want to send them as parameters after you've prepared the query.
Secondly, when in a loop, you can save yourself time and resources if you're running the same query over and over by preparing the statement, and then only changing the values you send to to that prepared statement using the PDOStatement::bindParam() function.
Thirdly, fetchAll() does not take a 'fetch_style' of PDO::FETCH_ASSOC. fetch() does. But I think you can get by with the default or none using fetchAll. You'll have to check into that and see what you need. Here are the fetchAll docs
$sql_sub = "SELECT * FROM menu_config WHERE client_id = :client_id AND id = :id ";
$query = $pdo->prepare($sql_sub);
for($i=0;$i<$num_results;$i++){
$query->bindParam(':client_id', $client_id);
$query->bindParam(':id', $data[$i]['root_menu_id']);
$query->execute();
$data_s = $query->fetchAll();
$sub_menu_title = "<strong>".$data[$i]['title']."</strong>";
if(empty($data_s[0]['title'])){
$main_menu_title = '<span style="color:#FF0000;font-weight:bold;">
No Main Menu Assigned to Sub Menu</span>';
}else{
$main_menu_title = $data_s[0]['title'];
}
$men_title = $data[$i]['title'];
}
I created this code:
$statement = $db->prepare("SELECT * FROM phptech_contact");
$statement->execute();
$result = $statement->result_metadata();
$object = $result->fetch_object();
print_r( $object );
When I run it, it doesn't work. Can anybody tell me why it doesn't work?
I have 20 rows in this table so data should be returned.
From http://ch.php.net/manual/en/mysqli-stmt.result-metadata.php
Note: The result set returned by mysqli_stmt_result_metadata() contains only metadata. It does not contain any row results. The rows are obtained by using the statement handle with mysqli_stmt_fetch().
As long as you don't need this meta data you don't need to call this method.
$statement = $db->prepare("SELECT fld1, fld2 FROM phptech_contact");
$statement->execute();
$stmt->bind_result($fld1, $fld2);
while ($stmt->fetch()) {
echo "$fld1 and $fld2<br />";
}
But I really dislike the mysqli extension. PDO is much cooler ... ;-)
$db = new PDO('...');
$stmt = $db->prepare("SELECT fld1, fld2 FROM phptech_contact");
$stmt->execute();
while ($obj = $stmt->fetchObject()) {
// ...
}
or
$objs = stmt->fetchAll(PDO::FETCH_OBJ);
if you're trying to get the rows from the database, the function you need is mysqli_stmt::fetch(), not mysqli_stmt::fetch_metadata()
You're also missing a few steps. When using prepared statements, you must specify the fields you would like to return instead of using the star wildcard, and then use mysqli_stmt::bind_result() to specify which variables the database fields should be placed in.
If you're more familiar with the original MySQL extension, prepared statements have a different process to use. If your select statement has a parameter (eg., "WHERE value=?") prepared statements are definitely recommended, but for your simple query, mysqli:query() would be sufficient, and not very different from the process of mysql_query()
I believe the problem is that mysqli_stmt::result_metadata() returns a mysqli_result object without any of the actual results — it only holds metadata.
So what you want to do is use $result = $statement->bind_result(...) and then call $result->fetch() repeatedly to get the results.
One of the comments under the bind-result() article shows how to do this for a query like yours, where you don't necessarily know all of the columns being returned.