PHP Code:
<?php
//"BASIC SEARCH"
//transform a user search to a mysql code for creating temp RANKING column
function search2mysql_rank($q,$column,$wmin=3,$maxterms=10,$ifnull=1) {
//$q string to convert;
//$column [string or string array to concatenate] to be searched;
//$maxterms limits search length to prevent overloading the server; 0 is unlimited;
//$ifnull 0 or 1: compensate for possible null values? default is YES
if (!is_string($q)||$q=='') { return FALSE; } //must have a [nonblank] string...
$q = explode(' ',$q); //split by words
foreach($q as $qk=>$qv) { //go through them all to check format/content of each word
if ($qv=='') { unset($q[$qk]); } //remove blanks
if (strpos($qv,'-')===0) { $qv = substr($qv,1); } //remove negation
else if (($wmin!=0)&&(strlen($qv)<$wmin)) { //is the term too short?
unset($q[$qk]); //remove it
}
}
//check for errors:
if (count($q)==0) { //no blank searches...
return FALSE;
}
if ($maxterms!=0&&count($q)>$maxterms) { //over term limit
return FALSE;
}
//end errors
//format $column including concatenation as needed
if (!is_array($column)) { //must switch string to array
$skipconcat=1; //skip concatenation later
$column = array($column); //convert to array
}
foreach($column as $colk=>$col) { //format each column individually
$col = '`'.$col.'`'; //format as column
if ($ifnull==1) {
$col = 'IFNULL('.$col.',\'\')'; //avoid null errors
}
$column[$colk] = $col; //store
}
$column = implode(', ',$column); //implode them into a list
if (!isset($skipconcat)) { //concatenate them:
$column = 'concat_ws(\' \','.$column.')';
}
$column = 'lcase('.$column.')'; //lowercase for caseless search
//format terms:
$negcount=0; //no negative terms, yet
foreach($q as $qk=>$qv) {
//escaping:
$qv = mysql_real_escape_string($qv); //make safe!
$qv = str_replace('%','\\%',$qv); //escape % so it's not a wildcard
$qv = str_replace('_','\\_',$qv); //escape _ so it's not a wildcard
//handle 'not':
$not = '';
if (strpos($qv,'-')===0) { //this term is negated
$qv = substr($qv,1); //remove the negative operator
$not = ' NOT'; //add for query
$negcount++; //for later use
}
$qv = mb_strtolower($qv); //lowercase for caseless search
//format as parameter:
$q[$qk] = 'IF('.$column.$not.' LIKE \'%'.$qv.'%\','.($not==''?1:'0.999').',0)';
}
if ($negcount==count($q)) { //only negative terms?
return FALSE; //not allowed
}
$q = implode('+',$q); //sum these up
if ($negcount>1) { //negative terms?
$q = $q.'-'.($negcount-1); //ignore results based on only the negative
}
return $q; //return the final OPERATIONS(s) for GENERATING COLUMN; use as required
}
//"ADVANCED SEARCH"
//transform a user search to a mysql code 'WHERE' string
function search2mysql_adv($q,$column,$wmin=3,$maxterms=10,$ifnull=1) {
//$q string to convert;
//$column [string or string array to concatenate] to be searched;
//$maxterms limits search length to prevent overloading the server; 0 is unlimited;
//$ifnull 0 or 1: compensate for possible null values? default is YES
if (!is_string($q)||$q=='') { return FALSE; } //must have a [nonblank] string...
$q = mbstring2chararray($q); //split query into [multibyte] character array
$qwords = array(); //setup an array to store the content 'words'
$ac=0; //setup array counter for search content array
$inquote = 0; //setup for check for being within quoted [literal] search data: 0 [false] or 1 [true]
$plevel = 0; //setup counter for levels of parenthetical embedding
$qstring = ''; //setup a blank term for the query syntax, later to get content too
$newword = 1; //status for whether we're at a word boundary
$negwords = array(); //setup blank array for tracking negative terms ("NOT")
$cswords = array(); //setup blank array for tracking case-sensitive terms [within quotes]
$qlen = count($q); //how many characters?
for($l=0;$l<$qlen;$l++) { //go through each character of the input and parse:
//open quote: not in quotes and at word boundary
if ($q[$l]=='"'&&$newword==1&&$inquote==0) {
$inquote=1;
}
//end quote: found while at the end of a word and while in quotes
elseif ($q[$l]=='"'&&$inquote==1&&($l==($qlen-1)||$q[$l+1]==' '||$q[$l+1]==')')) {
$inquote=0;
$cswords[] = $ac; //add this to a list of words to keep case sensitive
}
//start parentheses: at new word and not in quotes
elseif ($q[$l]=='('&&$newword==1&&$inquote==0) {
$qstring .= '#('; //add the parenthesis and marker [for later]
$plevel++;
if ($plevel<0) { //error: can't have close before open
return FALSE;
}
}
//end parentheses: at end of word and not in quotes
elseif ($q[$l]==')'&&($l==($qlen-1)||$q[$l+1]==' '||$q[$l+1]==')')&&$inquote==0) {
$qstring .= ')#'; //add the parenthesis and marker [for later]
$plevel--;
}
//a space is a break unless in a quote:
elseif ($q[$l]==' '&&$inquote==0) {
$newword=1;
$ac++;
}
//do OR which must be both the beginning and end of a word:
elseif ($newword==1&&$q[$l]=='O'&&($l<($qlen-1))&&$q[$l+1]=='R'&&($l==($qlen-2)||$q[$l+2]==' '||$q[$l+1]=='(')&&$inquote==0) {
$l++; //skip past the R
$l++; //skip past extra space to avoid issues
$qstring .= ' OR ';
}
//deal with - marker for "NOT" terms
elseif ($newword==1&&$q[$l]=='-'&&($l<($qlen-1))&&$q[$l+1]!='('&&$inquote==0&&($l==0||$q[$l-1]!='-')) {
$negwords[] = $ac;
}
//deal with - marker for "NOT" parentheticals
elseif ($newword==1&&$q[$l]=='-'&&($l<($qlen-1))&&$q[$l+1]=='('&&$inquote==0&&($l==0||$q[$l-1]!='-')) {
$qstring .= '#NOT ';
}
else { //not syntactic, must be content
if ($newword==1) { //begin a new word
$newword=0; //no longer a new word
$qwords[$ac] = ''; //setup blank string
$qstring .= '#*'.$ac.'*#'; //add the reference + markers [for later]
}
$qwords[$ac] .= $q[$l]; //add the letter
}
}
//errors:
if ($inquote==1) { //uneven quotes
return FALSE;
}
if ($plevel!=0) { //uneven parentheses
return FALSE;
}
if (in_array('',$qwords)) { //a blank word was found: input is badly formatted
return FALSE;
}
$qwordstemp = $qwords; //copy to check min length for words and query:
foreach($qwordstemp as $qk=>$qv) { //go through them all
if ($wmin!=0&&strlen($qv)<$wmin) { //are all terms too short? --> too hard right now to eliminate short terms, but can require that at least some are long enough
unset($qwordstemp[$qk]); //remove it
}
}
if (count($qwordstemp)==0) { //no blank searches... at least some terms must be valid: exist and be long enough, see above
return FALSE;
} //end min length check
if ($maxterms!=0&&count($qwords)>$maxterms) { //over max length: prevent overload
return FALSE;
}
if (count($negwords)==count($qwords)) { //all words are negative?
return FALSE;
}
if (strpos($qstring,'###')!==FALSE) { //weird error with markers
return FALSE;
}
if (strpos($qstring,'()')!==FALSE) { //input had parentheses with no content
return FALSE;
}
//end errors
$qstring = str_replace('##',' AND ',$qstring); //adjacent terms need to be conjoined with AND
$qstring = str_replace('#','',$qstring); //no need for floating markers now
//format $column including concatenation as needed
if (!is_array($column)) { //must switch string to array
$skipconcat=1; //skip concatenation later
$column = array($column); //convert to array
}
foreach($column as $colk=>$col) { //format each column individually
$col = '`'.$col.'`'; //format as column
if ($ifnull==1) {
$col = 'IFNULL('.$col.',\'\')'; //avoid null errors
}
$column[$colk] = $col; //store
}
$column = implode(', ',$column); //implode them into a list
if (!isset($skipconcat)) { //concatenate them:
$column = 'concat_ws(\' \','.$column.')';
}
//escape data, then format as a condition
foreach($qwords as $qwkey=>$qword) { //go through the words
//escaping:
$qword = mysql_real_escape_string($qword); //make safe!
$qword = str_replace('%','\\%',$qword); //escape % so it's not a wildcard
$qword = str_replace('_','\\_',$qword); //escape _ so it's not a wildcard
//format as condition:
$not = in_array($qwkey,$negwords)?' NOT ':' '; //is it negated?
//handle case sensitivity: lcase matching normal; in quotes is case sensitive
$lcase = ''; //set to blank by default
if (!in_array($qwkey,$cswords)) { //this is a non-case-sensitive word [otherwise do nothing special]
$lcase = 'lcase('; //set col prefix
$not = ')'.$not; //add col end to trailing $not var
$qword = mb_strtolower($qword); //make string lowercase
}
$qwords[$qwkey] = $lcase.$column.$not.'LIKE \'%'.$qword.'%\''; //format as a mysql query condition
}
//now that the terms and query are setup separately, time to put them together
$invar=0; //tracker value
$q = ''; //reset as blank string for output
for($x=0;$x<strlen($qstring);$x++) { //go through the whole query string
if ($qstring[$x]=='*'&&$invar==0) { //start compiling a key
$invar=1; //set as compiling key
$qwkey = ''; //setup blank string as key
}
elseif ($qstring[$x]=='*'&&$invar==1) { //key end reached
$q .= $qwords[$qwkey]; //add the word to the query
$invar=0; //reset
}
elseif($invar==1) { //continue compiling replace key
$qwkey .= $qstring[$x];
}
else { //it's just syntax
$q .= $qstring[$x];
}
}
return $q; //return the final CONDITION(s) for WHERE; use as required
}
?>
[continued below]
Bookmarks