Using MySQL REGEX anchors with wildcards inside - php

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; ";
}
}

Related

How can I remove a word from a string?

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;
}

Split up keyword query individual parts loop

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)

If $_POST value has the word AND, then insert word ARE

I have a typical question, I'm not sure whether it is possible or not. I have a form in which there is a field namely Producer. How do I make it possible, if user use the word and in the field then insert the word are in the result, and if user don't use the word and in the field then insert the word is in the result. Let me explain you with an example.
Example (the word and is in the field) then generate the following result:
ABC and DEF are the producers of the movie.
Example (the word and is NOT in the field) then generate the following result:
XYZ is the producer of the movie.
I have the following code:
if(!empty($_POST['Producer'])) {
$description .= ' ' . $_POST["Producer"] . ' is/are the producer(s) of the movie';
}
Kindly tell me if anyone have the idea.
Simply call strpos with $_POST['Producer'] as haystack and and as needle. If the return value is false, the string doesn't contain and.
Now you can depending on the return value create your output.
http://php.net/manual/en/function.strpos.php
if(!empty($_POST['Producer']))
{
if(stripos($_POST['Producer'], ' and ') != false) // ' and ' is found
$producers = $_POST['Producer'] .' are the producers ';
else
$producers = $_POST['Producer'] .' is the producer ';
$description = $producers .'of the movie';
}
I put ' and ' instead of 'and' (with whitespaces) because some names contains the word 'are', so it would return true even if there's only one name.
Below code should work (not tested).
if(!empty($_POST['Producer'])) {
$producer = $_POST["Producer"]; // CONSIDER SANITIZING
$pos = stripos($_POST['Producer'], ' and ');
list($verb, $pl) = $pos ? array('are', 's') : array('is', '');
$description .= " $producer $verb the producer$pl of the movie";
}
As noted you should also consider sanitizing the incoming value of $_POST["Producer"], depending on how you intend to use the formatted string.
I have not tested this but something along the lines of this should work.
$string = $_POST['Producer'];
//This is the case if the user used and.
$start = strstr($string, 'and');
if($start != null)
{
$newString = substr($string, 0, $start) . "are" . substr($string, $start+3, strlen($string))
}

How to search in database when the search term is a string that includes spaces?

I have this string "Beautiful sunset" (notice the double space)
When I search in the database:
SELECT * FROM images WHERE title LIKE '%$string%'
and I search for "Beautiful sunset" (notice the single space) it won't return any results.
How can I tackle this?
split the string by space.
now you have two strings something like $str1, $str2
trim this two strings(i.e remove leading and trailing whitespaces)
then rebuild string
$string = $str1+'%'+$str2
Try this
SELECT * FROM images WHERE where MATCH(title) AGAINST ('$string' IN BOOLEAN MODE)
Check this Link also
http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html
You could split your search string into multiple parts using the space and then build a sql like this for every part you have splitted:
SELECT * FROM images WHERE title LIKE '%$part[0]%'
or title LIKE '%$part[1]%'
or title LIKE '%$part[2]%'
or title LIKE '%$part[3]%'
...
Make sure to skip double spaces / empty parts.
One way you can do this is to use string replace to replace spaces with a wildcard character:
$string = str_replace(" ", "%", $string);
$Query = "SELECT * FROM images WHERE title LIKE '%$string%'";
If you don't know what string you're going to get (if it isn't "Beautiful sunset" all the time), you could explode the string and make a query based on that. Like so...
$c = false;
$stringtosearch = "Beautiful sunset";
$query = "SELECT * FROM images WHERE";
foreach(explode(" ", $stringtosearch) as $b)
{
if ($c)
{
$query .= " or";
}
$query .= " title LIKE " . $b;
$c = true;
}
And afterwards you would get the variable $query with your query string.

php tokenisation

I have a string of characters separated by many hashes (#). I need to get the individual words in between the hashes on php. here's what my code looks like:
$sql = "SELECT attribute_type.at_name,attribute_type.at_id FROM attribute_type
WHERE attribute_type.prodType_id = $pt_id
AND attribute_type.at_id NOT IN (SELECT at_id
FROM attribute_type
WHERE attribute_type.at_name = 'Product')";
while($items. strpos("#")>0){
// add the selected AT in every loop to be excluded
// .
// here tokens from $items are stored individually in
// $selectedAT (whose value changes in every loop/cycle)
//
// add to the current sql statement the values $at_id and $searchparam
$sql = $sql . "AND attribute_type.at_id NOT IN
(SELECT at_id FROM attribute_type
WHERE attribute_type.at_name = '$selectedAT')";
}
$dbcon = new DatabaseManager();
$rs = $dbcon->runQuery($sql);
explode creates an array by splitting a string on a given token
$words = explode("#", $items);
Now if you need to take these words you extracted from the string and use them to compare to some column in a SQL query...
$sql = "SELECT ... WHERE column IN ('" . implode("', '", $words) . "')";
You should not need to build a query in a loop as you are doing once you have the words in an array.
Even if you did want to do it that way, you don't want to create a subquery for every word when you could just OR the words together in one subquery.
Try strtok. Example paste:
$string = "This is\tan example\nstring";
$tok = strtok($string, " \n\t");
while ($tok !== false) {
echo "Word=$tok<br />";
$tok = strtok(" \n\t");
}
Do not use split as suggested in another answer (which has now been updated). It uses old POSIX regulat expressions and it's deprecated.
To split a string, use $words = explode('#', $items); which does not use a regular expression but a plain string.
Docref: http://php.net/explode

Categories