SQLITE build query string from values passed - php

Hi have a function that gets passed strings via the below;
getFilterResults($id, $limit, $misc);
and in my function i connect to SQLite DB and build a query based on the values passed through, like so;
function getFilterResults($id, $limit, $misc) {
$dbConnect = new SQLite3(database);
if(!empty($limit) && !empty($id) && !empty($misc)){
$buildString = ('SELECT * FROM fields ORDER BY id DESC');
}else{
//no params filters arrived so dont filter - aka get all results
$buildString = ("SELECT * FROM fields ORDER BY id DESC");
}
$query = $dbConnect->query($buildString);
//...
}
Issue is how do I build the query if some of the values are empty and I have to decide which value uses/starts of the 'WHERE' query..obviously the first value return that is not null starts of the 'WHERE'..
I can see the long way would be to loop through each one and build - once the first value found that is not null and kick off from there but that seems unpractical and not good practice.
//check if limit set..
if($limit) {
$doLimit = 'LIMIT '.$limit.'';
}
WHERE 'id = '.id.' AND misc = '.$misc.' '.$doLimit.'

Your function taking in ($id, $limit, $misc) really prevents you from being able to do anything else than what you already are... I would recommend using prepared statements though
// Stores values for a prepared statement
$values = [];
$sqlite = new SQLite3(/* ... */);
$query = 'SELECT * FROM fields WHERE id = ? AND misc = ? ORDER BY id DESC';
$values[] = $id;
$values[] = $misc;
if ($limit) {
$query .= ' LIMIT ?';
$values[] = $limit;
}
// Use prepare and let SQLite3 escape the values for you
$stmt = $sqlite->prepare($query);
foreach ($values as $idx => $val) {
$stmt->bindValue($idx, $val);
}
$result = $stmt->execute();

Related

What is the correct way of printing results set by `SELECT *` with multiple `WHERE` clauses in prepared statements using `fetch_assoc()` in PHP-MYSQL?

My query is below:
$query = 'SELECT * FROM `table_name` WHERE `uid` = ? AND `email` = ?';
$stmt_select = $conn->prepare($query);
$stmt_select->bind_param('is', $user_id, $user_email);
$user_id = $_SESSION['uid'];
$user_email = $_SESSION['user_email'];
$result = $stmt_select->execute();
I want to print results set using fetch_assoc, this is because I don't want to bind_result() to all 23 columns' name, I want to print it using $row['column_name'], what is the correct way of achieving it?
If you want to see my code, I asked another question here:
How to SELECT * with multiple WHERE using fetch_assoc in prepared statements in PHP-MYSQL?
But it didn't get answered correctly, so instead of correcting my code, can you tell me what's the best way to achieve it?
I had the same issue using SELECT * in mysqli. The issue lies with the bind_result part. Normally you would need to add many variables. Meaning if you had about 20 columns, you would need 20 variables to include in the bind_result statement.
I've tailored my solution to fix your issues.
$query = 'SELECT * FROM `table_name` WHERE `uid` = ? AND `email` = ?';
$stmt_select = $conn->prepare($query);
$stmt_select->bind_param('is', $user_id, $user_email);
$user_id = $_SESSION['uid'];
$user_email = $_SESSION['user_email'];
$result = $stmt_select->execute();
$stmt_select->store_result();//after this line we can output the number of rows if you need to check that as well
$number_of_rows = $stmt_select->num_rows;
$meta = $stmt_select ->result_metadata();
$parameters = array();
$results = array();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt_select , 'bind_result'), $parameters);
while ($stmt_select ->fetch()) {
foreach ($row as $key => $val) {
$x[$key] = $val; //here we get the key => value (Column => Value)
}
$results[] = $x; //lets grab everything here
}
print_r($results);

Variable sql query depending on number of search parameter

I need to do a sql query in php for search some entries (so using WHERE). But the field used to search could be of variable number.
I have a page with a search form, with 4 Field. It sends via POST the fields to a search.php that make a query:
$gomme_sql = $data->query("SELECT * FROM table WHERE parameter1 = '$_POST['name1']' AND parameter2 = '$_POST['name2']' ORDER BY id ASC");
But I don't know which field are filled. So, if I don't enter anything in field1 from the search form, I shouldn't have parameter1 = '$_POST['name1']' in the WHERE query.
Have you any idea how to obtain this?
Thank you
You can check the post data before appending that clause to the query in a way like this:
edit: adding additional check:
$sql="select something from someTable ";
if(!empty($_POST['name1']) || !empty($_POST['name2'])) // add as many as you like
{
$sql.=" where ";
if(!empty($_POST['name1']))
{
$sql.="parameter1= $_POST['name1']";
}
// etc etc...
}
$sql.=" ORDER BY id ASC";
and so on.
Having said that, please, please use prepared statements with this sort of input from the user. This is SUPER open to sql injection. Please do read this: How can I prevent SQL injection in PHP?
You can write generic sql select function like this , if you need more complex SQL just modify it.
<?php
function sqlSelect($table, $sel, $wh = '', $groupby = '', $order = '', $add = '') {
$tb = $table;
if (is_array($table)) {
$tb = implode(',', $table);
}
if ($wh) {
if (is_array($wh)) {
$w = array();
foreach ($wh as $k => $v) {
$v = mysqli_real_escape_string($v);
if (is_null($v))
$w [] = "$k=null ";
else
$w [] = "$k ='$v'";
}
$wh = 'where ' . implode(' and ', $w);
}else {
$wh = "where $wh";
}
}
if ($groupby)
$groupby = "group by $groupby";
if ($order)
$order = "order by $order";
$sql = "select $sel from $tb $wh $groupby $order $add ";
return $sql;
}
//set _GET as this is console test
$_GET['name1']='Bob';
$where = array(
'name1'=>$_GET['name1']
);
echo sqlSelect('sometable' , '*' , $where) ."\n";
// select * from sometable where name1 ='Bob'
//or some complex stuff
echo sqlSelect('persons', "age,status" , array('name'=>'Maria' , 'likes'=>'PHP') , null, 'age' , 'limit 20');
//select age,status from persons where name ='Maria' and likes ='PHP' order by age limit 20

Fetching data from database into array

I am using mysqli to fetch data from the database and put it into an array with a while loop. When i echo out the array i get an empty array however in a function i previously did this code worked but it had a different result from the database. I know that the database is giving out good data because when i echo out the result $idGroup it gives me 2 which is correct.
Ps i know it will keep replacing itself because i don't specify an index
private function Groups()
{
$functionRun = 0;
$i = 0;
$helperArray = array();
$this->grouplist = array();
$query = "SELECT GroupName, Groups.idGroup
FROM Groups
INNER JOIN Members
ON Groups.idGroup = Members.idGroup
WHERE Members.idMember = ? ";
//prepare query and execute
if($stmt = $this->connection->prepare($query))
{
$stmt->bind_param('s', $this->id);
$stmt->execute();
$stmt->bind_result($GroupName, $idGroup);
while ($stmt->fetch())
{
$helperArray[] = $idGroup;
}
echo $helperArray;
}
Use print_r when dealing with arrays. Use echo on strings.
Try this
$query = "SELECT GroupName, Groups.idGroup
FROM Groups
INNER JOIN Members
ON Groups.idGroup = Members.idGroup
WHERE Members.idMember = ? ";
//prepare query and execute
if($stmt = $this->connection->prepare($query))
{
$stmt->bind_param('s', $this->id);
$stmt->execute();
$stmt->bind_result($GroupName, $idGroup);
$helperArray =array();
while ($stmt->fetch())
{
$helperArray[] = $idGroup;
}
print_r($helperArray);
}

PDO not substituting parameters properly

I'm making a blog based on PHP and MySQL (using PDO to connect). I have written a function (below) that will retrieve blog posts and return them, but order by isn't working when I pass it by reference:
<?php
/**
* Get blog posts, filtered and sorted to taste.
* #return array Array of rows - each row is an array indexed by column name.
* #param string $inFunction What to select posts by - id, date or tag(s).
* #param string $inFilter Filter data to select posts by - id no., date(s) or tag(s). If you are filtering by date and only specify one, it will be taken to be the 'newer than' date.
* #param string $inOrder How to sort posts. This parameter is fed directly into the query and therefore should be raw SQL.
* #param object $inDatabase Database handle to pass to the SQL query.
*/
function getBlogPosts($inFunction, $inDatabase, $inFilter, $inOrder) {
switch ($inFunction) {
case "permalink": {
$query = $inDatabase->prepare("select * from blog_posts where permalink = :permalink");
$query->bindValue(":permalink", $inFilter);
$query->execute();
$result = $query->fetch();
return new BlogPost($result["id"], $result["title"], $result["permalink"], $result["post_full"], $result["post_sample"], $result["tags"], $result["timestamp"]);
break;
}
case "number": {
$query = $inDatabase->prepare("select * from blog_posts
order by :order
limit :limit_start , :limit_end");
$query->bindParam(":order", $inOrder);
$splitLimits = explode(", ", $inFilter);
if (sizeOf($splitLimits) === 1)
$splitLimits[] = 1; // First post
$limitEnd = $splitLimits[0] + $limitStart;
$limitStart = $splitLimits[1] - 1;
$query->bindValue(":limit_start", (int) $limitStart, PDO::PARAM_INT);
$query->bindValue(":limit_end", (int) $limitEnd, PDO::PARAM_INT);
$query->debugDumpParams();
$query->execute();
$results = $query->fetchAll(PDO::FETCH_ASSOC);
$return = array();
foreach ($results as $result) {
$return[] = new BlogPost($result["id"], $result["title"], $result["permalink"], $result["post_full"], $result["post_sample"], $result["tags"], $result["timestamp"]);
}
return $return;
break;
}
case "id": {
$query = $inDatabase->prepare("select * from blog_posts where id = :id order by :order");
$query->bindParam(":id", $inFilter);
$query->bindParam(":order", $inOrder);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC); // Prevents duplicate results when using loops (foreach, while etc.)
break;
}
case "date": {
$splitdate = explode(", ", $inFilter);
$query = $inDatabase->prepare("select * from blog_posts
where (date_posted > :newerthan_date)
and (date_posted <= :olderthan_date)
order by :order");
if (sizeof($splitdate) === 1) {
$splitdate[] = date("Y-m-d");
}
$query->bindParam(":newerthan_date", $splitdate[0]);
$query->bindParam(":olderthan_date", $splitdate[1]);
$query->bindParam(":order", $inOrder);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
break;
}
case "tag": {
$tags = explode(", ", $inFilter);
$insert = "";
foreach ($tags as $key => $tag) {
if ($key === 0) {
$insert .= "where tags like :tag_{$key}";
}
else {
$insert .= " or tags like :tag_{$key}";
}
}
$query = $inDatabase->prepare("select * from blog_posts
{$insert}
order by :order");
foreach ($tags as $key => $tag) {
$query->bindValue(":tag_{$key}", '%^' . $tag . '^%');
}
$query->bindParam(":order", $inOrder);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
break;
}
}
}
On the main page, $results = getBlogPosts("number", $sql_conn, "10", "timestamp desc"); is called and a foreach loop iterates through the posts. The problem is that PDO does not seem to be applying the :order parameter; whatever I put for $inOrder it always has the same order. If I go and directly change the statement within the getBlogPosts function to have order by timestamp desc instead of order by :order, it works fine.
I'm stumped - any ideas?
"This parameter is fed directly into the query and therefore should be raw SQL." => the comment is correct, the code is not, fix this. The reason is you can specify strings/numbers/etc. with parameters, but not identifiers (column names etc.).
What your query does it this:
SELECT ... FROM ... ORDER BY 'columnname';
Rather then:
SELECT ... FROM ... ORDER BY columnname;
So. it sorts by the string 'columnname', not the value in the field with the same name, with is the same for every row, so no sorting takes place (you might as well ORDER BY 1. The solution here is to add that order by clause as raw MySQL as the docblock comment states. If you want more control over it / prevent nastiness there, you could provide a whitelist of allowable order by clauses and refuse others.

SQL Injection - is this query secure?

I have a a page that appends different parameters to the URL that are used for the query.
For example
http://www.example.com/search.php?category=Schools&country[]=Belgium&country[]=Czech+Republic
My code is like this
if(isset($_GET['country'])){
$cties = "'" . implode("','", $_GET['country']) . "'";
}
else {
$cties = "'Albania','Andorra','Austria','Belarus','Belgium','Bosnia & Herzegovina','Bulgaria','Croatia','Czech Republic','Denmark','Estonia','Faroe Islands','Finland','France','Germany','Gibraltar','Great Britain','Greece','Hungary','Iceland','Ireland','Isle of Man','Italy','Latvia','Liechtenstein','Lithuania','Luxembourg','Macedonia','Malta','Moldova','Monaco','Montenegro','Netherlands','Norway','Poland','Portugal','Serbia','Romania','San Marino','Slovakia','Slovenia','Spain','Sweden','Switzerland','Ukraine','United Kingdom'";
}
if(isset($_GET['category'])){
$cat = $_GET['category'];
}
else{
$cat = " ";
}
try{
// create the Prepared Statement
$stmt = $con->prepare("SELECT * FROM MyTable
WHERE MyDate >= DATE(NOW())
AND (Category=:cat or '' = :cat)
AND Country IN ($cties)
ORDER BY MyDate ASC");
$stmt->bindValue(':cat', $cat, PDO::PARAM_STR);
$stmt->execute();
I was wondering if this query is secure and if not, what I am doing wrong.
Thanks in advance!
I finally got it (thanks to Your Common Sense):
if(isset($_GET['country'])){
$arr = $_GET['country'];
}
else {
$arr = array('Albania','Andorra','Austria','Belarus','Belgium','Bosnia & Herzegovina','Bulgaria','Croatia','Czech Republic','Denmark','Estonia','Faroe Islands','Finland','France','Germany','Gibraltar','Great Britain','Greece','Hungary','Iceland','Ireland','Isle of Man','Italy','Latvia','Liechtenstein','Lithuania','Luxembourg','Macedonia','Malta','Moldova','Monaco','Montenegro','Netherlands','Norway','Poland','Portugal','Serbia','Romania','San Marino','Slovakia','Slovenia','Spain','Sweden','Switzerland','Ukraine','United Kingdom');
}
if(isset($_GET['category'])){
$cat = $_GET['category'];
}
else{
$cat = " ";
}
// create the Prepared Statement
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM MyTable WHERE MyDate >= DATE(NOW())
AND Country IN ($in)
AND (Category=? or '' = ?)
ORDER BY MyDate ASC";
$stmt = $con->prepare($sql);
$arr[] = $cat; // adding category to array
$arr[] = $cat; // we need it twice here
// finally - execute
$stmt->execute($arr);
Yeah, Now I see your problem. Well, PDO is not too convenient a library for such a task. So, first of all I'll show you how it can be done with my own library:
$sql = "SELECT * FROM MyTable WHERE MyDate >= CURDATE()
AND (Category=?s or '' = ?s)
AND Country IN (?a)
ORDER BY MyDate ASC"
$data = $db->getAll($sql, $cat, $cat, $_GET['country']);
But I quite realize that you all so inclined to familiar methods. Well, let's elaborate with ugly PDO
First of all, what is the goal? The goal is
to create the query that contains placeholders for all the data. I'll stick to positional placeholders as they are easier to implement.
To create an array with all the variables that have to be bound to placeholders
It seems we need two placeholders for category and some unknown number fro cities. All right, this line will create a string of placeholders:
$in = str_repeat('?,', count($arr) - 1) . '?';
which we are going to insert into query.
// $arr is array with all the vars to bind. at the moment it contains cities only
$arr = $_GET['country'];
// creating string of ?s
$in = str_repeat('?,', count($arr) - 1) . '?';
// building query
$sql = "SELECT * FROM MyTable WHERE MyDate >= DATE(NOW())
AND Country IN ($in)
AND (Category=? or '' = ?)
ORDER BY MyDate ASC";
$stm = $db->prepare($sql);
$arr[] = $_GET['category']; // adding category to array
$arr[] = $_GET['category']; // we need it twice here
// finally - execute
$stm->execute($arr);
$data = $stm->fetchAll();
No, the SQL code could be injected in the $_GET['country'] parameter. You don't escape it anywhere.
See PHP PDO: Can I bind an array to an IN() condition?

Categories