php mysql search options combination - php

i have got 4 checkboxes for filterung mysql result. Checkboxes can be activated all or single, too. I don't know how to make sql statement. DO i really have to use all combined possibilities manually or is there a simplier solution? Perhaps with "switch"?
No as first statement i have:
if ($vart1 == "1" AND !isset($vart2) AND !isset($vart2) AND !isset($vart2) AND !isset($vart4)) {
$tname_sql .= " a.tdesc = 'option1' AND";
};
How many variations are there?
Thank you for help.
Regards,
Olaf.

You can make a simple PHP function which returns WHERE or AND depending how many times has been called:
$wa = 0;
function whereAnd() {
global $wa;
if ($wa == 0) {
$wa = 1;
return ' WHERE ';
} else {
return ' AND ';
}
}
$query = "SELECT * FROM table1 t ";
if (isset($var1)) {
$query .= whereAnd() . "t.field1 = " . $var1;
}
if (isset($var2)) {
$query .= whereAnd() . "t.field2 = " . $var2;
}
if (isset($var3)) {
$query .= whereAnd() . "t.field3 = " . $var3;
}
if (isset($var4)) {
$query .= whereAnd() . "t.field4 = " . $var4;
}
First call will returns WHERE, all other calls will return AND no matter what kind of combinations you may have. You don't need to care about how many checkboxes are passed.

Related

PHP/PDO Dynamically binding values (invalid param count error)

I was tasked (stuck) with trying to update an old mysql_query code to be PDO compliant.
This was (is) a messy search form, that was dynamically creating the query string based on field values if (or not) there were any key words submitted along with the form. (ie: any key word is parsed by spaces, and used for BOTH column searches)
So if a search term of 'dog' was entered.. it would search name & title for the key word of 'dog'..
I think I made my way through it.. keeping the main 'function' in-tact for the most part.. and updating when I needed to.
My approach was to take the function that is dynamically adding more criteria to the query string.... and also add this value field name & value to an array, so I can loop through it later on and dynamically bindValues with it..
I am now stick with the ever so popular Invalid Parameters error!!
However its not saying the counts dont match.. its saying it was defined at all.
I'm not clear where my error is stemming from.. (or how to easily see the computed/parsed query string.. or the actual bound parameters) I can just output the sql statement (before it parses any data).. or echo out my values in the array I loop through to (potentially) bind the data to the PDO call..
WHen I echo out the query (string).. and even the values I am attempting to dynamically bind... they all look legit to me:
Query Check: SELECT * FROM pid_information WHERE 1=1 AND (((title LIKE :title0) OR (name LIKE :name0)) AND ((title LIKE :title1) OR (name LIKE :name1))) ORDER BY title, name, link
PARAM CHECK: ':title0' -> %cat%
PARAM CHECK: ':name0' -> %cat%
PARAM CHECK: ':title1' -> %dog%
PARAM CHECK: ':name1' -> %dog%
To re-cap:
addCriteria() function is used to dynamically (concat) add to the query 'string'
I also populate an array to be used later to loop through and bindValues with.
Yes I know it is long.. yes I know ugly.. (please, just bear with me!) LOL
//dynamically add criteria to query
$boundSearchValues = array();
function addCriteria($targetFields, $criteriaString, $targetOperator='LIKE'){
global $boundSearchValues;
$fieldCount = 0;
$tempString = "";
if($criteriaString != ""){
$criteriaArray = explode(" ", $criteriaString);
$tempString .= " AND (";
foreach($criteriaArray as $criteriaIndex => $criteriaValue){
//is array of fields
if(is_array($targetFields)){
$tempString .= "(";
foreach ($targetFields as $targetField => $fieldName){
if($targetOperator != 'LIKE') {
$tempString .= "($fieldName ".$targetOperator." :". $fieldName.$fieldCount .")";
$boundSearchValues[] = [$fieldName.$fieldCount, $criteriaValue];
}else{
$tempString .= "($fieldName LIKE :". $fieldName.$fieldCount .")";
$boundSearchValues[] = [$fieldName.$fieldCount, '%'.$criteriaValue.'%'];
}
if($targetField+1 < count($targetFields)){
$tempString .= " OR ";
}
}
$tempString .= ")";
if($criteriaIndex+1 < count($criteriaArray)){
$tempString .= " AND ";
}
//not an array of fields
}else{
if($targetOperator != 'LIKE') {
$tempString .= "(".$targetFields . $targetOperator . " :" . $fieldName.$fieldCount . ")";
$boundSearchValues[] = [$fieldName.$fieldCount, $criteriaValue];
} else {
$tempString .= "(". $targetFields . " LIKE " . $fieldName . $fieldCount . ")";
$boundSearchValues[] = [$fieldName.$fieldCount, '%'.$criteriaValue.'%'];
}
}
$fieldCount++; //increment counter
}
$tempString .= ")";
}
return $tempString;
}
//start serach query
$searchDetails_sql = "SELECT * FROM $tablename ";
//dynamically update query string
if($clean_keywords != "") {
$whereClause = addCriteria(array('title', 'name'), $clean_keywords);
}else{
if($title != "" && $title != "all"){
$whereClause .= " AND title = :" . $title;
}
if($name != "" && $name != "all"){
$whereClause .= " AND name = :" . $name;
}
if($link != "" && $link != "all"){
$whereClause .= " AND link = :" . $link ;
}
}
$searchDetails_sql .= "WHERE 1=1 ". $whereClause;
$searchDetails_sql .= " ORDER BY title, name, link";
$searchDetails_stmt = $conn->prepare($searchDetails_sql);
//dynamically bind values
for($i=0; $i<count($boundSearchValues); $i++){
$searchDetails_stmt->bindValue("':".$boundSearchValues[$i][0] ."'", $boundSearchValues[$i][1]);
//$searchDetails_stmt->bindParam("':".$boundSearchValues[$i][0] ."'", $boundSearchValues[$i][1]);
echo '<br>PARAM CHECK: ' . $boundSearchValues[$i][0] . " / " . $boundSearchValues[$i][1];
}
$searchDetails_stmt->execute();
$searchDetails_stmt->setFetchMode(PDO::FETCH_ASSOC);
$searchDetails = $searchDetails_stmt->fetchAll(); //returns multi-dimensional array (and correct count)
I think you just messed up the string concatenation in this line
$searchDetails_stmt
->bindValue("':".$boundSearchValues[$i][0] ."'", $boundSearchValues[$i][1]);
You dont actually need the : so you could do this
$searchDetails_stmt
->bindValue($boundSearchValues[$i][0], $boundSearchValues[$i][1]);
Or fix the concatenation and keep the :
$searchDetails_stmt
->bindValue(":".$boundSearchValues[$i][0], $boundSearchValues[$i][1]);

Building WHERE clauses from multiple $_GET's

I am currently trying to write complex MySQL WHERE clauses that are generated from $_GET variables (which themselves come from select dropdowns). First, a bit of code so you know what I am talking about:
if(!isset($_GET['order'])){
$order= 'start asc';
} elseif ($_GET['order'] == "dateasc") {
$order= 'start asc';
} elseif ($_GET['order'] == "titleasc") {
$order= 'title asc';
} elseif ($_GET['order'] == "titledesc") {
$order= 'title desc';
};
if(!isset($_GET['cat'])){
$cat= '0';
} else {
$cat = $_GET['cat'];
};
if(!isset($_GET['loc'])){
$loc= '0';
} else {
$loc = $_GET['loc'];
};
if (isset($_GET['sd']) || isset($_GET['ed']) || isset($_GET['cat']) || isset($_GET['loc']) || isset($_GET['order']) ) {
$where = 'WHERE ';
if (isset($_GET['sd'])) {
$where .= "start = " . $_GET['sd'];
};
if (isset($_GET['ed'])) {
$where .= "AND end = " . $_GET['ed'];
};
if (isset($_GET['cat'])) {
$where .= "AND category = " . $_GET['cat'];
};
if (isset($_GET['loc'])) {
$where .= "AND location = " . $_GET['loc'];
};
};
$result = mysql_query("SELECT * FROM " . TABLE . $where . " ORDER BY " . $order);
Obviously this isn't working, otherwise I wouldn't be here. :) Basically, I have 4 variables that I want to conditionally use for sorting in my query: start date, and end date, a category, and a location. My problem is that all 4 of these may not always be used.. so given the above example, there might be a case where someone selects a category ($cat) but NOT a start date ($sd)... which means my WHERE clause would start off with 'AND', which is obviously invalid. So how do I build a query based off variables that may or may not be used?
I really feel like I am overthinking this, and I am afraid of writing 9000 lines of isset tests to account for every combination of $_GET variable usage. Surely there a simple way to build a WHERE clause from multiple $_GETs that may or may not be used every time..? I've tried Googling but can only find solutions that suggest using a framework for building complex queries and that just seems overly... clunky... for such a simple problem.
If you're just worried about having a where clause that starts with AND you can add 1=1 to account for no filters.
WHERE 1=1
Then, if you have any filters, it will look like this:
WHERE 1=1 AND col1=? AND col2=?
This may not be the cleanest solution, but it should be fairly simple to understand and implement.
if (isset($_GET['sd']) || isset($_GET['ed']) || isset($_GET['cat']) || isset($_GET['loc']) || isset($_GET['order']) ) {
$where = 'WHERE ';
if (isset($_GET['sd'])) {
if(strlen($where) > 6) {
$where .= " AND ";
}
$where .= "start = " . $_GET['sd'];
}
if (isset($_GET['ed'])) {
if(strlen($where) > 6) {
$where .= " AND ";
}
$where .= "end = " . $_GET['ed'];
}
if (isset($_GET['cat'])) {
if(strlen($where) > 6) {
$where .= " AND ";
}
$where .= "category = " . $_GET['cat'];
}
if (isset($_GET['loc'])) {
if(strlen($where) > 6) {
$where .= " AND ";
}
$where .= "location = " . $_GET['loc'];
}
}

Exiting a foreach loop and retrieve result

I'm working on a little project and I've gone brain dead, so I'm hoping someone here can help me defeat my coders block.
I'm trying to create a page using php that changes its content display depending on what (if any) value is passed to the page (Locations). I have created a safelist array which I've stored the different locations. First I check any value passed against the safe list, if its a match I display one set of content.
If it doesn't match I'm running a similarity test to check if theres maybe a simple typo and can still navigate people to the page I think they wanted but this is where I'm getting stuck.
I'm hoping that someone could type
www.example.co.uk/location.php <---- to load a generic location page
www.example.co.uk/location.php?loc=Bishops-Stortford <---- to load a targeted location page
www.example.co.uk/location.php?loc=Bishop-Stortford <---- to load a targeted location page despite mispelling providing its a 90% or more match
www.example.co.uk/location.php?loc=?php echo "I hacked your site"; ?> ---- hopefully my system will disarm nasty code injection
I'll post my code below so you can see what I've got.
<?php
$loc = "";
$safelist = array("Bishops Stortford", "Braintree", "Chelmsford", "Dunmow", "Harlow", "Hertford", "Saffron Walden", "Sawbridgeworth", "Stansted", "Ware",
"Essex", "Hertfordshire");
if(isset($_GET["loc"])) {
/* Gets the value of loc if set, replaces hyphens with spaces and capitalises first letters of words converting the rest to lowercase. */
$loc = ucwords(strtolower(str_replace("-", " ", $_GET["loc"])));
}
/* Is word in safelist */
if (in_array($loc, $safelist)) {
/* Yes */
if (($loc == "Essex") or ($loc == "Hertfordshire")) {
$county = True;
} else {
$county = False;
}
if ($county == False) {
echo "\"" . $loc . "\" is not a county";
}else{
echo "\"" . $loc . "\" is a county";
}
} else {
/* No, Is string 90% similar to any entry within the safelist? */
foreach ($safelist as $safeword) {
similar_text($safeword, $loc, $percent);
echo $safeword . " " . $loc . " " . $percent . "<br />";
if ($percent >= 90) {
}
}
?>
I can't think what to do for the if ($percent >=90). I know I want to exit the loop and get the result from the first 90% or more match I find but am not 100% sure how to do this.
Also whats the best way to deal with code injection like www.example.co.uk/location.php?loc=?php echo "I hacked your site"; ?>
I think this is what you want:
foreach ($safelist as $safeword) {
similar_text($safeword, $loc, $percent);
echo $safeword . " " . $loc . " " . $percent . "<br />";
if ($percent >= 90) {
$loc = $safeword;
$county = true;
break;
}
}
As long as you don't call eval() on user input, you don't have to worry about them injecting PHP statements. When you echo something, it's sent to the browser, it's not executed again by PHP. However, you should still sanitize the output, because it might contain HTML markup, perhaps even Javascript, which could hijack the user's browser. When displaying output on the page, use htmlentities() to encode it:
echo "Greetings, " . htmlentities($first_name);
To answer the second part of your question, I use htmlentities to output data directly to the screen from input and something like this function on the data before a save to a database:
function escape_value($value)
{
if($this->real_escape_string_exists)
{
if($this->magic_quotes_active){$value = stripslashes($value);}
$value = mysql_real_escape_string($value);
}
else
{
if(!$this->magic_quotes_active){$value = addslashes($value);}
}
return $value;
}
I think I would restructure it, something like this:
$loc = "";
$safelist = array("Bishops Stortford", "Braintree", "Chelmsford", "Dunmow", "Harlow", "Hertford", "Saffron Walden", "Sawbridgeworth", "Stansted", "Ware",
"Essex", "Hertfordshire");
if(isset($_GET["loc"])) {
/* Gets the value of loc if set, replaces hyphens with spaces and capitalises first letters of words converting the rest to lowercase. */
$loc = ucwords(strtolower(str_replace("-", " ", $_GET["loc"])));
}
$good = '';
if (in_array($loc, $safelist)) {
$good = $loc;
} else {
foreach ($safelist as $safeword) {
similar_text($safeword, $loc, $percent);
echo $safeword . " " . $loc . " " . $percent . "<br />";
if ($percent >= 90) {
$good = $safeword;
}
}
}
if ( ! empty($good)){
/* Yes */
if (($good == "Essex") or ($good == "Hertfordshire")) {
$county = True;
} else {
$county = False;
}
if ($county == False) {
echo "\"" . $good . "\" is not a county";
}else{
echo "\"" . $good . "\" is a county";
}
//And whatever else you want to do with the good location...
}
Like Barmar said, since you're not doing anything with the input value except for comparing it to an array, there's no risk of an attack in that way.

I need a character or string which will match ANY value in my table field

I have a form that requires the user to only fill out at least 1 (out of four) fields. They can then submit and get a search result based off of their input.
The problem is, I can't get a character to set my variables to that will match any database value. Here is my code for some context;
if (isset($_POST['buildname']) ||
isset($_POST['weapon']) ||
isset($_POST['category']) ||
isset($_POST['id']))
{
if ($_POST['buildname'] == "")
{
$buildname = ".*";
}
if ($_POST['weapon'] == "")
{
$weapon = ".*";
}
if ($_POST['category'] == "")
{
$category = ".*";
}
if ($_POST['id'] == "")
{
$id = ".*";
}
$buildname = sanitizeString($_POST['buildname']);
$weapon = ($_POST['weapon']);
$category = ($_POST['category']);
$id = ($_POST['id']);
$searchstring = "SELECT buildname,weapon,category,id,author FROM weapons " .
"WHERE buildname='$buildname' AND weapon='$weapon' AND category='$category' AND id='$id'";
As you can see, the code looks at if one of the variables is set, then submits a form. If a variable isn't set, it assigns a character of ".*" (which I thought would match anything). It then queries the database to match any rows. I get no results unless I enter EVERY field with a correct entry.
Any ideas?
Thanks!
I would not use %, instead do something like this
if (isset($_POST['buildname']) || isset($_POST['weapon']) || isset($_POST['category']) || isset($_POST['id'])){
$sqlArray = array();
if(isset($_POST['buildname'])){
$sqlArray[] = "buildname='" . mysqli_real_escape_string($connection,$_POST['buildname']) . "'";
}
if(isset($_POST['weapon'])){
$sqlArray[] = "weapon='" . mysqli_real_escape_string($connection,$_POST['weapon']) . "'";
}
if(isset($_POST['category'])){
$sqlArray[] = "category='" . mysqli_real_escape_string($connection,$_POST['category']) . "'";
}
if(isset($_POST['id'])){
$sqlArray[] = "id='" . mysqli_real_escape_string($connection,$_POST['id']) . "'";
}
$searchstring = "SELECT buildname,weapon,category,id,author FROM weapons " .
"WHERE " . implode(' AND ', $sqlArray);
}
The wildcard character for MySQL is: %
The query you are executing, you "thought would match anything" wont. The statement uses no regular expressions.
WHERE buildname='$buildname' AND weapon='$weapon'
Which is essentially saying you need to have the following fields equal their string value of:
WHERE buildname='.*' AND weapon='.*'
I doubt you have any building with a name of .*.
It would be better to not filter on that field. basically remove the WHERE cause criteria if the variable is not defined.
You can do this dynamically, buliding the SQL statement only when you need to filter by that field.
if (isset($_POST['somevalue']) && ! empty($_POST['somevalue'])) {
$where .= 'column_name = ?';
$values[] = sanitizeString($_POST['somevalue]);
}
I've also used positional parameters which assumes you will be using the PDO or MySQLi libraries for querying.
No, you are using = operator, that only compares 2 values. In your case it will search for '.*' - and fail. If you want to ignore the fields, that were not filled, just don't put them into the query: no need for regexps. So, if the weapon and category are missing, your query should be like this
$searchstring = 'SELECT buildname,weapon,category,id,author FROM weapons WHERE ';
$fields = array('buildname', 'weapon', 'category', 'id');
$data = array();
foreach($fields as $value)
{
if (isset($_POST[$value]) && ($_POST[$value] != "") )
{
$data[] = sanitizeString($_POST[$value]);
}
}
$n = count($data);
if($n > 0)
{
$searchstring .= implode(' AND ', $data);
//do MySQL request and output result
}
Don't overcomplicate simple things. Also your code is vulnerable to SQL injection as some fields are not escaped.
You can do it like this:
$fields = array('buildname', 'weapon', 'category', 'id');
$sql = 'SELECT buildname, weapon, category, id, author FROM weapons';
$prefix = ' WHERE ';
foreach ($fields as $field) {
if (isset($_POST[$field]) && strlen($_POST[$field])>1) {
$sql .= $prefix . $field . '=\''
. sanitizeString($_POST[$field]) . '\'';
$prefix = ' AND ';
}
}
if ($prefix == ' AND ') {
// send the query
}
Notice: if you want to perform search with incomplete values, you could use LIKE instead of =, example:
$sql .= $prefix . $field . ' LIKE \'%' . sanitizeString($_POST[$field]) . '%\'';
But keep in mind that LIKE is slower than =

way to check for if sql row exists?

my problem is this
i am fetching a mysql row via this
$sql_istorrenthere = $this->query_silent("SELECT media_type
FROM " . DB_PREFIX . "auction_media WHERE
auction_id='" . $item_details['auction_id'] . "'");
$row = mysql_fetch_array($sql_istorrenthere);
and then calling it with this
if ($row['media_type'] == 4)
{
$display_output = GMSG_TORRENT;}
else
{
$display_output = GMSG_NOTORRENT;
}
}
however, media_type has multiple values, (1,2,3,4)
how to write it so that it checks if 4 exists? because now i believe it is checking if media_type equals 4 and that is false, which is giving me the wrong display_output
You can use mysql_num_rows to determine if any rows were returned, and this works by adding a search condition in your query adding " AND media_type = 4" to the end
if(mysql_num_rows($sql_istorrenthere)) {
} else {
}
// You can loop through records by doing the following, this prints out every media type :)
while ($row = mysql_fetch_array($sql_istorrenthere)) {
echo $row['media_type'] . '<br />';
}
You can just add on "AND media_type = '4'" to your query. But you really should use paramaterized queries.
Once your query has "AND media_type = '4'" you can check RowCount.
There are probably better ways, but here's one idea.
$media_type_ids = explode(',', $row['media_type']);
if (array_search(4, $media_type_ids) !== FALSE) {
// found
}
It could be possible to even do this in-situ in the database query ... potentially.
// Comment the next line after you get what it is
#print ("Value of media type is: >>>".$row['media_type']."<<<"); // Line to be commented
if (isset($row['media_type']) && $row['media_type'] == 4) {
$display_output = GMSG_TORRENT;
}
else {
$display_output = GMSG_NOTORRENT;
}
To fetch all media types:
<?php
$sql_istorrenthere = $this->query_silent("SELECT media_type FROM " . DB_PREFIX . "auction_media");
while ($row = mysql_fetch_array($sql_istorrenthere)) {
// Comment the next line after you get what it is
#print ("Value of media type is: >>>".$row['media_type']."<<<"); // Line to be commented
if (isset($row['media_type']) && $row['media_type'] == 4) {
$display_output = GMSG_TORRENT;
}
else {
$display_output = GMSG_NOTORRENT;
}
}

Categories