Array from Form Input - Select Statement MySQLi Parameterisation - php

Turning phrases entered in a Form input into an array to pass into a MySQL select statement where clause using MySQLi. The php code I have achieves this, but I can't workout how to parameterise the query to prevent against sql injection attacks. I've had a look at a few questions on this site, but I'm struggling to relate it to my code.
if(!empty($_POST['Message']))
{
$searchStr = get_post($con,'Message');
$aKeyword = explode(" ", $searchStr);
$query ="SELECT m.ID, m.MessageText FROM MessageMain m LEFT OUTER JOIN Likes l on m.ID = l.PostID WHERE MessageText LIKE '%" . $aKeyword[0] . "%'";
for($i = 1; $i < count($aKeyword); $i++) {
if(!empty($aKeyword[$i])) {
$query .= " OR MessageText like '%" . $aKeyword[$i] . "%'";
}
}
$query .= " GROUP BY m.ID, m.MessageText ORDER BY count(m.id) desc";
$result = $con->query($query);
$rowcount=mysqli_num_rows($result);

If you would like to build the WHERE clause dynamically based on the number of keywords to match you could do it like this:
if (!empty($_POST['Message'])) {
$searchStr = get_post($con, 'Message');
$aKeyword = explode(" ", $searchStr);
$whereClauseArr = [];
foreach ($aKeyword as $keyword) {
if ($keyword) {
$whereClauseArr[] = "MessageText LIKE ?";
$whereValues[] = '%'.$keyword.'%';
}
}
$stmt = $con->prepare(
'SELECT m.ID, m.MessageText
FROM MessageMain m
LEFT OUTER JOIN Likes l on m.ID = l.PostID
WHERE '.implode(' OR ', $whereClauseArr).'
GROUP BY m.ID, m.MessageText ORDER BY count(m.id) desc'
);
$stmt->bind_param(str_repeat('s', count($whereValues)), ...$whereValues);
$stmt->execute();
$result = $stmt->get_result();
}
Although in your case, checking the same column against multiple values would probably be better done with regular expression. This would make your query simpler and potentially also faster depending on the number of keywords you have.
if (!empty($_POST['Message'])) {
$searchStr = get_post($con, 'Message');
$aKeyword = explode(" ", $searchStr);
$aKeyword = array_filter($aKeyword); // Remove empty values
$stmt = $con->prepare(
'SELECT m.ID, m.MessageText
FROM MessageMain m
LEFT OUTER JOIN Likes l on m.ID = l.PostID
WHERE MessageText REGEXP ?
GROUP BY m.ID, m.MessageText ORDER BY count(m.id) desc'
);
$regexString = implode('|', $aKeyword);
$stmt->bind_param('s', $regexString);
$stmt->execute();
$result = $stmt->get_result();
}

Related

SQL in a prepared statement with inner join SQL statement

my query is working but I don't know how to put it in a prepared statement
This is my query(working)
SELECT P.first_name, P.middle_name, P.last_name, P.lrn, P.section
FROM students P INNER JOIN student_load C ON P.lrn = C.lrn
WHERE subject_code = 12332654
This is my prepared statement(I'm new to the prepared statement so I don't know much)
function get_total_all_records()
{
include('../config.php');
//IM NOT SURE IF THE PROBLEM IS HERE($statement)
$statement = $connection->prepare("SELECT P.first_name, P.middle_name,
P.last_name, P.lrn, P.section
FROM students P
INNER JOIN student_load C ON P.lrn = C.lrn");
$statement->execute();
$result = $statement->fetchAll();
return $statement->rowCount();
}
$query = '';
$output = array();
// OR IF THE PROBLE IS HERE($query)
$query = "SELECT P.first_name, P.middle_name, P.last_name, P.lrn, P.section
FROM students P INNER JOIN student_load C ON P.lrn = C.lrn
WHERE subject_code = 12332654";
if(isset($_POST["search"]["value"])){
$query .= 'AND ( lrn LIKE "%'.$_POST["search"]["value"].'%"
OR teacher LIKE "%'.$_POST["search"]["value"].'%"
OR sem LIKE "%'.$_POST["search"]["value"].'%" )';
}
One of the points of preparing a query is that you can use parameters for the parts you want to replace with data. This helps to protect you from SQL Injection Attack which concatenating variables into the string does not.
As your first query has no need of parameters it could quite happily be run using a simple ->query() but the second would be best run using a prepare and execute
$params = [':subjcode'=>$subject_code];
$query = "SELECT P.first_name, P.middle_name, P.last_name, P.lrn, P.section
FROM students P INNER JOIN student_load C ON P.lrn = C.lrn
WHERE subject_code = :subjcode ";
if(isset($_POST["search"]["value"])){
$query .= 'AND ( lrn LIKE :s1
OR teacher LIKE :s2
OR sem LIKE :s3 )';
$params = array_merge($params,
[':s1'=> '%' . $_POST["search"]["value"] . '%'],
[':s2'=> '%' . $_POST["search"]["value"] . '%'],
[':s3'=> '%' . $_POST["search"]["value"] . '%']
);
}
$stmt = $connection->prepare($query);
$stmt->execute($params);
Note I also added a space after :subjcode "; which was missing in your original query

SQL Statement: Getting results based on 2 values

I've got a pretty complex SQL statement and want to add a WHERE clause that only selects results WHERE the hidden column IS NOT '1', however it needs to relate to a specific user.
i.e If in table 1 hidden is 1 and userid is 1 I don't want to get this results. However as there is no record for user 2 in that table I want them to see it.
This is what I have managed to get working so far:
$where .= " AND uh.hidden IS NULL ";
However if I login as User 2 then I see the same results as user 1.
How do I make it so results are shown based on the user too?
SQL query:
$pdo = new PDO('mysql:host=localhost;dbname=myDB', 'root', 'root');
$select = 'SELECT tl.id,
tl.name,
tl.locale,
ROUND(AVG(pr.rating),0) AS rating ';
$from = ' FROM theList AS tl ';
$join = ' LEFT JOIN post_rating AS pr ON tl.id = pr.postid ';
$join2 = ' LEFT JOIN user_hidden_list AS uh ON uh.est_id = tl.id ';
$opts = isset($_POST['filterOpts']) ? $_POST['filterOpts'] : [''];
$where = ' WHERE 1 = 1 ';
if (in_array("pub", $opts)) {
$where .= " AND pub = 1";
}
if (in_array("bar", $opts)) {
$where .= " AND bar = 1";
}
$where = ' WHERE uh.hidden IS NULL ';
$group = ' GROUP BY tl.id, tl.name ';
$sql = $select . $from . $join . $join2 . $where . $group;
$statement = $pdo->prepare($sql);
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
$json = json_encode($results);
echo($json);

Codeigniter create a prepared statement or use active record for search with concat

I was wondering how I can prevent sql injections with this search query of mine? Unfortunately I had to use the string due to me needing to concatenate the first and last names to search. I have tried prepared statements but they don't seem to work here too. I hope someone can help me with this. Thank you.
My function
public function admin_search($input,$limit,$start){
$sql = "SELECT * FROM agent_accounts as aa LEFT JOIN person as p ON aa.person_id = p.id "
. "WHERE CONCAT_WS('', p.first_name, p.last_name) LIKE '%$input%' "
. "OR p.email LIKE '%$input%' OR p.phone_number LIKE '%$input%' "
. "OR aa.account_number LIKE '%$input%' LIMIT $limit OFFSET $start";
$query = $this->db->query($sql);
if($query->num_rows()>0){
foreach($query->result()as $row){
$documents[] = $row;
}
return $documents;
}else{
return false;
}
}
Codeigniter Active records automatically escape queries to prevent from SQL injection
Like this
$query = $this->db->get_where('table_name', array('id' => $id), $limit, $offset);
But if you use query siblings
Try this :
$sql = "SELECT * FROM agent_accounts as aa LEFT JOIN person as p ON aa.person_id = p.id "
. "WHERE CONCAT_WS('', p.first_name, p.last_name) LIKE ? "
. "OR p.email LIKE ? OR p.phone_number LIKE ? "
. "OR aa.account_number LIKE ? LIMIT ? OFFSET ?";
$query = $this->db->query($sql,array('%{$input}%','%{$input}%','%{$input}%','%{$input}%',$limit,$start));
The question marks in the query are automatically replaced with the values from array.

PDO Prepared Statement - Different behavior inside 2 similar methods

I would like someone to explain me why the first method works only after quoting the placeholder ':cat_id' in the WHERE clause, and requires the $this->db->query($query);, otherwise it throws fatal error:
"SQLSTATE[HY093]: Invalid parameter number: number of bound variables
does not match number of tokens in..."
while the second method doesn't need neither quoting and the $this->db->query() method?
public function getAllPosts($cat_id = null)
{
// Query build
$query = "SELECT posts.*, users.username, categories.title FROM posts "
. "INNER JOIN users "
. "ON posts.user_id = users.id "
. "INNER JOIN categories "
. "ON posts.category_id = categories.id ";
// Filter
if (!is_null($cat_id))
{
$query .= "WHERE category_id = ':cat_id' ";
// Order query
$query .= "ORDER BY create_date DESC";
$this->db->bind(':cat_id', $cat_id);
}
else
{
$query .= "ORDER BY create_date DESC";
}
$this->db->query($query);
// Run the query
// Assign Result Set
$results = $this->db->resultset();
return $results;
}
public function getCategoryPosts($cat_id)
{
$query = "SELECT posts.*, users.username, categories.title FROM posts "
. "INNER JOIN users "
. "ON posts.user_id = users.id "
. "INNER JOIN categories "
. "ON posts.category_id = categories.id "
. "WHERE posts.category_id = :cat_id "
. "ORDER BY create_date DESC";
$this->db->bind(':cat_id', $cat_id);
$results = $this->db->resultset();
return $results;
}
Update:
Here are a echo output of the query from the first method:
1. with quoted:
SELECT posts.*, users.username, categories.title FROM posts
INNER JOIN users ON posts.user_id = users.id
INNER JOIN categories ON posts.category_id = categories.id
WHERE category_id = ':cat_id' ORDER BY create_date DESC
2. unquoted:
SELECT posts.*, users.username, categories.title FROM posts
INNER JOIN users ON posts.user_id = users.id
INNER JOIN categories ON posts.category_id = categories.id
WHERE category_id = :cat_id ORDER BY create_date DESC
You are missing a trailing space on the following string
$query .= "WHERE category_id = ':cat_id'";
So you are concatenating it into:
WHERE category_id = ':cat_id'ORDER BY create_date DESC
If you remove the ', you would get:
WHERE category_id = :cat_idORDER BY create_date DESC
So PDO expects a bound value for :cat_idORDER
Also in getCategoryPosts() you create a query but never actually use it.

This Dynamic Query code, where is the problem(php)?

I have this codes for Dynamic Query on a search box but i don't have any result for a search word(this word must return several records) while i have output for string($q),appreciate that help me for finding the problem.
<?php
error_reporting(E_ALL);
//include("../mahal/mahal.php");
include("../mahal/mahalmag.php");
$textfieldstring = mysql_real_escape_string($_REQUEST['Textfield1']);
$connmag = mysql_connect($dbhostmag, $dbusermag, $dbpassmag) or die ('Error connecting to Database,this is maybe due to heavy traffic,please refresh your page.');
$dbname = 'magazine';
mysql_select_db("$dbname");
mysql_query('SET NAMES utf8',$connmag); //for utf charachter
$q = strip_tags(trim($textfieldstring));
if($q) {
$search_words = explode(' ', strtolower($q));
for($i = 0; $i < count($search_words); $i++) {
if($i) {
$str_search .= "OR title LIKE '%".mysql_real_escape_string($search_words[$i])."%' OR articlebody LIKE '%".mysql_real_escape_string($search_words[$i])."%' ";
} else {
$str_search = "OR title LIKE '%".mysql_real_escape_string($search_words[$i])."%' OR articlebody LIKE '%".mysql_real_escape_string($search_words[$i])."%' ";
}
}
}
$search_data = "WHERE (".$str_search.")";
$query_search = "SELECT a.articleid, a.title, a.state, SUBSTR(a.articlebody,1,300) AS description1,
p.photofilename, a.deletitem, ac.catid,c.parentid,a.articlebody
FROM tbarticles a
INNER JOIN tbphotos p ON (p.articleid = a.articleid)
INNER JOIN tbarticlecategories ac ON (ac.articleid = a.articleid)
INNER JOIN tbcategories c ON (c.catid = ac.catid)
$search_data AND a.deletitem = '1'";
$result_search = mysql_query($query_search);
$num_rows = mysql_num_rows($result_search);
$query_row = mysql_fetch_assoc($result_search);
echo "$num_rows";
echo "$q";
?>
Update: the extra "OR" has been removed from the case where $i is 0.
When the search term is (for example) "cold", the generated SQL query is:
SELECT a.articleid, a.title, a.state, SUBSTR(a.articlebody,1,100) AS description1,
p.photofilename, a.deletitem, ac.catid,c.parentid,a.articlebody
FROM tbarticles a
INNER JOIN tbphotos p ON (p.articleid = a.articleid)
INNER JOIN tbarticlecategories ac ON (ac.articleid = a.articleid)
INNER JOIN tbcategories c ON (c.catid = ac.catid)
WHERE ( title LIKE '%cold%' OR articlebody LIKE '%cold%' )
AND a.deletitem = '1'
With a search term of "cold play", the query is:
SELECT a.articleid, a.title, a.state, SUBSTR(a.articlebody,1,100) AS description1,
p.photofilename, a.deletitem, ac.catid,c.parentid,a.articlebody
FROM tbarticles a
INNER JOIN tbphotos p ON (p.articleid = a.articleid)
INNER JOIN tbarticlecategories ac ON (ac.articleid = a.articleid)
INNER JOIN tbcategories c ON (c.catid = ac.catid)
WHERE ( title LIKE '%cold%' OR articlebody LIKE '%cold%'
OR title LIKE '%play%' OR articlebody LIKE '%play%' )
AND a.deletitem = '1' but lot of records are not related to search word
You forgot to strip the first OR from $str_search
$search_data = "WHERE (".$str_search.")";
The code should have been
$str_search = '';
for($i = 0; $i < count($search_words); $i++) {
if($i) { $str_search .= " OR "; }
$str_search .= " title LIKE '%".mysql_real_escape_string($search_words[$i])."%' OR articlebody LIKE '%".mysql_real_escape_string($search_words[$i])."%' ";
}
To make it match multiple $search_words where all words must exist in a single articlebody or a single title, then
$str_search_article = '';
$str_search_title = '';
for($i = 0; $i < count($search_words); $i++) {
if($i) {
$str_search_article .= " AND ";
$str_search_title .= " AND ";
}
$search_word = "'%".mysql_real_escape_string($search_words[$i])."%'";
$str_search_title .= " title LIKE " . $search_word;
$str_search_article .= " articlebody LIKE " . $search_word;
}
$search_data = "WHERE ((".$str_search_title.") OR (".$str_search_article."))";

Categories