I'm very new to php/SQL (1 day) so forgive me if I am doing this in a backwards way.
This php below is intended to return the 100 most recent entries into the DB. I attempt to do it by selecting 100 entries to be returned and sort by the date(time stamp) the entry was made. Will this return the 100 more recent entries to the DB? Or am I understanding this wrong?
$type = isset($_GET['type']) ? $_GET['type'] : "global";
$offset = isset($_GET['offset']) ? $_GET['offset'] : "0";
$count = isset($_GET['count']) ? $_GET['count'] : "100";
$sort = isset($_GET['sort']) ? $_GET['sort'] : "date DESC";
// Localize the GET variables
$udid = isset($_GET['udid']) ? $_GET['udid'] : "";
$name = isset($_GET['name']) ? $_GET['name'] : "";
// Protect against sql injections
$type = mysql_real_escape_string($type);
$offset = mysql_real_escape_string($offset);
$count = mysql_real_escape_string($count);
$sort = mysql_real_escape_string($sort);
$udid = mysql_real_escape_string($udid);
$name = mysql_real_escape_string($name);
// Build the sql query
$sql = "SELECT * FROM $table WHERE ";
switch($type) {
case "global":
$sql .= "1 ";
break;
case "device":
$sql .= "udid = '$udid' ";
break;
case "name":
$sql .= "name = '$name' ";
break;
}
$sql .= "ORDER BY $sort ";
$sql .= "LIMIT $offset,$count ";
$result = mysql_query($sql,$conn);
if(!$result) {
die("Error retrieving scores " . mysql_error());
}
//echo $result;
$rows = array();
while($row = mysql_fetch_assoc($result)) {
$rows[] = $row;
}
This should work, although date is a MySQL keyword, so you would either need to enclose date in backquotes or rename that column.
Also, definitely make sure you've sanitized those inputs before building your query. Building a query off of user-editable values from $_GET or $_POST with no sanitation is very unsafe.
For WHERE parameters, you should be running mysql_real_escape_string() on those (which I see you are, I'm not sure if you were before or not). That's enough because you're wrapping those values in quotes in your query, and since you're escaping that string, any attempt to break out of those quotes won't work.
For the stuff like the ORDER BY you have, I would define a valid "list" of allowed values and check to make sure your parameter is in that list. For example:
$valid_orderbys = array('`date` DESC', '`date` ASC', '`name` DESC', '`name` ASC');
if (in_array($_GET['sort'], $valid_orderbys))
{
// you're good, you can use this value
}
else
{
// unexpected value, either alert the user or
// use a default value you define
}
Or for LIMIT, you could use PHP's built-in is_numeric() to verify that the value you're being given is a number, not a crafted string.
It's not enough to simply escape the $table, ORDER BY and LIMIT parameters because they're not wrapped in quotes and therefore someone can just maliciously inject a value of ; DROP TABLE whatever; --. This ends up making your query something like:
SELECT * FROM ; DROP TABLE whatever; --WHERE ...
Queries are separated by semicolons, so there are three queries here. The first fails because it's invalid, the second succeeds in dropping the table, and the third is just a comment so nothing happens. But you can see, if you let users throw whatever they want as one of those parameters, it's a wide open security hole. (I'm not sure if enclosing the table name in backquotes helps this, someone let me know if you know. But in any case, you can do the same attack on the LIMIT and ORDER BY parameters.)
If you have a unique, auto-increment ID for each record (as you should), it would be more efficient to ORDER BY id DESC.
Actually, you already got it right. You should only look into specifying the columns instead of using *.
Related
Trying to create a dynamic search functionality.
Goal : allowing user to search by email (if not empty), if empty (by last name), if both are not empty, than by both, etc.
I know I can write if statement depicting every scenario and than insert SQL command based on that, question is can this be handled in a more simplified manner. Thanks for your help.
Current function set up does OR across all fields, values are coming from $_POST:
find_transaction($email,$last_name,$first_name, $transaction_id)
{
GLOBAL $connection;
$query = "SELECT * ";
$query .= "FROM transactions WHERE ";
$query .= "email='{$email}' ";
$query .= "OR last_name='{$last_name}' ";
$query .= "OR first_name='{$first_name}' ";
$query .= "OR transaction_id='{$transaction_id}' ";
$query .= "ORDER BY date DESC";
$email = mysqli_query($connection,$query);
confirm_query($email);
return $email;
}
I do this all the time, it's not too much work. Basically build your WHERE statement dynamically based off your POST variables, using a series of if statements.
For example:
$where_statement = "";
// First variable so is simpler check.
if($email != ""){
$where_statement = "WHERE email = '{$email}'";
}
// Remaining variables also check if '$where_statement' has anything in it yet.
if($last_name != ""){
if($where_statement == ""){
$where_statement = "WHERE last_name = '{$last_name}'";
}else{
$where_statement .= " OR last_name = '{$last_name}'";
}
}
// Repeat previous 'last_name' check for each remain variable.
SQL statement would change to:
$query = "SELECT * FROM transactions
$where_statement
ORDER BY date DESC";
Now, the SQL will only contain filters depending on what values are present, so someone puts in just email, it would generate:
$query = "SELECT * FROM transactions
WHERE email = 'smith#email.com'
ORDER BY date DESC";
If they put in just last name, it would generate:
$query = "SELECT * FROM transactions
WHERE last_name = 'Smith'
ORDER BY date DESC";
If they put both, would generate:
$query = "SELECT * FROM transactions
WHERE email = 'email#email.com' OR last_name = 'Smith'
ORDER BY date DESC";
Etc., etc.
You could add as many variables you wish here, and basically if the specific variable is not blank, it will add it to the "$where_statement", and depending on if there is anything in the "$where_statement" yet or not, it will decide to start with = "WHERE ", or append .= " OR" (notice the '.=' and the space before 'OR'.
Better use Data Interactive table : http://datatables.net/
It's useful and no SQL-injection :) Good luck !
I have a php code with a query:
$query = "SELECT * FROM TDdb WHERE status = $status AND occupation =$occupation";
I am sending the values status and occupation with a client application to this php code.
This works when I send both status and occupation. But I want it to return rows if I just send status but not occupation also ( I mean no matter what the occupation is).
does anyone have any suggestions?
I would appreciate any help.
PS: I want to do it without if statement and just but changing the query
Personally I would create a base query and append conditions wherever you have them, like so:
$sql = 'SELECT * FROM TDdb';
$conditions = array();
$args = array();
if ($action) {
$conditions[] = 'status = :status';
$args[':status'] = $status;
}
if ($occupation) {
$conditions[] = 'occupation = :occupation';
$args[':occupation'] = $occupation;
}
if ($conditions) {
$sql .= ' WHERE ' . join(' AND ', $conditions);
}
$stmt = $db->prepare($sql);
$stmt->execute($args);
Looks like you've got a few good options for how to do it in SQL, or how to make the SQL string variable in PHP.
One reason to consider using an 'if' in the PHP code for the database access performance.
When you introduce an 'or' condition like that in SQL, you're not going to get index access. It is much harder for the database to determine what path it should take than for the PHP code because the SQL engine optimizes the query without knowing what the variable will resolve to at execution.
You already know in the PHP which version of the query you really want. This will perform better if you make that choice there.
This will work if you pass an occupation or a NULL value.
SELECT *
FROM TDdb
WHERE status = $status
AND ($occupation IS NULL OR occupation = $occupation)
"SELECT * FROM TDdb WHERE status = '$status' AND (occupation = '$occupation' OR occupation IS NULL)";
Apart from the solution provided by #Tom and #Damien Legros, you may create two query strings one with occupation and one without occupation. Something like:
$query = "SELECT * FROM TDdb WHERE status = $status";
if ($occupation != "") {
/*When you have value for occupation*/
$query .= " AND occupation =$occupation";
}
So in this case, data will be returned if you have only the status field. Secondly, please check if the status and occupation fields in table are varchar then you have to enclose them in single quotes (').
Thanks everyone for help. specially jack.
finally i created my query like this:
$query = 'SELECT * FROM TDdb';
if ($status) {
$query = $query." WHERE status = '".$status."'";
}
if ($occupation) {
$query = $query." AND occupation = '".$occupation."'";
}
I'm updating a MySQL table with posted PHP data.
I first gather the posted data, and put them in appropriate variables. Together with the necessary if/else checks.
Then, I only have to write my query once.
But now I have an if/else to check wether to update a specific field or not. How can I store a "do-not-update" value inside the corresponding variable?
Because otherwise I have to put an if/else check around the whole query, just for one field.
I just want to be as efficient as possible. :)
My query is as follows:
$updateTable = mysql_query("UPDATE myTable SET field1 = '$field1', field2 = '$field2'");
wherever you are get $_POST into variables, do this,
if( $field2 === 'xyz' ) { //if value is 'xyz', do not update
$sql = '';
} else
$sql = ", field2 = '$field2'";
Then in the query,
$updateTable = mysql_query("UPDATE myTable SET field1 = '$field1' $sql");
Edit: if using 1/0 (true or false),
if( $field2 == true ) { //if value is true, do not update
$sql = '';
} else
$sql = ", field2 = '$field2'";
You will need to build up your query, storing it in a PHP string, for example:
$sql = "UPDATE `table` SET ";
if ($_POST['foo']!=='') {
$sql .= " `foo`='".mysql_real_escape_string($_POST['foo'])."',";
}
if ($_POST['bar']!=='') {
$sql .= " `bar`='".mysql_real_escape_string($_POST['bar'])."',";
}
$sql = rtrim($sql,',');
$sql .= " WHERE `id`='".mysql_real_escape_string($_POST['id'])."'"
Then execute your string as the query.
If you are asking whether the field should be updated, you can do one of two things:
1) Specify a criteria that ensures field1 and field2 are only updated if the rows match the criteria. If the criteria does not match, the record will not be updated. This is the most common way.
UPDATE myTable ...
WHERE criteria1 = 1 AND criteria2 = 'Red'
2) Run a query before the UPDATE to see whether to perform an update.
I'm not exactly sure what you are asking for, but perhaps this answers your question:
$updateTable = mysql_query("
UPDATE myTable SET
field1 = IF('$field1'>'','$field1', field1),
field2 = IF('$field2'>'','$field2', field2)
");
Of course, you are opening yourself up to SQL injection with the code, as written.
Lets assume you have gathered the fields to update in an array $fields like this :
array (
'filed1' => 'value' ,
'field2' => ''value
)
Now you need to generate the query, you can do this by looping in the array:
$sql = "UPDATE mytable ";
$sql .= $fields ? "SET " : "" ;
foreach ($fields as $key=>$value) {
$sql.= $value ? "$key = '$value' , " : '' ;
}
//you need to omit the trailing ','
$sql[strlen($sql) -1 ] = "";
Tips :
Do sanitize all user input using mysqli_real_escape_string or something better than that.
Happy coding :)
I'm currently coding a simple search script in PHP that requires three variables from the user namely:
$Capacity, $Location and $RoomType
Capacity is a required field which the jquery validate plugin checks for numerical entry on input - but Location and RoomType are optional.
I'm trying to now draft a SQL query that will search the table rooms.
There are three columns in the table also called Capacity, Location and RoomType that I want to search using the variables.
How would I write this SQL query? Especially with $Capacity being required, $Location / $RoomType expected to be left blank or filled in at the users discretion?
You could use LIKE ...% in your sql query, so that even when blank, it'll be treated as a wildcard.
$q = 'SELECT * FROM foo WHERE capacity = "'.$capacity.'" AND location LIKE "'.$location.'%" AND roomtype LIKE "'.$roomtype.'%"';
Of course, remember to escape the inputs.
Something like this should work:
function BuildSearchQuery($Capacity, $Location, $RoomType)
{
$where = array();
if (!empty($Capacity))
{
$where[] = "Capacity = '" . mysql_real_escape_string($Capacity) . "'";
}
if (!empty($Location))
{
$where[] = "Location = '" . mysql_real_escape_string($Location) . "'";
}
if (!empty($RoomType))
{
$where[] = "RoomType = '" . mysql_real_escape_string($RoomType) . "'";
}
if (empty($where))
{
return false;
}
$sql = "select * from `table` where ";
$sql += implode(" AND ", $where);
return $sql;
}
Although nowadays many frameworks exists that allow you to do this more easily and less error-prone than manually crafting queries.
$query =select * from table where Capacity =$Capacity
if(isset($Location) && $Location!='') $query.= and Location LIKE '%$location%'
if(isset($RoomType) && $RoomType!='') $query.= and RoomType LIKE '%$RoomType%'
Making use of LIKE or = operator in query is upto you.
Depend on how complex it is (and or not ???)
But basically
Select ... From Rooms Where Capacity = #Capacity
and ((Location = #Location) Or (IsNull(#Location,'') = ''))
and ((RoomType = #RoomType) or (IsNull(#RoomType,'') = ''))
Or some such.
If you aren't using parameterised queryies then replace #.... with the escaped inputs.
I want to make an category-system CMS. Everything is fine, except a big trouble.
How can I can handle and generate the mysql query depends by some inputs like:
site.com/some-category&sortby=views&from=smt&anotherInput=key
For example, for this input my query should be something like
SELECT * FROM `articles` WHERE from='smt' AND afield='key' ORDER BY VIEWS
But these inputs will be different. How I can write this code? I don't know much things about designs patterns, but, I've heard about Factory pattern, is this a part of my solution?
Than
Factory pattern can help you with e.g. connecting/quering various databases without need to rewrite the entire code. This has nothing to do about query itself.
You can look at PDO extension, I usually use it together with prepared statements.
It will let you write queries like this:
$prepare = $db->prepare('
SELECT
*
FROM
articles
WHERE
from=:from AND afield=:afield
ORDER BY
views
');
$prepare->bindValue(':from', $_GET['from'], PDO::PARAM_STR);
$prepare->bindValue(':afield', $_GET['afield'], PDO::PARAM_STR);
$prepare->execute();
return $prepare;
The good thing about it is that you don't need to protect this from sql injections as PDO makes it for you. Also, the query is cached and you can run it several times with different params.
Very bad practice to use raw GET params in query directly, i.e. you shouldn't make constructions like
SELECT * FROM articles WHERE from=$_GET['from'] AND afield='key' ORDER BY VIEWS
but instead something like
if ($_GET['from'] == 'smt') $from = 'smt'
SELECT * FROM articles WHERE from='$from' AND afield='key' ORDER BY VIEWS
and so on
P.S. keyword is 'sql injection'
You can build the query string as pieces depending on what you need:
$query = "SELECT * FROM `articles` WHERE 1 = 1";
$where = ''
if (isset($_GET['from'])) {
$where .= " AND `from` = '" . mysql_real_escape_string($_GET['from']) . "'"
}
if (isset($_GET['anotherInput'])) {
$where .= " AND `from` = '" . mysql_real_escape_string($_GET['anotherInput']) . "'"
}
if (isset($_GET['sortby'] == 'views') {
$orderby = " ORDER BY `views` DESC"
} else {
$orderby = " ORDER BY `id` DESC"
}
$query = $query . $where . $orderby;
$result = mysql_query($query);
This is sort of the straight PHP/MySQL way, but I actually do suggest that you use prepared statements as in Pavel Dubinin's answer.
This has nothing to do with patterns. Use the $_GET superglobal variable to dynamically generate your query string.
$query = "SELECT * FROM articles WHERE from='".
$_GET['from'].
"' AND afield='".
$_GET['anotherInput'].
"' ORDER BY ".
$_GET['sortby'];
Disclaimer: This is prone to SQL injection. Use input escaping and prepared statements, ex PDO, in a production environment like:
$query = "SELECT * FROM articles WHERE from='?' AND afield='?' ORDER BY ?";
$clean_from = htmlentities($_POST['from'], ENT_QUOTES, 'UTF-8');
$clean_anotherInput = htmlentities($_POST['anotherInput'], ENT_QUOTES, 'UTF-8');
$clean_sortby = htmlentities($_POST['sortby'], ENT_QUOTES, 'UTF-8');
$clean_inputs = array($clean_from, $clean_anotherInput, $clean_sortby);
$sth = $dbh->prepare($query);
$sth->execute($clean_inputs);