Implementing extensible search query in Mysqli - php

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.

Related

Mysqli Prepared Statement with % wildcard [duplicate]

This question already has an answer here:
Correct way to use LIKE '%{$var}%' with prepared statements?
(1 answer)
Closed last month.
Im am trying to build a dynamic prepared statement so that I can reuse the code. I am running into a problem when using %?% in my prepared state as it used LIKE. My code is as follows:
$where = " First_Name LIKE '%?%' ";
$vals = array('Mike');
$type = 's';
$dbd = mysqli_stmt_init($dbconnection);
if (mysqli_stmt_prepare($dbd, "SELECT * FROM Contacts WHERE $where" )) {
mysqli_stmt_bind_param($dbd, $type, ...$vals);
if (!mysqli_stmt_execute($dbd)) {
echo "Execute Error: " . mysqli_error($dbconnection);
} else {
//do nothing
}
} else {
echo "Prep Error: " . mysqli_error($dbconnection);
}
mysqli_stmt_get_result($dbd);
So when I use "First_Name = ?" it works fine so I think my issue is with the '%?%'. I have searched resolutions but couldn't find anything related to my dynamic prepared statement. Thank you for any help.
You need to bind the complete value, not just a portion of it. This means doing:
$where = "First_Name LIKE ?"
And then binding:
$vals = array('%Mike%');

MySQLi dynamically created prepared statement fails: "No data supplied for parameters in prepared statement"

I have a piece of code that reads a crash log and other metadata from a MySQL database before sending it back to the requester as JSON (this code is called by an AJAX function on another page). Optionally, filters can be specified with GET, which alter the prepared statement to filter for only the specified results - for example, to only show results from one app version. Here is the code in question:
$conn = new mysqli($sql_servername, $sql_username, $sql_password, "Crashes");
if ($conn->connect_error) {
//failed
die();
}
$sql="
SELECT ID,phone,appver,osver,user_agent,username,message,app_name,date_received FROM Crashes WHERE ";
$params="";
$params2="";
if (isset($_GET["filter_phone"])) {
$sql .= "phone = ? AND "; //i.e "WHERE phone = ? AND (...osver = ? AND appver = ? etc)"
$params.="s";
$params2.=', $_GET["filter_phone"]';
}
if (isset($_GET["filter_appver"])) {
$sql .= "appver = ? AND ";
$params.="s";
$params2.=', $_GET["filter_appver"]';
}
if (isset($_GET["filter_osver"])) {
$sql .= "osver = ? AND ";
$params.="s";
$params2.=', $_GET["filter_osver"]';
}
if (isset($_GET["filter_user_agent"])) {
$sql .= "user_agent = ? AND ";
$params.="s";
$params2.=', $_GET["filter_user_agent"]';
}
if (isset($_GET["filter_username"])) {
$sql .= "username = ? AND ";
$params.="s";
$params2.=', $_GET["filter_username"]';
}
if (isset($_GET["filter_message"])) {
$sql .= "message = ? AND ";
$params.="s";
$params2.=', $_GET["filter_message"]';
}
if (isset($_GET["filter_app_name"])) {
$sql .= "app_name = ? AND ";
$params.="s";
$params2.=', $_GET["filter_app_name"]';
}
$sql.="1";
//echo "\$params: ".$params."<br>";
//echo "\$params2: ".$params2."<br>";
//echo "<br>\$stmt->bind_param(\$params$params2);<br>";
//echo var_dump($_GET);
$stmt = $conn->prepare($sql);
exec("\$stmt->bind_param(\$params$params2);");
if ($stmt->execute()) {
//echo "<br>Success!";
} else {
//echo "<br>Failure: ".$stmt->error;
}
$result = $stmt->get_result();
$out = array();
while ($row = $result->fetch_assoc()) {
array_push($out, $row);
}
echo json_encode($out);
This code works perfectly when no filters are specified - however, when any filter is specified, the statement fails with the error No data supplied for parameters in prepared statement. I have verified that the code in the exec() should be (for example, if the user_agent filter is set) $stmt->bind_param($params, $_GET["filter_user_agent"]);
Additionally, the SQL query works perfectly when I use the query but just manually replace the ?s with the filters, so it does not appear to be a problem with the initial query, but rather the prepared statement. Help would be much appreciated.
NB: The commented out echos are for debug purposes, and are usually disabled, but I have left them in to show how I got the information that I currently have.
EDIT: Answered my own question below - the error was that I was using exec() - a function which executes external shell commands, whereas what I wanted was eval(), which evaluates a string input and the executes it as a PHP script.
Turns out I was simply mistaken as to the function of exec(). (Too much Python?) exec() runs an external shell command, whereas what I was looking for was eval() which evaluates and runs any string inputted as PHP code.
Looks like you never actually use the values $params and $params2. You concatenate them, but where is your $stmt->bind_param()? It's commented out?
I don't see a $conn->prepare("") either. Did you omit some code?
$conn is defined as a mysqli object and then again never used. Something's not right here.
Edit:
Try exec("\$stmt->bind_param(\$params\$params2);"); I assume you somehow execute the code and escape the variables - following that logic you should escape both params and params2, perhaps?

Having trouble with PDO Prepared Statement and "LIKE"

So, first off, I know there are certain rules you have to follow when preparing a LIKE statement with PDO. I have already looked these up and I'm trying my best to follow them, but the query consistently returns no results even though I know the query itself is legitimate (MySQL command line client works correctly with the query).
This is for a school project; I need to make a website with a MySQL/php backend for a fictional bookstore.
I have a class in a php script called DBConnection. It is in a separate namespace (hence the backslashes for PDO objects and functions). This is part of it:
<?php
class DBConnection {
// ...
public function prepAndExecute($sql, $args) {
try {
$stmt = $this->conn->prepare($sql);
for($i = 1; $i <= count($args); $i++) {
$stmt->bindValue($i, $args[$i-1], \PDO::PARAM_STR);
}
$stmt->execute();
return $stmt;
} catch(\PDOException $e) {
return false;
}
}
}
?>
The actual MySQL query I am trying to run:
SELECT ISBN, Title, Author, Price FROM Book WHERE Title LIKE "%rich%";
My attempt at using a PDO Prepared Statement to run this on the website:
<?php
// based on the search form from the previous page
// (all values are set correctly by the form, already tested)
$criteria = $_POST["searchCriteria"]; // "Title" (from a <select> element)
$term = $_POST["searchTerm"]; // "rich" (from the text box)
$conn = new DBConnection(); // uses namespace correctly, just didn't
// include here for simplicity
$sql = "SELECT ISBN, Title, Author, Price FROM Book WHERE ? LIKE ?";
$stmt = $conn->prepAndExecute($sql, array($criteria, "%" . $term . "%"));
// I have also tried $term = "%" . $term . "%", still no luck
echo $stmt->rowCount(); // 0
?>
I ran the above query in the MySQL command line, and got 1 result as expected. I know the class/functions work because I use that same function to run all other SELECT and INSERT queries, and have had no problems until I try to run this LIKE statement.
Am I doing something wrong? Because I double and triple checked everything and could have sworn I was doing this right.
http://php.net/manual/en/pdostatement.bindparam.php
$sth = $dbh->prepare('SELECT * FROM `users` WHERE `firstname` LIKE :keyword');
// Put the percentage sing on the keyword
$keyword = "%".$keyword."%";
// Bind the parameter
$sth->bindParam(':keyword', $keyword, PDO::PARAM_STR);

Prepared statement issue

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.

PDO BindValue without adding to query!

Im trying to find out the best way to execute these types of queries
What I want to do is build a filter type function where i was in an array and the query is constructed!
the problem is with PDO before you can bind a value you need to prepare the statement, but if i prepare the statement I cant change the query.
Let me show you an example:
public function GetFeedsByFilter($filter = array())
{
//Base Query
$Query = 'SELECT feeds,sites,users,categories WHERE feed_site_id = site_id AND feed_uid = user_id AND feed_category_id = category_id';
if(isset($filter['limit'])){$filter['limit'] = 30;} //Setters
//Check the different filters
if(isset($filter['category']))
{
//Here I want to bind the category
//i can do the following
$Query .= ' AND category_id = :cid';
//But now I cant bind the value with $statement->bindValue
}
}
now I can first construct the string for the query then do all the if statements to bind them but that's way to much!
Any ideas how I can overcome this issue ?
you can fill an array with the placeholders in your if parts.
e.g
if(isset($filter['category'])){
$query .= ' AND category_id = :cid';
$binds['cid'] = 123;
}
Then "prepare" your query and give the bind values as option in the execute command
$pdo->execute($binds);
see http://uk.php.net/manual/de/pdostatement.execute.php for more info

Categories