How to make dynamic postgres prepared statements in PHP - php

I'm trying to make some prepared statements in PHP using postgres.
It's a bit difficult to explaing so i'll just show you:
$stmt = "SELECT * FROM customer WHERE zip = '$1'";
if(isset($_POST["CITY"])){
$stmt .= "AND city = '$2'";
}
if(isset($_POST["COUNTRY"])){
$stmt .= "AND country = '$3'";
}
$result = pg_prepare("myconnection", "my query", $stmt);
$result1 = pg_execute("myconnection","my query", array("0000","someCity","someCountry"));
Sorry if some of the code is wrong but it's a freehand example. What I need is to be able to make the prepared statement dynamic depending on if some variables isset/not-null.
It doesn't seem to work when posting 3 variables in the array when the statement only expects 1 or if i only need to add $1 and $3 but not $2. I hope you understand.
I need to use it this weekend, so I hope someone knows!
Thank you in advance!

In a prepared statement, the SQL is static on purpose. The number of parameters cannot vary once the statement is prepared.
But it'd be easy for your code to submit the right number of parameters depending on the statement. You could add a variable for the counter of parameters, and a dynamic php array to pass to pg_execute instead of hard-coded literals. And they would be incremented/populated inside the if (isset(...)) branches.

There is nothing wrong in having 3 different statements (one for each case) and execute the one that applies depending on the number of parameters passed.
Example:
EDIT: I modified the code to match all cases:
Only the zip specified
Zip + city
Zip + country
Zip + city + country
(even if there are some other cases, you'll understand the idea)
$stmt = "SELECT * FROM customer WHERE zip = '$1'";
if(isset($_POST["CITY"]) && isset($_POST["COUNTRY"])) {
$stmt3 = $stmt . " AND city = '$2'" . " AND country = '$3'";
} elseif(isset($_POST["CITY"])) {
$stmt1 = $stmt . " AND city = '$2'";
} elseif(isset($_POST["COUNTRY"])) {
$stmt2 = $stmt . " AND country = '$2'";
}
if(isset($stmt3)) {
$result = pg_prepare("myconnection", "my query", $stmt3);
$result1 = pg_execute("myconnection","my query", array("0000","someCity","someCountry"));
} elseif(isset($stmt2)) {
$result = pg_prepare("myconnection", "my query", $stmt2);
$result1 = pg_execute("myconnection","my query", array("0000","someCountry"));
} elseif(isset($stmt1)) {
$result = pg_prepare("myconnection", "my query", $stmt1);
$result1 = pg_execute("myconnection","my query", array("0000","someCity"));
} else {
$result = pg_prepare("myconnection", "my query", $stmt);
$result1 = pg_execute("myconnection","my query", array("0000"));
}
I omitted (just as you did) all the error checks for brevity.

Although both Daniel and aymeric are correct - no sense in testing twice, nor using numbers. See below:
$some_vars = array();
$some_vars[":zip"] = $_POST["ZIP"];
$stmt = "SELECT * FROM customer WHERE zip = :zip";
if(isset($_POST["CITY"])){
$some_vars[":city"] = $_POST["CITY"]);
$stmt .= " AND city = :city";
}
if(isset($_POST["COUNTRY"])){
$some_vars[":country"] = $_POST["COUNTRY"]);
$stmt .= " AND country = :country";
}
$result = pg_prepare("myconnection", "my query", $stmt);
$result1 = pg_execute("myconnection","my query", $some_vars);
Don't forget to sanitize and such.

Don't do string concatenation. Check if the parameters are set. If not set them to empty. Use a single query string:
$zip = $_POST["zip"];
$city = $_POST["city"];
$country = $_POST["country"];
if (!isset($zip)) $zip = '';
if (!isset($city)) $city = '';
if (!isset($country)) $country = '';
$stmt = "
select *
from customer
where
(zip = '$1' or '$1' = '')
and
(city = '$2' or '$2' = '')
and
(country = '$3' or '$3' = '')
";
$result = pg_prepare("myconnection", "my query", $stmt);
$result1 = pg_execute(
"myconnection",
"my query",
array($zip, $city, $country)
);
Each condition will only be enforced if the respective parameter is not the empty string.
The same logic could use the null value in stead of empty those columns contain empty strings that should be selected.

Related

Php search with multiple values

i try to make a search system which has multiple inputs. I tried to to select the data in query and that it supose to add the aditional data if it exist but it dont do that. i messed it up and dont know how to fix it. The data from my other file gets there. I checked it. Did anyone know what i did wrong?
<?php
$connect= mysqli_connect("localhost", "root", "root", "website");
$query = "SELECT * FROM data";
$sort = " date DESC"
if(isset($_GET["search"])){
$keywordsearch = $connect->escape_string($_GET['search']);
$query .= " WHERE name like '%$keywordsearch%'";
if(isset($_GET['tag'])){
$keywordtag = $connect->escape_string($_GET['tag']);
$query .= " AND tag like '%$keywordtag%'";
$query .= " ORDER BY $sort";
}
}
$result = mysqli_query($connect, $query);
while($row = mysqli_fetch_array($result)){
echo $row['name']."<br>";
}
?>
This can be done by creating an array of the data passed in and using that array you can control all the other requirements to build a query and pass parameters using a Parameterised and bound query
$_GET = ['search'=> 'fluff', 'tag'=>'easy'];
$connect= mysqli_connect("localhost", "root", "root", "website");
$query = "SELECT * FROM data";
$sort = " ORDER BY date DESC";
if(isset($_GET["search"])){
$query .= " WHERE name like ?";
$vals[] = '%' . $_GET["search"] . '%';
}
if(isset($_GET['tag'])){
$query .= " OR tag like ?";
$vals[] = '%' . $_GET["tag"] . '%';
}
$query .= $sort;
$types = str_repeat('s', count($vals));
$stmt = $connect->prepare($query);
$stmt->bind_param($types, ...$vals);
$stmt->execute();
$result = $stmt->get_result();
while($row = $result->fetch_array(MYSQLI_ASSOC)){
echo $row['name']."<br>";
}
RESULT:
easy peasy<br>
fluff fluff<br>
From your comment: problem is that i can input the tag variable and if the tag variable is something that is not included in the database it shows nothing
That is because you are using an AND rather than an OR in the query. I corrected that also

Dynamic value in sql query using php

I want to search a certain string in all the columns of different tables, so I am looping the query through every column name. but if i give it as dynamic value it does not seem to work.
what is wrong?
<?php
$search = $_POST['search'];
$columns = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'feedback'";
$columns_result = $conn->query($columns);
$columns_array = array();
if (!$columns_result) {
echo $conn->error;
} else {
while ($row = $columns_result->fetch_assoc()) {
//var_dump($row);
//echo $row['COLUMN_NAME']."</br>";
array_push($columns_array, $row['COLUMN_NAME']);
}
}
var_dump($columns_array);
$row_result = array();
for ($i = 0; $i < count($columns_array); $i++) {
echo $columns_array[$i] . "</br>";
$name = "name";
// $sql = 'SELECT * FROM feedback WHERE "'.$search.'" in ("'.$columns_array[$i].'")';
$sql = 'SELECT * FROM feedback WHERE ' . $name . ' like "' . $search . '"';
$result = $conn->query($sql);
if (!$result) {
echo "hi";
echo $conn->error;
} else {
foreach ($result as $row) {
array_push($row_result, $row);
echo "hey";
}
}
}
var_dump($row_result);
I am getting the column names of the table and looping through them because I have so many other tables which I need to search that given string. I don't know if it is optimal I did not have any other solution in my mind. If someone can tell a good way I will try that.
It looks to me that you want to generate a where clause that looks at any available nvarchar column of your table for a possible match. Maybe something like the following is helpful to you?
I wrote the following with SQL-Server in mind since at the beginning the question wasn't clearly tagged as MySql. However, it turns out that with a few minor changes the query work for MySql too (nvarchar needs to become varchar):
$search='%';$tbl='feedback';
if (isset($_POST['search'])) $search = $_POST['search'];
$columns = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '$tbl' AND DATA_TYPE ='nvarchar'";
$columns_result = $conn->query($columns);
$columns_array = array();
if(!$columns_result) print_r($conn->errorInfo());
else while ($row = $columns_result->fetch(PDO::FETCH_ASSOC))
array_push($columns_array, "$row[COLUMN_NAME] LIKE ?");
$where = join("\n OR ",$columns_array);
$sth = $conn->prepare("SELECT * FROM $tbl WHERE $where");
for ($i=count($columns_array); $i;$i--) $sth->bindParam($i, $search);
$sth->execute();
$result = $sth->fetchAll(PDO::FETCH_ASSOC);
print_r($result);
The above is a revised version using prepared statements. I have now tested this latest version using PHP 7.2.12 and SQL-Server. It turned out that I had to rewrite my parameter binding part. Matching so many columns is not a very elegant way of doing queries anyway. But it has been a nice exercise.
It looks like you are using mysqli, so I wanted to give another way of doing it via mysqli.
It does more or less the same as cars10m solution.
$search = $_POST['search'];
$columns = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'feedback'";
$columns_result = $conn->query($columns)->fetch_all(MYSQLI_ASSOC);
// Here dynamically prepare WHERE with all the columns joined with OR
$sql = 'SELECT * FROM feedback WHERE ';
$arrayOfWHERE = [];
foreach($columns_result as $col){
$arrayOfWHERE[] = '`'.$col['COLUMN_NAME'].'` LIKE ?';
}
$sql .= implode(' OR ', $arrayOfWHERE);
// prepare/bind/execute
$stmt = $conn->prepare($sql);
$stmt->bind_param(str_repeat("s", count($arrayOfWHERE)), ...array_fill(0, count($arrayOfWHERE), $search));
$stmt->execute();
$result = $stmt->get_result();
$row_result = $result->fetch_all(MYSQLI_ASSOC);
var_dump($row_result);
Of course this will search for this value in every column of the table. It doesn't consider data type. And as always I have to point out the using PDO is better than mysqli. If you can switch to PDO.

SQL in php, make partial search using LIKE

I am trying to make SQL in php to return all the entries that matches a keyword that is entered by the user (from search bar).
I want to return all the entries that their name "partial" matches with the keyword.
I want at least to match the keyword, if an entry name in database before has space and after maybe another letter/space.
For example I have three entries with names "Milk", "Semi skimmed Milk" and "Full Milk 2". If the keyword is "Milk" or "milk" or "MiLK", I want to get all these three entries.
The only case I am thinking it might be the problem is case sensitive.
I tried with a keyword that exists exactly in database, but my app (on android) stops .
Based on user3783243 answer.
PHP FILE
<?php
$servername = "";
$username = "";
$password = "";
$dbname = "";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT name FROM items WHERE name LIKE CONCAT ('%', ?, '%')";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $keyword);
$res = $stmt->get_result();
while($row = $res->fetch_assoc()) {
echo $row["name"] . ",";
}
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo $row["name"] . ",";
}
} else {
echo "0";
}
$conn->close();
?>
Your query should be:
$sql = "SELECT * FROM items WHERE name LIKE CONCAT ('%', ?, '%')";
and then $keyword should be bound with whatever syntax the driver you are using supports.
As is your query would have been:
SELECT * FROM items WHERE name LIKE CONCAT ('%', Milk, '%')
and you wanted Milk to be a string so it needed to be quoted. As is mysql would have thought that was a column.
Alternatively you could do:
$keyword = '%' . $_POST['keyword'] . '%';
$sql = "SELECT * FROM items WHERE name LIKE CONCAT ?";
that is the same and still requires the binding though.
The binding also takes away the SQL injection. See How can I prevent SQL injection in PHP? and/or https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet#Defense_Option_1:_Prepared_Statements_.28with_Parameterized_Queries.29
Per update.. replace:
$keyword =$_POST['keyword'];
$sql = "SELECT * FROM items WHERE name LIKE '%$keyword%)";
$result = $conn->query($sql);
with:
$sql = "SELECT name FROM items WHERE name LIKE CONCAT ('%', ?, '%')";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $keyword);
$stmt->execute();
$res = $stmt->get_result();
if(empty($conn->errno) && !empty($res)) {
while($row = $res->fetch_assoc()) {
echo $row["name"] . ",";
}
} else {
echo '0';
//print_r($conn->errno);
}
$conn->close();
...
also remove
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo $row["name"] . ",";
}
} else {
echo "0";
}
$conn->close();
In this case you can convert the input in search bar to either upper or lower case by default then apply query in db like
For Upper case:
$keyword =strtoupper($_POST['keyword']);
$sql = "SELECT * FROM items WHERE upper(name) LIKE '%$keyword%)";
Or for lower case:
$keyword =strtolower($_POST['keyword']);
$sql = "SELECT * FROM items WHERE lower(name) LIKE '%$keyword%)";

Using Custom Variables with PHP Prepare statement

Here I go... I have a search function for users, like this:
if ($_GET['s_country']){
$s_country = htmlentities($_GET['s_country'],ENT_QUOTES,"UTF-8");
$s_country=trim($s_country);
$s_country_row = " and country= ?";
} else {
$s_country="";
$s_country_row = " and (country= ? or not country= '')";
}
$s_city = "";
$s_city_row = " and (city= ? or not city= '')";
if ($_GET['s_city']){
$s_city = strip_tags($_GET['s_city']);
$s_city=trim($s_city);
$s_city_row = " and city= ?";
}
$search = mysqli_prepare($dbconnect, "SELECT id FROM user WHERE gender=? $s_country_row $s_city_row");
mysqli_stmt_bind_param($search, 'iss', $s_gender, $s_country, $s_city);
In the above example I have used my own way to dismiss variables. I need to dismiss/remove variables that are not searched for or have no input.
If "country" is not searched for, it should return all rows with all "country" values.
Is there any better way to do this? (Without prepare statement it is quite easy to customize everything, but I hope security does not mean less flexibility).
Thank you for reading this
$country = isset($_GET['s_country'])? " AND country=\'$_GET['s_country']\'" : '';
$query = " SELECT id FROM user WHERE gender=? $country ";
This is the pseudo idea
here query is forming in such a way that
- if it contains country then : SELECT id FROM user WHERE gender=? and country = 'US';
- if country is not defined then : SELECT id FROM user WHERE gender=?
as no conditions for country in the second query this will throw all country rows with particular gender

PHP to create MySQL query from URL query

I have a relatively small search form that I want people to use to search my SQL database.
The only way I've done this before was with a billion nested if statements. Is there a better way to do this?
I am parsing my URL query string, so I have my variables of say:
$city
$bedrooms
$elementaryschool
If I were to just try to try:
$sql = "SELECT * FROM $table_name WHERE ";
if(isset($city)) {
$sql .= " `City` LIKE " . $city;
}
if(isset($bedrooms)) {
$sql .= " AND `Bedrooms` >= " . $bedrooms;
}
if(isset($elementaryschool)) {
$sql .= " AND `ElementarySchool` = " . $elementaryschool;
}
Then I run into an issue when $city isn't set because my query ends up with "SELECT * FROM $table_name WHERE AND Bedrooms >= $bedrooms"
That wouldn't exactly work. What are my options?
I completely understand how to do it if I am including all parameters in my query, which seems to be what all previous questions have asked. But how do I do this when not all fields will have a value? I have a total of 12 possible fields to use for searching, and they can search by just 1 or by all 12.
As I mentioned before, all of the questions I have been able to find refer to coming up with one static SQL query, instead of one that will have varying number of parameters.
I would go with:
$sql = "SELECT * FROM $table_name";
$where = array();
$params = array();
and then:
if(isset($city)) {
$where[] = "City LIKE ?";
$params[] = $city;
}
if(isset($bedrooms)) {
$where[] = "Bedrooms >= ?";
$params[] = $bedrooms;
}
if(isset($elementaryschool)) {
$where[] = "ElementarySchool = ?";
$params[] = $elementaryschool;
}
and finally:
if(!empty($where)) {
$sql .= "WHERE " . implode(" AND ", $where);
}
$result = $db->prepare($sql)->execute($params);
PLEASE NOTE that here, since I do not know what kind of database layer/abstraction you are using, $db represents the database connection, prepare() is used to create a prepared statement, and execute() tu run it, as you would do using PDO; this also protects against SQL injection.

Categories