I am carrying out a little experiment, appending the string JERRY to every sql keyword in the sql statement by checking from an array of SQL keywords which I specify. I want to remove the string JERRY from the search variable, so that if I typed a' UNION (SELECT 1, fname, username, password FROM users);-- in the search input field, the printed sql statement should look like this;
SELECTJERRY * FROMJERRY shopping WHEREJERRY title LIKEJERRY '%a' UNION (SELECT 1, fname, username, password FROM users);-- %'
The goal is that I don't want the SQL Keywords in the input search variable to have the string JERRY.
BUT right now, this is what I get;
SELECTJERRY * FROMJERRY shopping WHEREJERRY title LIKEJERRY '%a' UNIONJERRY (SELECT 1, fname, username, password FROMJERRY users);-- %'
How can i achieve this?
$search = $_GET['search'];
if (empty($search)) {
echo "Please fill in the search bar";
exit();
}
$keywords = ["SELECT", "FROM", "WHERE", "LIKE", "AND", "OR", "ON","UNION", "JOIN"];
$sql = "SELECT * FROM shopping WHERE title LIKE '%$search%'";
$splittedSql = explode(" ", $sql);
foreach ($splittedSql as $sl) {
if (in_array($sl, $keywords)) {
$newstatement = $sl . "JERRY" . ' ';
} else {
$newstatement = $sl . ' ';
}
echo $newstatement;
}
The issue is that you're checking your static query with a user supplied $search value included in it. Achieving your desired results would require a limit on the keyword replacements.
One approach would be to first check the user supplied $search value for the specified keywords, If it exists, alter your static query. Then you can apply the user supplied $search value after-the-fact, which can be accomplished easily using sprintf.
Instead of exploding the query, you can use preg_replace to apply the keyword values all at once, using a capture group () and replacement value of $1JERRY.
You can use word boundaries \b on the pattern to avoid false positives on words like sAND, tON,lORe, etc.
Lastly using stripos to check the $search value as opposed to in_array() and the /i regex modifier, will allow the $keyword matching and replacements to be case-insensitive.
Approach 1: https://3v4l.org/ie2Mj
$search = 'a\' UNION (SELECT 1, fname, username, password FROM users);--';
$keywords = ["SELECT", "FROM", "WHERE", "LIKE", "AND", "OR", "ON","UNION", "JOIN"];
//sprintf requires textual percent signs to be escaped as %%
$query = 'SELECT * FROM shopping WHERE title LIKE \'%%%s%%\'';
foreach ($keywords as $w) {
if (false !== stripos($search, $w)) {
//found a keyword build the replacement capture groups.
$patterns = '/\b(' . implode('|', $keywords) . ')\b/i';
$query = preg_replace($patterns, '$1JERRY', $query);
break;
}
}
printf($query, $search);
An alternative to iterating over the $keywords would be to use preg_match to determine if the $search value contains a keyword value.
Approach 2: https://3v4l.org/iVbBc
$search = 'a\' UNION (SELECT 1, fname, username, password FROM users);--';
$keywords = ["SELECT", "FROM", "WHERE", "LIKE", "AND", "OR", "ON","UNION", "JOIN"];
$patterns = '/\b(' . implode('|', $keywords) . ')\b/i';
$query = 'SELECT * FROM shopping WHERE title LIKE \'%%%s%%\'';
if (preg_match($patterns, $search)) {
$query = preg_replace($patterns, '$1JERRY', $query);
}
printf($query, $search);
Results for both approaches:
SELECTJERRY * FROMJERRY shopping WHEREJERRY title LIKEJERRY '%a' UNION (SELECT 1, fname, username, password FROM users);--%'
Since $search will be affected by explode , using whitespace, we can prevent this by replacing whitespaces with unique characters:
$search = str_replace(" ","uniquecharacters",$search);
and then replace those unique characters back with white space/s
$keywords = ["SELECT", "FROM", "WHERE", "LIKE", "AND", "OR", "ON","UNION", "JOIN"];
$search = str_replace(" ","uniquecharacters",$search);
$sql = "SELECT * FROM shopping WHERE title LIKE '%$search%'";
$splittedSql = explode(" ", $sql);
foreach ($splittedSql as $sl) {
if (in_array($sl, $keywords)) {
$newstatement = $sl . "JERRY" . ' ';
} else {
$newstatement = str_replace("uniquecharacters"," ",$sl);
$newstatement = $sl . ' ';
}
echo $newstatement;
}
Related
My search SQL currently retrieves submitted text and up to 17 similar matches.
I also need the same form input to take in multiple words(separated by spaces) and retrieve their corresponding data by exact match and none similar.
this is what I have now -
$word=$_POST['word'];
$word = explode(" ", $word);
$searchString = $whereClause = "";
foreach($words as $word){
$searchString .= " OR word_eng LIKE '%".$word."%'";
}
$whereClause = ($searchString != "") ? " WHERE ".preg_replace('/OR/',
'', $searchString, 1) : $whereClause;
$sql = "SELECT word_eng FROM words ".$whereClause." LIMIT 17 ";
echo $sql;
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo $row["word_eng"];
}
I am getting the following error -
Warning: Invalid argument supplied for foreach() in /home/foneti2/public_html/howjsay/201701/searchResult.php on line 10
and echo $sql is telling me
SELECT word_eng FROM words LIMIT 17
which is getting all the words from database which i don't want
UPDATE
So thanks to you guys I have got it working to my needs but I still have a small issue - when retrieving multiple words from the database they are not being retrieved in the same order that is put into the input box. Any suggestions?
$word=$_POST['word'];
$word3 = $_POST['word'];
$word = explode(";", $word);
$noOfWords = count($word);
$word2 = $word3;
if($noOfWords == 1){
$searchString = " word_eng LIKE '".$word3."%'";
}
else {
$searchString = $whereClause = "";
foreach($word as $word){
$searchString .= " OR word_eng LIKE '".$word."'";
echo $word;
}
}
$whereClause = ($searchString != "") ? " WHERE ".preg_replace('/OR/',
'', $searchString, 1) : $whereClause;
$sql = "SELECT word_eng FROM words ".$whereClause ." LIMIT 17 ";
thankyou
If I understood what you mean, you have a input, and want to retrieve data from this input. Which is composed of words separated by spaces.
Try something like this:
$sql="SELECT word_eng FROM words WHERE word_end LIKE '%" . $word . "%' AND word_end LIKE '%" . $word2 . "%' LIMIT 17";
To optimize it, you can do a foreach for your words, with in it, you "update" your $sql string, by adding one AND ***.
And after your foreach, just add your LIMIT 17
As you mentioned in the question, multiple words are separated by spaces.
<?php
$words = "Hi All This Is Testing";
//The explode() function breaks a string into an array.
//Since, words are separated by spaces
$words = explode(" ", $words);
$searchString = $whereClause = "";
foreach($words as $word){
$searchString .= " OR word_eng LIKE '%".$word."%'";
}
$whereClause = ($searchString != "") ? " WHERE ".preg_replace('/OR/', '', $searchString, 1) : $whereClause;
$sql = "SELECT word_eng FROM words ".$whereClause." LIMIT 17 ";
echo $sql;
?>
Case-1: If $searchString is not empty.
Output: SELECT word_eng FROM words WHERE word_eng LIKE '%Hi%' OR word_eng LIKE '%All%' OR word_eng LIKE '%This%' OR word_eng LIKE '%Is%' OR word_eng LIKE '%Testing%' LIMIT 17
Case-2: If $searchString is empty.
Output: SELECT word_eng FROM words LIMIT 17
Explanation:
Using explode(), All words (separated by spaces) are now stored in array. Looping each word and appending with $searchString. Then, if $searchString is not empty, then, replacing first occurrence of OR from $searchString through preg_replace.
I have a SQL query for my search form.
$term = $request->get('term');
$queries = Article::where('title', 'LIKE', '%' . $term . '%')->published()->get();
My research is working. If I have an article called "my great article is awesome" and that I write in my search form "greate article" it works.
But if I write "article awesome", the words do not follow each other, and it does not work.
How do I get my query to work just with keywords?
Thank you
You can do something like follows:
$term = $request->get('term');
$keywords = explode(" ", $term);
$article = Article::query();
foreach($keywords as $word){
$article->orWhere('title', 'LIKE', '%'.$word.'%');
}
$articles = $article->published()->get();
If you want only results that contain all the words in the query just replace the orWhere with where.
If you want to filter out certain words you could add something like:
$filtered = ["a", "an", "the"];
$filteredKeywords = array_diff($keywords, $filtered);
Alternatively you can pass a closure if you want to be more dynamic:
$filteredKeywords = array_filter($keywords, function($word) {
return strlen($word) > 2;
});
why don't you try something like that
$search = "article awesome";
$search = preg_replace("#\s+#", '%', $search);
replacing spaces with '%' will resolve the case you mentioned
if you want to make the search ignoring the words order this should work
$search = trim(" article awesome great ");
$search = preg_split("#\s+#", $search);
$where = "WHERE column like '%" . implode( "%' AND column like '%", $search ) . "%'";
however it will take more execution time and resources on the server,
also think to add some injection escaping to avoid sql syntax errors
I have Chinese php search queries.
I want to split up any query up into individual characters.
ex: 你好 (ni hao, hello) split into 你 and 好
my query is set like:
$q = $_REQUEST["q"];
the results I want to split is set up like:
$results4 = $db->query( "SELECT CHS, PIN, DEF FROM FOUR
WHERE CHS LIKE '%".$q."%' OR PIN LIKE '%".$q."%'");
while ($row4 = $results4->fetchArray()) {
How can I split up the keyword and look up all the components?
If you want it all in one query you will have to generate the whole query. If you were looking for an exact match you could use something similar to the in_array() function, but with LIKE it doesn't work.
You could however loop through the array of characters and put together the WHERE part programatically.
Like this
$where = array();
foreach ( $qtwo as $word ) {
$where[] = "CHS LIKE '%" . $word . "%'";
}
$where = implode(' OR ', $where);
Use this $where variable in your query
You can use str_split to convert a string in an array of chars
$chars = str_split($q)
I have implemented searching using Query having LIKE clause with % wild cards on both side.
I have column having names of countries. If I search with P or PAK it shows result But If I search it with 'I live in Pakistan' it doesn't compare.
The thing I understand is it matches with the sub string.
Is it possible to make its vice versa like I pass the string 'I live in Pakistan' and it matches with characters in fields and gets the result of Pakistan.
Instant help would be appreciated.
you could use the like() function from codeigniter framework.
$this->db->like();
This function enables you to generate LIKE clauses, useful for doing searches.
Note: All values passed to this function are escaped automatically.
Simple key/value method:
$this->db->like('title', 'match');
// Produces: WHERE title LIKE '%match%'
If you use multiple function calls they will be chained together with AND between them:
$this->db->like('title', 'match');
$this->db->like('body', 'match');
// WHERE title LIKE '%match%' AND body LIKE '%match%
If you want to control where the wildcard (%) is placed, you can use an optional third argument. Your options are 'before', 'after' and 'both' (which is the default).
$this->db->like('title', 'match', 'before');
// Produces: WHERE title LIKE '%match'
$this->db->like('title', 'match', 'after');
// Produces: WHERE title LIKE 'match%'
$this->db->like('title', 'match', 'both');
// Produces: WHERE title LIKE '%match%'
If you do not want to use the wildcard (%) you can pass to the optional third argument the option 'none'.
$this->db->like('title', 'match', 'none');
// Produces: WHERE title LIKE 'match'
See This
I think what you Need is to search and compare for every word in the provided text, i think this will be helpfull.
$temp = array();
$str = array();
$str = explode(' ', $string);
foreach ( $str as $word) {
if (strlen($word) > 2) {
$this->db->or_like('country_name', $word, 'both');
} else {
continue;
}
}
$countries = $this->db->get('countries');
i think in $countries you will have all the needed result.
You could replace the whitespaces with commas, and then use find_in_set. Assuming your string is $str, consider this query:
SELECT *
FROM countries c
WHERE c.name LIKE '%$str%' OR
FIND_IN_SET(c.name, REPLACE($str, ' ', ',')) > 0
Try this
public function searchCountry($value)
{
$value = $value;
$query = $this->db->query("SELECT * FROM table_name WHERE country LIKE '%$value%' ");
$result = $query->result_array();
return $result;
}
This could work (disclaimer: not tested):
public function searchCountry($value)
{
$sql = "SELECT DISTINCT FROM `countries`";
$value = explode(' ', $value);// use all words from query
$first_match = FALSE;
foreach ($value as $k => $v)
{
if (strlen($v) >= 3)// exclude 1 and 2 letter words if want
{
if ( ! $first_match)
{
$sql .= " WHERE `name` LIKE %$v%";
}
else
{
$sql .= " OR `name` LIKE %$v%";
}
}
}
$sql .= " LIMIT 50";
$query = $this->db->query($sql);
$result = $query->result_array();
return $result;
}
but you probably should use #Mureinik solution.
You can use a regular expression trick for this purpose:
where country regexp replace($str, ' ', '|')
This constructs the expression:
where country regexp 'I|live|in|Pakistan'
which the evaluates to true because of the rules for regular expressions.
Say I have db content with the word "supercomputer". I want to search using the following:
select * from content where content REGEXP '[[:<:]]super*[[:>:]]';
I want the user doing the search to be able to add wildcards like ? and *. I want whole word searches unless the user does as I explained above so using [ REGEX 'super' ] isn't an option.
I'd think this would work, but I guess I'm still to new to using expressions.
The wildcard character * means something different in regex (sort of). It denotes that a sequence may be repeated zero or more times, not that there is some text of zero or more length that it can be replaced by.
You're going to have to do some preprocessing if you want the user to be able to use wildcards like this, but you can simply replace any * with the expression: \w*. This says that you are expecting zero or more word characters, not spaces or punctuation. so the complete expression would look like:
select * from content where content REGEXP '[[:<:]]super.*[[:>:]]';
This says you want any sequence that begins with 'super' and contains only word characters and is surrounded by word boundaries.
This ended up being my final PHP / MySQL solution for my search.
$bCustom = (strpos($search, "*") !== false && strpos($search, "?") !== false) ? false : true;
$sql = "SELECT content.*, users.user, users.lastname, users.firstname FROM content INNER JOIN users ON content.created_by=users.id ";
$search = (trim($get['SEARCH']) === "") ? "*" : trim($get['SEARCH']);
if ($bCustom) {
$search = str_replace("*", ".*", $search);
$search = str_replace("?", ".", $search);
$sql .= "WHERE content.name REGEXP '[[:<:]]" . $search . "[[:>:]]' OR content.content REGEXP '[[:<:]]"
. $search . "[[:>:]]' ORDER BY content.name ASC; ";
} else {
if ($search !== "") {
$sql .= "WHERE MATCH (name, content) AGAINST ('" . $search . "')";
} else {
$sql .= "ORDER BY content.name ASC; ";
}
}