I have this code (which worked):
if (isset($_POST['plant_name']) && $_POST['plant_name']) {
$where .= "AND (common_name) LIKE '".strtolower($_POST['plant_name']) . "' OR (latin_name) LIKE '".strtolower($_POST['plant_name'])."%' ";
}
But I wanted to change it to prepared statements and my attempt is below but I am getting errors:
$plant_name = $_POST['plant_name'];
if (isset($_POST['plant_name']) && $_POST['plant_name']) {
$stmt = $conn2->prepare . $where .= "AND (common_name) LIKE '".'?'. "' OR (latin_name) LIKE '".'?'."%' ";
}
$stmt->bind_param('s', $plant_name);
$stmt->execute();
Could somebody please help me out please
My errors are:
Notice: Undefined property: mysqli::$prepare
Fatal error: Call to a member function bind_param() on a non-object
EDIT: You're using mysqli not PDO my fault. Unfortunately mysqli doesn't support named parameters.
In your original example, you're treating $conn2->prepare like it's a property, but it's a function.
Try this:
// Presumably by this point you have a $sql and a $where that you're appending to.
if (isset($_POST['plant_name']) && $_POST['plant_name']) {
$where .= "AND (common_name) LIKE ? OR (latin_name) LIKE ?";
}
$stmt = $conn2->prepare($sql . $where);
if (isset($_POST['plant_name']) && $_POST['plant_name']) {
$stmt->bind_param('s', strtolower($_POST['plant_name']));
$stmt->bind_param('s', strtolower($_POST['plant_name'])."%");
}
Here's the PDO way (I think it's a lot cleaner, but it's probably not worth changing from mysqli to PDO at this point for you):
$statement = $conn2->prepare(
"UPDATE tablename SET
field1 = :value1,
field2 = :value2
WHERE common_name LIKE :plant_name
OR latin_name LIKE :plant_name
");
$statement->bindValue('value1', $_POST['field1']);
$statement->bindValue('value2', $_POST['field2']);
$statement->bindValue('plant_name', strtolower($_POST['plant_name']));
Note a few things about this:
I switched you from using ? (numerically indexed placeholders) to :name (name-based placeholders). Since you're using the same value for searching both fields, this gets you a very small performance gain, and makes the SQL a lot more readable.
You don't want to put quote marks around the bound parameter. One of the advantages of bound parameters is that they don't need to be quote-escaped. The SQL is sent on a separate channel from the parameter values. So there's no chance of SQL injection. The database notices the bound parameter and reads the right value all on its own by looking it up in the bound parameters data.
Related
OK, so I have gone round and round with this now for 2 hours and cannot figure out where the so-called SQL syntax error is. I finally re-wrote the prepared statement as a standard query - and it works fine, literally identical syntax.
Prepared Statement Code: (NOT working)
if ($account_info = $mysqli->prepare("SELECT users.specid, users.username ?
FROM users ? WHERE users.id = ?")) {
//A SWITCH to determine bind_param and bind_result
} else {
//Error output
}
The above results in the following MYSQL error:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near '? FROM users ? WHERE users.id = ?' at line 1
Now if I literally change the '?' to $variables and make the prepared statement into a normal query like:
if ($account_info = $mysqli->query("SELECT users.specid, users.username $param1
FROM users $param2 WHERE users.id = $param3")) {
//Fetch array and set variables to results
} else {
//Error output
}
The above code WORKS as expected with no errors.
For those curious what the $variables are in the specific switch case I'm testing:
$param1 = ', tenants.paper';
$param2 = ', tenants';
$param3 = $_SESSION['user_id'].' AND tenants.id = users.specid';
So why does one work but not the other when they have the same syntax??? It doesn't even get to the bind_param part!? I'd prefer to use the prepared statement method.
You can't pass object nane (tablename or columnname ) as param .
So users.username ? and users ? as you are trying to use are wrong ..
passing param is not a string substituition ..
This kind of action are disallowed by param binding
and you should avoid this ..but if you really need then try with string concatenation
You only bind values for parameter bindings. Not parts of SQL. ::bind_param
What you are trying to do with $param1 = ', tenants.paper'; is already SQL injection. Prepared statements are build to prevent this.
You should make a method per query instead of a generic query.
You cannot bind complex query parts and columns in a query. I also don't understand why you need to parametrise strings you explicitly set in your code.
Do this instead:
$param = $_SESSION['user_id'];
if ($account_info = $mysqli->prepare("SELECT users.specid, users.username, tenants.paper
FROM users JOIN tenants ON tenants.id=users.specid WHERE users.id = ?")) {
//A SWITCH to determine bind_param and bind_result
} else {
//Error output
}
If you (at any point in the future) need to escape column names from user input (though you shouldn't allow users such power to begin with) do this:
$columnNameFromUserInput = $_GET["column"];
$columnNameFromUserInput = "`".str_replace("`","",$columnNameFromUserInput)."`";
This should be enough.
Do not put query segments that have parts that need escaping in a variable. Put the parts that need escaping in their own separate variables so you can bind them is the whole idea here.
Example:
$param1 = ', tenants.paper'; //Bad has a comma in it, should be `tenants`.`paper` and the comma should go in the query itself
$param2 = ', tenants'; //Bad, though you have to use JOIN in any SQL language after 1992
//The next part is very very bad.
// You have something that needs escaping mixed with things that compose a query. Split them.
$param3 = $_SESSION['user_id'].' AND tenants.id = users.specid';
I am trying to migrate to Mysqli and I got my Mysql code to search for parameters like this:
$querySt = "SELECT userID FROM myTable";
if (isset($_POST["UserID"])) {
if (ctype_digit($_POST["UserID"])) {
addWhereIfNoHave();
$in_userID = mysql_real_escape_string($_POST["UserID"]);
$querySt .= " UserID = '$in_userID'";
}
}
if (isset($_POST["name"])) {
addWhereIfNoHave();
$in_name = mysql_real_escape_string($_POST["name"]);
$querySt .= " imgName LIKE LOWER('%$in_name%')";
}
if (isset($_POST["ScoreLessThan"])) {
if (ctype_digit($_POST["ScoreLessThan"])) {
addWhereIfNoHave();
$in_ScoreLessThan = mysql_real_escape_string($_POST["ScoreLessThan"]);
$querySt .= " Score < '$in_ScoreLessThan'";
}
}
...
...
there are other if statements here looking for other post data, and
they keep on adding parameters into mysql query string just like above.
...
...
//this function is called in those if statements above. It either adds "WHERE" or "AND".
function addWhereIfNoHave(){
global $querySt;
if (strpos($querySt, 'WHERE') !== false){
$querySt .= " OR";
return true;
}else{
$querySt .= " WHERE";
return false;
}
}
This function works ok looking for all the parameters input from PHP post. However, I am migrating this to Mysqli, and I have a bit of trouble converting this code to Mysqli version. For example,
$stmt = $conn->prepare("SELECT userID FROM myTable WHERE UserID = ? AND name= ?");
$stmt->bind_param('ss', $userid, $name);
Suppose, I wanna search the table using 2 variables, I bind 2 variables like above, but in the case of my Mysql above, I keep on extending additional parameters into the string before executing the mysql query.
But for Mysqli, how can we do this? Is it possible to bind additional parameters and extending the string for prepare statement like Mysql code above? How should this problem be approach for Mysqli?
My current problem is mainly with the bind_param. I could concatenate the search query further and add all the '?' into the prepare statement, but with different variable types and number variables needed to be specified in bind_param, this is where I am stuck.
I have the following code:
function dbPublish($status)
{
global $dbcon, $dbtable;
if(isset($_GET['itemId']))
{
$sqlQuery = 'UPDATE ' . $dbtable . ' SET active = ? WHERE id = ?';
$stmt = $dbcon->prepare($sqlQuery);
$stmt->bind_param('ii', $status, $_GET['itemId']);
$stmt->execute();
$stmt->close();
}
}
Do I need to mysql_real_escape_string in this case or am i okay?
No, you don't have to escape value yourself (i.e. no you don't need to call mysqli_real_escape_string), when you are using prepared statements : the DB engine will do that itself.
(Actually, if you were calling mysql_real_escape_string and using bound parameters, your strings would get escaped twice -- which would not be great : you'd end up with escaping characters everywhere...)
As a sidenote : your values are passed as integers (as indicated by the 'ii'), so you wouldn't have to call mysql_real_escape_string, even if you were not using prepared statements : as its name indicates, this function is used to escape... strings.
For integers, I generally just use intval to make sure the data I inject into my SQL queries really are integers.
(But, as you are using prepared queries, once again, you don't have to do that kind of escaping yourself)
No, you must not. Combining the two would result
in visible escape characters showing up in your data.
function dbPublish($status)
{
global $dbcon, $dbtable;
if(isset($_GET['itemId']))
{
$sqlQuery = 'UPDATE ' . $dbtable . ' SET active = ? WHERE id = ?';
$stmt = $dbcon->prepare($sqlQuery);
$stmt->bind_param('ii', $status, $_GET['itemId']);
$stmt->execute();
$stmt->close();
}
}
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)
I have the following code:
function dbPublish($status)
{
global $dbcon, $dbtable;
if(isset($_GET['itemId']))
{
$sqlQuery = 'UPDATE ' . $dbtable . ' SET active = ? WHERE id = ?';
$stmt = $dbcon->prepare($sqlQuery);
$stmt->bind_param('ii', $status, $_GET['itemId']);
$stmt->execute();
$stmt->close();
}
}
Do I need to mysql_real_escape_string in this case or am i okay?
No, you don't have to escape value yourself (i.e. no you don't need to call mysqli_real_escape_string), when you are using prepared statements : the DB engine will do that itself.
(Actually, if you were calling mysql_real_escape_string and using bound parameters, your strings would get escaped twice -- which would not be great : you'd end up with escaping characters everywhere...)
As a sidenote : your values are passed as integers (as indicated by the 'ii'), so you wouldn't have to call mysql_real_escape_string, even if you were not using prepared statements : as its name indicates, this function is used to escape... strings.
For integers, I generally just use intval to make sure the data I inject into my SQL queries really are integers.
(But, as you are using prepared queries, once again, you don't have to do that kind of escaping yourself)
No, you must not. Combining the two would result
in visible escape characters showing up in your data.
function dbPublish($status)
{
global $dbcon, $dbtable;
if(isset($_GET['itemId']))
{
$sqlQuery = 'UPDATE ' . $dbtable . ' SET active = ? WHERE id = ?';
$stmt = $dbcon->prepare($sqlQuery);
$stmt->bind_param('ii', $status, $_GET['itemId']);
$stmt->execute();
$stmt->close();
}
}